mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-07 05:11:05 +01:00
(stable) Promote 2019 Week 7
This commit is contained in:
commit
dd060b94f0
129 changed files with 2004 additions and 904 deletions
|
@ -9,7 +9,7 @@ return array(
|
|||
'names' => array(
|
||||
'conpherence.pkg.css' => '3c8a0668',
|
||||
'conpherence.pkg.js' => '020aebcf',
|
||||
'core.pkg.css' => '7a73ffc5',
|
||||
'core.pkg.css' => '85a1da99',
|
||||
'core.pkg.js' => '5c737607',
|
||||
'differential.pkg.css' => 'b8df73d4',
|
||||
'differential.pkg.js' => '67c9ea4c',
|
||||
|
@ -30,7 +30,7 @@ return array(
|
|||
'rsrc/css/aphront/notification.css' => '30240bd2',
|
||||
'rsrc/css/aphront/panel-view.css' => '46923d46',
|
||||
'rsrc/css/aphront/phabricator-nav-view.css' => 'f8a0c1bf',
|
||||
'rsrc/css/aphront/table-view.css' => 'daa1f9df',
|
||||
'rsrc/css/aphront/table-view.css' => '205053cd',
|
||||
'rsrc/css/aphront/tokenizer.css' => 'b52d0668',
|
||||
'rsrc/css/aphront/tooltip.css' => 'e3f2412f',
|
||||
'rsrc/css/aphront/typeahead-browse.css' => 'b7ed02d2',
|
||||
|
@ -46,7 +46,7 @@ return array(
|
|||
'rsrc/css/application/config/config-options.css' => '16c920ae',
|
||||
'rsrc/css/application/config/config-template.css' => '20babf50',
|
||||
'rsrc/css/application/config/setup-issue.css' => '5eed85b2',
|
||||
'rsrc/css/application/config/unhandled-exception.css' => '9da8fdab',
|
||||
'rsrc/css/application/config/unhandled-exception.css' => '9ecfc00d',
|
||||
'rsrc/css/application/conpherence/color.css' => 'b17746b0',
|
||||
'rsrc/css/application/conpherence/durable-column.css' => '2d57072b',
|
||||
'rsrc/css/application/conpherence/header-pane.css' => 'c9a3db8e',
|
||||
|
@ -151,7 +151,7 @@ return array(
|
|||
'rsrc/css/phui/phui-document.css' => '52b748a5',
|
||||
'rsrc/css/phui/phui-feed-story.css' => 'a0c05029',
|
||||
'rsrc/css/phui/phui-fontkit.css' => '9b714a5e',
|
||||
'rsrc/css/phui/phui-form-view.css' => '0807e7ac',
|
||||
'rsrc/css/phui/phui-form-view.css' => '01b796c0',
|
||||
'rsrc/css/phui/phui-form.css' => '159e2d9c',
|
||||
'rsrc/css/phui/phui-head-thing.css' => 'd7f293df',
|
||||
'rsrc/css/phui/phui-header-view.css' => '93cea4ec',
|
||||
|
@ -164,7 +164,7 @@ return array(
|
|||
'rsrc/css/phui/phui-left-right.css' => '68513c34',
|
||||
'rsrc/css/phui/phui-lightbox.css' => '4ebf22da',
|
||||
'rsrc/css/phui/phui-list.css' => '470b1adb',
|
||||
'rsrc/css/phui/phui-object-box.css' => '9b58483d',
|
||||
'rsrc/css/phui/phui-object-box.css' => 'f434b6be',
|
||||
'rsrc/css/phui/phui-pager.css' => 'd022c7ad',
|
||||
'rsrc/css/phui/phui-pinboard-view.css' => '1f08f5d8',
|
||||
'rsrc/css/phui/phui-property-list-view.css' => 'cad62236',
|
||||
|
@ -502,6 +502,7 @@ return array(
|
|||
'rsrc/js/phui/behavior-phui-selectable-list.js' => 'b26a41e4',
|
||||
'rsrc/js/phui/behavior-phui-submenu.js' => 'b5e9bff9',
|
||||
'rsrc/js/phui/behavior-phui-tab-group.js' => '242aa08b',
|
||||
'rsrc/js/phui/behavior-phui-timer-control.js' => 'f84bcbf4',
|
||||
'rsrc/js/phuix/PHUIXActionListView.js' => 'c68f183f',
|
||||
'rsrc/js/phuix/PHUIXActionView.js' => 'aaa08f3b',
|
||||
'rsrc/js/phuix/PHUIXAutocomplete.js' => '58cc4ab8',
|
||||
|
@ -519,7 +520,7 @@ return array(
|
|||
'aphront-list-filter-view-css' => 'feb64255',
|
||||
'aphront-multi-column-view-css' => 'fbc00ba3',
|
||||
'aphront-panel-view-css' => '46923d46',
|
||||
'aphront-table-view-css' => 'daa1f9df',
|
||||
'aphront-table-view-css' => '205053cd',
|
||||
'aphront-tokenizer-control-css' => 'b52d0668',
|
||||
'aphront-tooltip-css' => 'e3f2412f',
|
||||
'aphront-typeahead-control-css' => '8779483d',
|
||||
|
@ -650,6 +651,7 @@ return array(
|
|||
'javelin-behavior-phui-selectable-list' => 'b26a41e4',
|
||||
'javelin-behavior-phui-submenu' => 'b5e9bff9',
|
||||
'javelin-behavior-phui-tab-group' => '242aa08b',
|
||||
'javelin-behavior-phui-timer-control' => 'f84bcbf4',
|
||||
'javelin-behavior-phuix-example' => 'c2c500a7',
|
||||
'javelin-behavior-policy-control' => '0eaa33a9',
|
||||
'javelin-behavior-policy-rule-editor' => '9347f172',
|
||||
|
@ -817,7 +819,7 @@ return array(
|
|||
'phui-font-icon-base-css' => 'd7994e06',
|
||||
'phui-fontkit-css' => '9b714a5e',
|
||||
'phui-form-css' => '159e2d9c',
|
||||
'phui-form-view-css' => '0807e7ac',
|
||||
'phui-form-view-css' => '01b796c0',
|
||||
'phui-head-thing-view-css' => 'd7f293df',
|
||||
'phui-header-view-css' => '93cea4ec',
|
||||
'phui-hovercard' => '074f0783',
|
||||
|
@ -831,7 +833,7 @@ return array(
|
|||
'phui-left-right-css' => '68513c34',
|
||||
'phui-lightbox-css' => '4ebf22da',
|
||||
'phui-list-view-css' => '470b1adb',
|
||||
'phui-object-box-css' => '9b58483d',
|
||||
'phui-object-box-css' => 'f434b6be',
|
||||
'phui-oi-big-ui-css' => '9e037c7a',
|
||||
'phui-oi-color-css' => 'b517bfa0',
|
||||
'phui-oi-drag-ui-css' => 'da15d3dc',
|
||||
|
@ -877,7 +879,7 @@ return array(
|
|||
'syntax-highlighting-css' => '8a16f91b',
|
||||
'tokens-css' => 'ce5a50bd',
|
||||
'typeahead-browse-css' => 'b7ed02d2',
|
||||
'unhandled-exception-css' => '9da8fdab',
|
||||
'unhandled-exception-css' => '9ecfc00d',
|
||||
),
|
||||
'requires' => array(
|
||||
'01384686' => array(
|
||||
|
@ -2111,6 +2113,11 @@ return array(
|
|||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
),
|
||||
'f84bcbf4' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
),
|
||||
'f8c4e135' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_user.user_externalaccount
|
||||
ADD providerConfigPHID VARBINARY(64) NOT NULL;
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
$account_table = new PhabricatorExternalAccount();
|
||||
$account_conn = $account_table->establishConnection('w');
|
||||
$table_name = $account_table->getTableName();
|
||||
|
||||
$config_table = new PhabricatorAuthProviderConfig();
|
||||
$config_conn = $config_table->establishConnection('w');
|
||||
|
||||
foreach (new LiskRawMigrationIterator($account_conn, $table_name) as $row) {
|
||||
if (strlen($row['providerConfigPHID'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$config_row = queryfx_one(
|
||||
$config_conn,
|
||||
'SELECT phid
|
||||
FROM %R
|
||||
WHERE providerType = %s AND providerDomain = %s
|
||||
LIMIT 1',
|
||||
$config_table,
|
||||
$row['accountType'],
|
||||
$row['accountDomain']);
|
||||
if (!$config_row) {
|
||||
continue;
|
||||
}
|
||||
|
||||
queryfx(
|
||||
$account_conn,
|
||||
'UPDATE %R
|
||||
SET providerConfigPHID = %s
|
||||
WHERE id = %d',
|
||||
$account_table,
|
||||
$config_row['phid'],
|
||||
$row['id']);
|
||||
}
|
|
@ -1714,6 +1714,7 @@ phutil_register_library_map(array(
|
|||
'ManiphestTaskFerretEngine' => 'applications/maniphest/search/ManiphestTaskFerretEngine.php',
|
||||
'ManiphestTaskFulltextEngine' => 'applications/maniphest/search/ManiphestTaskFulltextEngine.php',
|
||||
'ManiphestTaskGraph' => 'infrastructure/graph/ManiphestTaskGraph.php',
|
||||
'ManiphestTaskGraphController' => 'applications/maniphest/controller/ManiphestTaskGraphController.php',
|
||||
'ManiphestTaskHasCommitEdgeType' => 'applications/maniphest/edge/ManiphestTaskHasCommitEdgeType.php',
|
||||
'ManiphestTaskHasCommitRelationship' => 'applications/maniphest/relationship/ManiphestTaskHasCommitRelationship.php',
|
||||
'ManiphestTaskHasDuplicateTaskEdgeType' => 'applications/maniphest/edge/ManiphestTaskHasDuplicateTaskEdgeType.php',
|
||||
|
@ -2195,6 +2196,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorAuthChallengeGarbageCollector' => 'applications/auth/garbagecollector/PhabricatorAuthChallengeGarbageCollector.php',
|
||||
'PhabricatorAuthChallengePHIDType' => 'applications/auth/phid/PhabricatorAuthChallengePHIDType.php',
|
||||
'PhabricatorAuthChallengeQuery' => 'applications/auth/query/PhabricatorAuthChallengeQuery.php',
|
||||
'PhabricatorAuthChallengeStatusController' => 'applications/auth/controller/mfa/PhabricatorAuthChallengeStatusController.php',
|
||||
'PhabricatorAuthChallengeUpdate' => 'applications/auth/view/PhabricatorAuthChallengeUpdate.php',
|
||||
'PhabricatorAuthChangePasswordAction' => 'applications/auth/action/PhabricatorAuthChangePasswordAction.php',
|
||||
'PhabricatorAuthConduitAPIMethod' => 'applications/auth/conduit/PhabricatorAuthConduitAPIMethod.php',
|
||||
'PhabricatorAuthConduitTokenRevoker' => 'applications/auth/revoker/PhabricatorAuthConduitTokenRevoker.php',
|
||||
|
@ -2270,6 +2273,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorAuthInviteVerifyException' => 'applications/auth/exception/PhabricatorAuthInviteVerifyException.php',
|
||||
'PhabricatorAuthInviteWorker' => 'applications/auth/worker/PhabricatorAuthInviteWorker.php',
|
||||
'PhabricatorAuthLinkController' => 'applications/auth/controller/PhabricatorAuthLinkController.php',
|
||||
'PhabricatorAuthLinkMessageType' => 'applications/auth/message/PhabricatorAuthLinkMessageType.php',
|
||||
'PhabricatorAuthListController' => 'applications/auth/controller/config/PhabricatorAuthListController.php',
|
||||
'PhabricatorAuthLoginController' => 'applications/auth/controller/PhabricatorAuthLoginController.php',
|
||||
'PhabricatorAuthLoginMessageType' => 'applications/auth/message/PhabricatorAuthLoginMessageType.php',
|
||||
|
@ -2368,6 +2372,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorAuthSessionPHIDType' => 'applications/auth/phid/PhabricatorAuthSessionPHIDType.php',
|
||||
'PhabricatorAuthSessionQuery' => 'applications/auth/query/PhabricatorAuthSessionQuery.php',
|
||||
'PhabricatorAuthSessionRevoker' => 'applications/auth/revoker/PhabricatorAuthSessionRevoker.php',
|
||||
'PhabricatorAuthSetExternalController' => 'applications/auth/controller/PhabricatorAuthSetExternalController.php',
|
||||
'PhabricatorAuthSetPasswordController' => 'applications/auth/controller/PhabricatorAuthSetPasswordController.php',
|
||||
'PhabricatorAuthSetupCheck' => 'applications/config/check/PhabricatorAuthSetupCheck.php',
|
||||
'PhabricatorAuthStartController' => 'applications/auth/controller/PhabricatorAuthStartController.php',
|
||||
|
@ -3867,7 +3872,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPeopleInviteController' => 'applications/people/controller/PhabricatorPeopleInviteController.php',
|
||||
'PhabricatorPeopleInviteListController' => 'applications/people/controller/PhabricatorPeopleInviteListController.php',
|
||||
'PhabricatorPeopleInviteSendController' => 'applications/people/controller/PhabricatorPeopleInviteSendController.php',
|
||||
'PhabricatorPeopleLdapController' => 'applications/people/controller/PhabricatorPeopleLdapController.php',
|
||||
'PhabricatorPeopleListController' => 'applications/people/controller/PhabricatorPeopleListController.php',
|
||||
'PhabricatorPeopleLogQuery' => 'applications/people/query/PhabricatorPeopleLogQuery.php',
|
||||
'PhabricatorPeopleLogSearchEngine' => 'applications/people/query/PhabricatorPeopleLogSearchEngine.php',
|
||||
|
@ -4987,6 +4991,7 @@ phutil_register_library_map(array(
|
|||
'PhortuneAccountViewController' => 'applications/phortune/controller/account/PhortuneAccountViewController.php',
|
||||
'PhortuneAdHocCart' => 'applications/phortune/cart/PhortuneAdHocCart.php',
|
||||
'PhortuneAdHocProduct' => 'applications/phortune/product/PhortuneAdHocProduct.php',
|
||||
'PhortuneAddPaymentMethodAction' => 'applications/phortune/action/PhortuneAddPaymentMethodAction.php',
|
||||
'PhortuneCart' => 'applications/phortune/storage/PhortuneCart.php',
|
||||
'PhortuneCartAcceptController' => 'applications/phortune/controller/cart/PhortuneCartAcceptController.php',
|
||||
'PhortuneCartCancelController' => 'applications/phortune/controller/cart/PhortuneCartCancelController.php',
|
||||
|
@ -7397,6 +7402,7 @@ phutil_register_library_map(array(
|
|||
'ManiphestTaskFerretEngine' => 'PhabricatorFerretEngine',
|
||||
'ManiphestTaskFulltextEngine' => 'PhabricatorFulltextEngine',
|
||||
'ManiphestTaskGraph' => 'PhabricatorObjectGraph',
|
||||
'ManiphestTaskGraphController' => 'ManiphestController',
|
||||
'ManiphestTaskHasCommitEdgeType' => 'PhabricatorEdgeType',
|
||||
'ManiphestTaskHasCommitRelationship' => 'ManiphestTaskRelationship',
|
||||
'ManiphestTaskHasDuplicateTaskEdgeType' => 'PhabricatorEdgeType',
|
||||
|
@ -7925,6 +7931,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorAuthChallengeGarbageCollector' => 'PhabricatorGarbageCollector',
|
||||
'PhabricatorAuthChallengePHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorAuthChallengeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorAuthChallengeStatusController' => 'PhabricatorAuthController',
|
||||
'PhabricatorAuthChallengeUpdate' => 'Phobject',
|
||||
'PhabricatorAuthChangePasswordAction' => 'PhabricatorSystemAction',
|
||||
'PhabricatorAuthConduitAPIMethod' => 'ConduitAPIMethod',
|
||||
'PhabricatorAuthConduitTokenRevoker' => 'PhabricatorAuthRevoker',
|
||||
|
@ -8019,6 +8027,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorAuthInviteVerifyException' => 'PhabricatorAuthInviteDialogException',
|
||||
'PhabricatorAuthInviteWorker' => 'PhabricatorWorker',
|
||||
'PhabricatorAuthLinkController' => 'PhabricatorAuthController',
|
||||
'PhabricatorAuthLinkMessageType' => 'PhabricatorAuthMessageType',
|
||||
'PhabricatorAuthListController' => 'PhabricatorAuthProviderConfigController',
|
||||
'PhabricatorAuthLoginController' => 'PhabricatorAuthController',
|
||||
'PhabricatorAuthLoginMessageType' => 'PhabricatorAuthMessageType',
|
||||
|
@ -8138,6 +8147,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorAuthSessionPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorAuthSessionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorAuthSessionRevoker' => 'PhabricatorAuthRevoker',
|
||||
'PhabricatorAuthSetExternalController' => 'PhabricatorAuthController',
|
||||
'PhabricatorAuthSetPasswordController' => 'PhabricatorAuthController',
|
||||
'PhabricatorAuthSetupCheck' => 'PhabricatorSetupCheck',
|
||||
'PhabricatorAuthStartController' => 'PhabricatorAuthController',
|
||||
|
@ -9866,7 +9876,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPeopleInviteController' => 'PhabricatorPeopleController',
|
||||
'PhabricatorPeopleInviteListController' => 'PhabricatorPeopleInviteController',
|
||||
'PhabricatorPeopleInviteSendController' => 'PhabricatorPeopleInviteController',
|
||||
'PhabricatorPeopleLdapController' => 'PhabricatorPeopleController',
|
||||
'PhabricatorPeopleListController' => 'PhabricatorPeopleController',
|
||||
'PhabricatorPeopleLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorPeopleLogSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
|
@ -11229,6 +11238,7 @@ phutil_register_library_map(array(
|
|||
'PhortuneAccountViewController' => 'PhortuneAccountProfileController',
|
||||
'PhortuneAdHocCart' => 'PhortuneCartImplementation',
|
||||
'PhortuneAdHocProduct' => 'PhortuneProductImplementation',
|
||||
'PhortuneAddPaymentMethodAction' => 'PhabricatorSystemAction',
|
||||
'PhortuneCart' => array(
|
||||
'PhortuneDAO',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
|
|
|
@ -594,7 +594,7 @@ final class AphrontRequest extends Phobject {
|
|||
$request_uri = idx($_SERVER, 'REQUEST_URI', '/');
|
||||
|
||||
$uri = new PhutilURI($request_uri);
|
||||
$uri->setQueryParam('__path__', null);
|
||||
$uri->removeQueryParam('__path__');
|
||||
|
||||
$path = phutil_escape_uri($this->getPath());
|
||||
$uri->setPath($path);
|
||||
|
@ -829,7 +829,10 @@ final class AphrontRequest extends Phobject {
|
|||
}
|
||||
|
||||
$uri->setPath($this->getPath());
|
||||
$uri->setQueryParams(self::flattenData($_GET));
|
||||
$uri->removeAllQueryParams();
|
||||
foreach (self::flattenData($_GET) as $query_key => $query_value) {
|
||||
$uri->appendQueryParam($query_key, $query_value);
|
||||
}
|
||||
|
||||
$input = PhabricatorStartup::getRawInput();
|
||||
|
||||
|
|
|
@ -118,6 +118,12 @@ final class AphrontApplicationConfiguration
|
|||
$database_exception = $ex;
|
||||
}
|
||||
|
||||
// If we're in developer mode, set a flag so that top-level exception
|
||||
// handlers can add more information.
|
||||
if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) {
|
||||
$sink->setShowStackTraces(true);
|
||||
}
|
||||
|
||||
if ($database_exception) {
|
||||
$issue = PhabricatorSetupIssue::newDatabaseConnectionIssue(
|
||||
$database_exception,
|
||||
|
@ -282,23 +288,69 @@ final class AphrontApplicationConfiguration
|
|||
}
|
||||
} catch (Exception $ex) {
|
||||
$original_exception = $ex;
|
||||
$response = $this->handleThrowable($ex);
|
||||
} catch (Throwable $ex) {
|
||||
$original_exception = $ex;
|
||||
$response = $this->handleThrowable($ex);
|
||||
}
|
||||
|
||||
$response_exception = null;
|
||||
try {
|
||||
if ($original_exception) {
|
||||
$response = $this->handleThrowable($original_exception);
|
||||
}
|
||||
|
||||
$response = $this->produceResponse($request, $response);
|
||||
$response = $controller->willSendResponse($response);
|
||||
$response->setRequest($request);
|
||||
|
||||
self::writeResponse($sink, $response);
|
||||
} catch (Exception $ex) {
|
||||
$response_exception = $ex;
|
||||
} catch (Throwable $ex) {
|
||||
$response_exception = $ex;
|
||||
}
|
||||
|
||||
if ($response_exception) {
|
||||
// If we encountered an exception while building a normal response, then
|
||||
// encountered another exception while building a response for the first
|
||||
// exception, just throw the original exception. It is more likely to be
|
||||
// useful and point at a root cause than the second exception we ran into
|
||||
// while telling the user about it.
|
||||
if ($original_exception) {
|
||||
throw $original_exception;
|
||||
}
|
||||
throw $ex;
|
||||
|
||||
// If we built a response successfully and then ran into an exception
|
||||
// trying to render it, try to handle and present that exception to the
|
||||
// user using the standard handler.
|
||||
|
||||
// The problem here might be in rendering (more common) or in the actual
|
||||
// response mechanism (less common). If it's in rendering, we can likely
|
||||
// still render a nice exception page: the majority of rendering issues
|
||||
// are in main page content, not content shared with the exception page.
|
||||
|
||||
$handling_exception = null;
|
||||
try {
|
||||
$response = $this->handleThrowable($response_exception);
|
||||
|
||||
$response = $this->produceResponse($request, $response);
|
||||
$response = $controller->willSendResponse($response);
|
||||
$response->setRequest($request);
|
||||
|
||||
self::writeResponse($sink, $response);
|
||||
} catch (Exception $ex) {
|
||||
$handling_exception = $ex;
|
||||
} catch (Throwable $ex) {
|
||||
$handling_exception = $ex;
|
||||
}
|
||||
|
||||
// If we didn't have any luck with that, raise the original response
|
||||
// exception. As above, this is the root cause exception and more likely
|
||||
// to be useful. This will go to the fallback error handler at top
|
||||
// level.
|
||||
|
||||
if ($handling_exception) {
|
||||
throw $response_exception;
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
|
|
|
@ -32,22 +32,21 @@ final class AphrontAjaxResponse extends AphrontResponse {
|
|||
}
|
||||
|
||||
public function buildResponseString() {
|
||||
$request = $this->getRequest();
|
||||
$console = $this->getConsole();
|
||||
if ($console) {
|
||||
// NOTE: We're stripping query parameters here both for readability and
|
||||
// to mitigate BREACH and similar attacks. The parameters are available
|
||||
// in the "Request" tab, so this should not impact usability. See T3684.
|
||||
$uri = $this->getRequest()->getRequestURI();
|
||||
$uri = new PhutilURI($uri);
|
||||
$uri->setQueryParams(array());
|
||||
$path = $request->getPath();
|
||||
|
||||
Javelin::initBehavior(
|
||||
'dark-console',
|
||||
array(
|
||||
'uri' => (string)$uri,
|
||||
'key' => $console->getKey($this->getRequest()),
|
||||
'uri' => $path,
|
||||
'key' => $console->getKey($request),
|
||||
'color' => $console->getColor(),
|
||||
'quicksand' => $this->getRequest()->isQuicksand(),
|
||||
'quicksand' => $request->isQuicksand(),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -60,7 +59,6 @@ final class AphrontAjaxResponse extends AphrontResponse {
|
|||
|
||||
$response = CelerityAPI::getStaticResourceResponse();
|
||||
|
||||
$request = $this->getRequest();
|
||||
if ($request) {
|
||||
$viewer = $request->getViewer();
|
||||
if ($viewer) {
|
||||
|
|
|
@ -218,7 +218,7 @@ abstract class AphrontResponse extends Phobject {
|
|||
$uri = id(new PhutilURI($uri))
|
||||
->setPath(null)
|
||||
->setFragment(null)
|
||||
->setQueryParams(array());
|
||||
->removeAllQueryParams();
|
||||
|
||||
$uri = (string)$uri;
|
||||
if (preg_match('/[ ;\']/', $uri)) {
|
||||
|
|
|
@ -4,8 +4,20 @@ final class AphrontUnhandledExceptionResponse
|
|||
extends AphrontStandaloneHTMLResponse {
|
||||
|
||||
private $exception;
|
||||
private $showStackTraces;
|
||||
|
||||
public function setShowStackTraces($show_stack_traces) {
|
||||
$this->showStackTraces = $show_stack_traces;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getShowStackTraces() {
|
||||
return $this->showStackTraces;
|
||||
}
|
||||
|
||||
public function setException($exception) {
|
||||
// NOTE: We accept an Exception or a Throwable.
|
||||
|
||||
public function setException(Exception $exception) {
|
||||
// Log the exception unless it's specifically a silent malformed request
|
||||
// exception.
|
||||
|
||||
|
@ -61,10 +73,36 @@ final class AphrontUnhandledExceptionResponse
|
|||
$body = $ex->getMessage();
|
||||
$body = phutil_escape_html_newlines($body);
|
||||
|
||||
$classes = array();
|
||||
$classes[] = 'unhandled-exception-detail';
|
||||
|
||||
$stack = null;
|
||||
if ($this->getShowStackTraces()) {
|
||||
try {
|
||||
$stack = id(new AphrontStackTraceView())
|
||||
->setTrace($ex->getTrace());
|
||||
|
||||
$stack = hsprintf('%s', $stack);
|
||||
|
||||
$stack = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'unhandled-exception-stack',
|
||||
),
|
||||
$stack);
|
||||
|
||||
$classes[] = 'unhandled-exception-with-stack';
|
||||
} catch (Exception $trace_exception) {
|
||||
$stack = null;
|
||||
} catch (Throwable $trace_exception) {
|
||||
$stack = null;
|
||||
}
|
||||
}
|
||||
|
||||
return phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'unhandled-exception-detail',
|
||||
'class' => implode(' ', $classes),
|
||||
),
|
||||
array(
|
||||
phutil_tag(
|
||||
|
@ -79,6 +117,7 @@ final class AphrontUnhandledExceptionResponse
|
|||
'class' => 'unhandled-exception-body',
|
||||
),
|
||||
$body),
|
||||
$stack,
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -5,14 +5,22 @@
|
|||
* Normally this is just @{class:AphrontPHPHTTPSink}, which uses "echo" and
|
||||
* "header()" to emit responses.
|
||||
*
|
||||
* Mostly, this class allows us to do install security or metrics hooks in the
|
||||
* output pipeline.
|
||||
*
|
||||
* @task write Writing Response Components
|
||||
* @task emit Emitting the Response
|
||||
*/
|
||||
abstract class AphrontHTTPSink extends Phobject {
|
||||
|
||||
private $showStackTraces = false;
|
||||
|
||||
final public function setShowStackTraces($show_stack_traces) {
|
||||
$this->showStackTraces = $show_stack_traces;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getShowStackTraces() {
|
||||
return $this->showStackTraces;
|
||||
}
|
||||
|
||||
|
||||
/* -( Writing Response Components )---------------------------------------- */
|
||||
|
||||
|
|
|
@ -67,19 +67,13 @@ abstract class AlmanacController
|
|||
$is_builtin = isset($builtins[$key]);
|
||||
$is_persistent = (bool)$property->getID();
|
||||
|
||||
$delete_uri = id(new PhutilURI($delete_base))
|
||||
->setQueryParams(
|
||||
array(
|
||||
'key' => $key,
|
||||
'objectPHID' => $object->getPHID(),
|
||||
));
|
||||
$params = array(
|
||||
'key' => $key,
|
||||
'objectPHID' => $object->getPHID(),
|
||||
);
|
||||
|
||||
$edit_uri = id(new PhutilURI($edit_base))
|
||||
->setQueryParams(
|
||||
array(
|
||||
'key' => $key,
|
||||
'objectPHID' => $object->getPHID(),
|
||||
));
|
||||
$delete_uri = new PhutilURI($delete_base, $params);
|
||||
$edit_uri = new PhutilURI($edit_base, $params);
|
||||
|
||||
$delete = javelin_tag(
|
||||
'a',
|
||||
|
@ -143,7 +137,7 @@ abstract class AlmanacController
|
|||
|
||||
$phid = $object->getPHID();
|
||||
$add_uri = id(new PhutilURI($edit_base))
|
||||
->setQueryParam('objectPHID', $object->getPHID());
|
||||
->replaceQueryParam('objectPHID', $object->getPHID());
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
|
|
|
@ -105,10 +105,10 @@ final class PhabricatorAuditManagementDeleteWorkflow
|
|||
$query->withPHIDs(mpull($commits, 'getPHID'));
|
||||
}
|
||||
|
||||
$commits = $query->execute();
|
||||
$commits = mpull($commits, null, 'getPHID');
|
||||
$commit_iterator = new PhabricatorQueryIterator($query);
|
||||
|
||||
$audits = array();
|
||||
foreach ($commits as $commit) {
|
||||
foreach ($commit_iterator as $commit) {
|
||||
$commit_audits = $commit->getAudits();
|
||||
foreach ($commit_audits as $key => $audit) {
|
||||
if ($id_map && empty($id_map[$audit->getID()])) {
|
||||
|
@ -131,51 +131,87 @@ final class PhabricatorAuditManagementDeleteWorkflow
|
|||
continue;
|
||||
}
|
||||
}
|
||||
$audits[] = $commit_audits;
|
||||
}
|
||||
$audits = array_mergev($audits);
|
||||
|
||||
$console = PhutilConsole::getConsole();
|
||||
if (!$commit_audits) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$audits) {
|
||||
$console->writeErr("%s\n", pht('No audits match the query.'));
|
||||
return 0;
|
||||
}
|
||||
$handles = id(new PhabricatorHandleQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(mpull($commit_audits, 'getAuditorPHID'))
|
||||
->execute();
|
||||
|
||||
$handles = id(new PhabricatorHandleQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withPHIDs(mpull($audits, 'getAuditorPHID'))
|
||||
->execute();
|
||||
foreach ($commit_audits as $audit) {
|
||||
$audit_id = $audit->getID();
|
||||
|
||||
|
||||
foreach ($audits as $audit) {
|
||||
$commit = $commits[$audit->getCommitPHID()];
|
||||
|
||||
$console->writeOut(
|
||||
"%s\n",
|
||||
sprintf(
|
||||
$description = sprintf(
|
||||
'%10d %-16s %-16s %s: %s',
|
||||
$audit->getID(),
|
||||
$audit_id,
|
||||
$handles[$audit->getAuditorPHID()]->getName(),
|
||||
PhabricatorAuditStatusConstants::getStatusName(
|
||||
$audit->getAuditStatus()),
|
||||
$commit->getRepository()->formatCommitName(
|
||||
$commit->getCommitIdentifier()),
|
||||
trim($commit->getSummary())));
|
||||
trim($commit->getSummary()));
|
||||
|
||||
$audits[] = array(
|
||||
'auditID' => $audit_id,
|
||||
'commitPHID' => $commit->getPHID(),
|
||||
'description' => $description,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$is_dry_run) {
|
||||
$message = pht(
|
||||
'Really delete these %d audit(s)? They will be permanently deleted '.
|
||||
'and can not be recovered.',
|
||||
count($audits));
|
||||
if ($console->confirm($message)) {
|
||||
foreach ($audits as $audit) {
|
||||
$id = $audit->getID();
|
||||
$console->writeOut("%s\n", pht('Deleting audit %d...', $id));
|
||||
$audit->delete();
|
||||
}
|
||||
if (!$audits) {
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht('No audits match the query.'));
|
||||
return 0;
|
||||
}
|
||||
|
||||
foreach ($audits as $audit_spec) {
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
$audit_spec['description']);
|
||||
}
|
||||
|
||||
if ($is_dry_run) {
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht('This is a dry run, so no changes will be made.'));
|
||||
return 0;
|
||||
}
|
||||
|
||||
$message = pht(
|
||||
'Really delete these %s audit(s)? They will be permanently deleted '.
|
||||
'and can not be recovered.',
|
||||
phutil_count($audits));
|
||||
if (!phutil_console_confirm($message)) {
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht('User aborted the workflow.'));
|
||||
return 1;
|
||||
}
|
||||
|
||||
$audits_by_commit = igroup($audits, 'commitPHID');
|
||||
foreach ($audits_by_commit as $commit_phid => $audit_specs) {
|
||||
$audit_ids = ipull($audit_specs, 'auditID');
|
||||
|
||||
$audits = id(new PhabricatorRepositoryAuditRequest())->loadAllWhere(
|
||||
'id IN (%Ld)',
|
||||
$audit_ids);
|
||||
|
||||
foreach ($audits as $audit) {
|
||||
$id = $audit->getID();
|
||||
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht('Deleting audit %d...', $id));
|
||||
|
||||
$audit->delete();
|
||||
}
|
||||
|
||||
$this->synchronizeCommitAuditState($commit_phid);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -87,4 +87,39 @@ abstract class PhabricatorAuditManagementWorkflow
|
|||
return $commits;
|
||||
}
|
||||
|
||||
protected function synchronizeCommitAuditState($commit_phid) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$commit = id(new DiffusionCommitQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($commit_phid))
|
||||
->needAuditRequests(true)
|
||||
->executeOne();
|
||||
if (!$commit) {
|
||||
return;
|
||||
}
|
||||
|
||||
$old_status = $commit->getAuditStatusObject();
|
||||
$commit->updateAuditStatus($commit->getAudits());
|
||||
$new_status = $commit->getAuditStatusObject();
|
||||
|
||||
if ($old_status->getKey() == $new_status->getKey()) {
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'No synchronization changes for "%s".',
|
||||
$commit->getDisplayName()));
|
||||
} else {
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'Synchronizing "%s": "%s" -> "%s".',
|
||||
$commit->getDisplayName(),
|
||||
$old_status->getName(),
|
||||
$new_status->getName()));
|
||||
|
||||
$commit->save();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,8 +6,16 @@ final class PhabricatorAuditSynchronizeManagementWorkflow
|
|||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('synchronize')
|
||||
->setExamples('**synchronize** ...')
|
||||
->setSynopsis(pht('Update audit status for commits.'))
|
||||
->setExamples(
|
||||
"**synchronize** __repository__ ...\n".
|
||||
"**synchronize** __commit__ ...\n".
|
||||
"**synchronize** --all")
|
||||
->setSynopsis(
|
||||
pht(
|
||||
'Update commits to make their summary audit state reflect the '.
|
||||
'state of their actual audit requests. This can fix inconsistencies '.
|
||||
'in database state if audit requests have been mangled '.
|
||||
'accidentally (or on purpose).'))
|
||||
->setArguments(
|
||||
array_merge(
|
||||
$this->getCommitConstraintArguments(),
|
||||
|
@ -21,36 +29,7 @@ final class PhabricatorAuditSynchronizeManagementWorkflow
|
|||
foreach ($objects as $object) {
|
||||
$commits = $this->loadCommitsForConstraintObject($object);
|
||||
foreach ($commits as $commit) {
|
||||
$commit = id(new DiffusionCommitQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($commit->getPHID()))
|
||||
->needAuditRequests(true)
|
||||
->executeOne();
|
||||
if (!$commit) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$old_status = $commit->getAuditStatusObject();
|
||||
$commit->updateAuditStatus($commit->getAudits());
|
||||
$new_status = $commit->getAuditStatusObject();
|
||||
|
||||
if ($old_status->getKey() == $new_status->getKey()) {
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'No changes for "%s".',
|
||||
$commit->getDisplayName()));
|
||||
} else {
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'Updating "%s": "%s" -> "%s".',
|
||||
$commit->getDisplayName(),
|
||||
$old_status->getName(),
|
||||
$new_status->getName()));
|
||||
|
||||
$commit->save();
|
||||
}
|
||||
$this->synchronizeCommitAuditState($commit->getPHID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,8 +61,8 @@ final class PhabricatorAuthApplication extends PhabricatorApplication {
|
|||
'start/' => 'PhabricatorAuthStartController',
|
||||
'validate/' => 'PhabricatorAuthValidateController',
|
||||
'finish/' => 'PhabricatorAuthFinishController',
|
||||
'unlink/(?P<pkey>[^/]+)/' => 'PhabricatorAuthUnlinkController',
|
||||
'(?P<action>link|refresh)/(?P<pkey>[^/]+)/'
|
||||
'unlink/(?P<id>\d+)/' => 'PhabricatorAuthUnlinkController',
|
||||
'(?P<action>link|refresh)/(?P<id>\d+)/'
|
||||
=> 'PhabricatorAuthLinkController',
|
||||
'confirmlink/(?P<akey>[^/]+)/'
|
||||
=> 'PhabricatorAuthConfirmLinkController',
|
||||
|
@ -86,7 +86,9 @@ final class PhabricatorAuthApplication extends PhabricatorApplication {
|
|||
=> 'PhabricatorAuthSSHKeyRevokeController',
|
||||
'view/(?P<id>\d+)/' => 'PhabricatorAuthSSHKeyViewController',
|
||||
),
|
||||
|
||||
'password/' => 'PhabricatorAuthSetPasswordController',
|
||||
'external/' => 'PhabricatorAuthSetExternalController',
|
||||
|
||||
'mfa/' => array(
|
||||
$this->getQueryRoutePattern() =>
|
||||
|
@ -97,6 +99,8 @@ final class PhabricatorAuthApplication extends PhabricatorApplication {
|
|||
'PhabricatorAuthFactorProviderViewController',
|
||||
'message/(?P<id>[1-9]\d*)/' =>
|
||||
'PhabricatorAuthFactorProviderMessageController',
|
||||
'challenge/status/(?P<id>[1-9]\d*)/' =>
|
||||
'PhabricatorAuthChallengeStatusController',
|
||||
),
|
||||
|
||||
'message/' => array(
|
||||
|
|
|
@ -20,7 +20,15 @@ final class PhabricatorAuthConfirmLinkController
|
|||
|
||||
$panel_uri = '/settings/panel/external/';
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
if ($request->isFormOrHisecPost()) {
|
||||
$workflow_key = sprintf(
|
||||
'account.link(%s)',
|
||||
$account->getPHID());
|
||||
|
||||
$hisec_token = id(new PhabricatorAuthSessionEngine())
|
||||
->setWorkflowKey($workflow_key)
|
||||
->requireHighSecurityToken($viewer, $request, $panel_uri);
|
||||
|
||||
$account->setUserPHID($viewer->getPHID());
|
||||
$account->save();
|
||||
|
||||
|
@ -31,14 +39,7 @@ final class PhabricatorAuthConfirmLinkController
|
|||
return id(new AphrontRedirectResponse())->setURI($panel_uri);
|
||||
}
|
||||
|
||||
// TODO: Provide more information about the external account. Clicking
|
||||
// through this form blindly is dangerous.
|
||||
|
||||
// TODO: If the user has password authentication, require them to retype
|
||||
// their password here.
|
||||
|
||||
$dialog = id(new AphrontDialogView())
|
||||
->setUser($viewer)
|
||||
$dialog = $this->newDialog()
|
||||
->setTitle(pht('Confirm %s Account Link', $provider->getProviderName()))
|
||||
->addCancelButton($panel_uri)
|
||||
->addSubmitButton(pht('Confirm Account Link'));
|
||||
|
|
|
@ -95,7 +95,7 @@ abstract class PhabricatorAuthController extends PhabricatorController {
|
|||
|
||||
private function buildLoginValidateResponse(PhabricatorUser $user) {
|
||||
$validate_uri = new PhutilURI($this->getApplicationURI('validate/'));
|
||||
$validate_uri->setQueryParam('expect', $user->getUsername());
|
||||
$validate_uri->replaceQueryParam('expect', $user->getUsername());
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI((string)$validate_uri);
|
||||
}
|
||||
|
@ -213,19 +213,19 @@ abstract class PhabricatorAuthController extends PhabricatorController {
|
|||
return array($account, $provider, $response);
|
||||
}
|
||||
|
||||
$provider = PhabricatorAuthProvider::getEnabledProviderByKey(
|
||||
$account->getProviderKey());
|
||||
|
||||
if (!$provider) {
|
||||
$config = $account->getProviderConfig();
|
||||
if (!$config->getIsEnabled()) {
|
||||
$response = $this->renderError(
|
||||
pht(
|
||||
'The account you are attempting to register with uses a nonexistent '.
|
||||
'or disabled authentication provider (with key "%s"). An '.
|
||||
'administrator may have recently disabled this provider.',
|
||||
$account->getProviderKey()));
|
||||
'The account you are attempting to register with uses a disabled '.
|
||||
'authentication provider ("%s"). An administrator may have '.
|
||||
'recently disabled this provider.',
|
||||
$config->getDisplayName()));
|
||||
return array($account, $provider, $response);
|
||||
}
|
||||
|
||||
$provider = $config->getProvider();
|
||||
|
||||
return array($account, $provider, null);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,14 +6,20 @@ final class PhabricatorAuthLinkController
|
|||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
$action = $request->getURIData('action');
|
||||
$provider_key = $request->getURIData('pkey');
|
||||
|
||||
$provider = PhabricatorAuthProvider::getEnabledProviderByKey(
|
||||
$provider_key);
|
||||
if (!$provider) {
|
||||
$id = $request->getURIData('id');
|
||||
|
||||
$config = id(new PhabricatorAuthProviderConfigQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->withIsEnabled(true)
|
||||
->executeOne();
|
||||
if (!$config) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$provider = $config->getProvider();
|
||||
|
||||
switch ($action) {
|
||||
case 'link':
|
||||
if (!$provider->shouldAllowAccountLink()) {
|
||||
|
@ -37,15 +43,15 @@ final class PhabricatorAuthLinkController
|
|||
return new Aphront400Response();
|
||||
}
|
||||
|
||||
$account = id(new PhabricatorExternalAccount())->loadOneWhere(
|
||||
'accountType = %s AND accountDomain = %s AND userPHID = %s',
|
||||
$provider->getProviderType(),
|
||||
$provider->getProviderDomain(),
|
||||
$viewer->getPHID());
|
||||
$accounts = id(new PhabricatorExternalAccountQuery())
|
||||
->setViewer($viewer)
|
||||
->withUserPHIDs(array($viewer->getPHID()))
|
||||
->withProviderConfigPHIDs(array($config->getPHID()))
|
||||
->execute();
|
||||
|
||||
switch ($action) {
|
||||
case 'link':
|
||||
if ($account) {
|
||||
if ($accounts) {
|
||||
return $this->renderErrorPage(
|
||||
pht('Account Already Linked'),
|
||||
array(
|
||||
|
@ -56,7 +62,7 @@ final class PhabricatorAuthLinkController
|
|||
}
|
||||
break;
|
||||
case 'refresh':
|
||||
if (!$account) {
|
||||
if (!$accounts) {
|
||||
return $this->renderErrorPage(
|
||||
pht('No Account Linked'),
|
||||
array(
|
||||
|
@ -76,11 +82,6 @@ final class PhabricatorAuthLinkController
|
|||
|
||||
switch ($action) {
|
||||
case 'link':
|
||||
id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
|
||||
$viewer,
|
||||
$request,
|
||||
$panel_uri);
|
||||
|
||||
$form = $provider->buildLinkForm($this);
|
||||
break;
|
||||
case 'refresh':
|
||||
|
|
|
@ -35,6 +35,7 @@ final class PhabricatorAuthLoginController
|
|||
return $response;
|
||||
}
|
||||
|
||||
$invite = $this->loadInvite();
|
||||
$provider = $this->provider;
|
||||
|
||||
try {
|
||||
|
@ -103,7 +104,7 @@ final class PhabricatorAuthLoginController
|
|||
// The account is not yet attached to a Phabricator user, so this is
|
||||
// either a registration or an account link request.
|
||||
if (!$viewer->isLoggedIn()) {
|
||||
if ($provider->shouldAllowRegistration()) {
|
||||
if ($provider->shouldAllowRegistration() || $invite) {
|
||||
return $this->processRegisterUser($account);
|
||||
} else {
|
||||
return $this->renderError(
|
||||
|
|
|
@ -14,11 +14,6 @@ final class PhabricatorAuthOneTimeLoginController
|
|||
$key = $request->getURIData('key');
|
||||
$email_id = $request->getURIData('emailID');
|
||||
|
||||
if ($request->getUser()->isLoggedIn()) {
|
||||
return $this->renderError(
|
||||
pht('You are already logged in.'));
|
||||
}
|
||||
|
||||
$target_user = id(new PhabricatorPeopleQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withIDs(array($id))
|
||||
|
@ -27,6 +22,19 @@ final class PhabricatorAuthOneTimeLoginController
|
|||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
// NOTE: We allow you to use a one-time login link for your own current
|
||||
// login account. This supports the "Set Password" flow.
|
||||
|
||||
$is_logged_in = false;
|
||||
if ($viewer->isLoggedIn()) {
|
||||
if ($viewer->getPHID() !== $target_user->getPHID()) {
|
||||
return $this->renderError(
|
||||
pht('You are already logged in.'));
|
||||
} else {
|
||||
$is_logged_in = true;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: As a convenience to users, these one-time login URIs may also
|
||||
// be associated with an email address which will be verified when the
|
||||
// URI is used.
|
||||
|
@ -100,7 +108,7 @@ final class PhabricatorAuthOneTimeLoginController
|
|||
->addCancelButton('/');
|
||||
}
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
if ($request->isFormPost() || $is_logged_in) {
|
||||
// If we have an email bound into this URI, verify email so that clicking
|
||||
// the link in the "Welcome" email is good enough, without requiring users
|
||||
// to go through a second round of email verification.
|
||||
|
@ -121,6 +129,12 @@ final class PhabricatorAuthOneTimeLoginController
|
|||
|
||||
$next_uri = $this->getNextStepURI($target_user);
|
||||
|
||||
// If the user is already logged in, we're just doing a "password set"
|
||||
// flow. Skip directly to the next step.
|
||||
if ($is_logged_in) {
|
||||
return id(new AphrontRedirectResponse())->setURI($next_uri);
|
||||
}
|
||||
|
||||
PhabricatorCookies::setNextURICookie($request, $next_uri, $force = true);
|
||||
|
||||
$force_full_session = false;
|
||||
|
@ -204,24 +218,52 @@ final class PhabricatorAuthOneTimeLoginController
|
|||
|
||||
$request->setTemporaryCookie(PhabricatorCookies::COOKIE_HISEC, 'yes');
|
||||
|
||||
return (string)id(new PhutilURI($panel_uri))
|
||||
->setQueryParams(
|
||||
array(
|
||||
'key' => $key,
|
||||
));
|
||||
$params = array(
|
||||
'key' => $key,
|
||||
);
|
||||
|
||||
return (string)new PhutilURI($panel_uri, $params);
|
||||
}
|
||||
|
||||
$providers = id(new PhabricatorAuthProviderConfigQuery())
|
||||
// Check if the user already has external accounts linked. If they do,
|
||||
// it's not obvious why they aren't using them to log in, but assume they
|
||||
// know what they're doing. We won't send them to the link workflow.
|
||||
$accounts = id(new PhabricatorExternalAccountQuery())
|
||||
->setViewer($user)
|
||||
->withUserPHIDs(array($user->getPHID()))
|
||||
->execute();
|
||||
|
||||
$configs = id(new PhabricatorAuthProviderConfigQuery())
|
||||
->setViewer($user)
|
||||
->withIsEnabled(true)
|
||||
->execute();
|
||||
|
||||
$linkable = array();
|
||||
foreach ($configs as $config) {
|
||||
if (!$config->getShouldAllowLink()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$provider = $config->getProvider();
|
||||
if (!$provider->isLoginFormAButton()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$linkable[] = $provider;
|
||||
}
|
||||
|
||||
// If there's at least one linkable provider, and the user doesn't already
|
||||
// have accounts, send the user to the link workflow.
|
||||
if (!$accounts && $linkable) {
|
||||
return '/auth/external/';
|
||||
}
|
||||
|
||||
// If there are no configured providers and the user is an administrator,
|
||||
// send them to Auth to configure a provider. This is probably where they
|
||||
// want to go. You can end up in this state by accidentally losing your
|
||||
// first session during initial setup, or after restoring exported data
|
||||
// from a hosted instance.
|
||||
if (!$providers && $user->getIsAdmin()) {
|
||||
if (!$configs && $user->getIsAdmin()) {
|
||||
return '/auth/';
|
||||
}
|
||||
|
||||
|
|
|
@ -11,21 +11,25 @@ final class PhabricatorAuthRegisterController
|
|||
$viewer = $this->getViewer();
|
||||
$account_key = $request->getURIData('akey');
|
||||
|
||||
if ($request->getUser()->isLoggedIn()) {
|
||||
if ($viewer->isLoggedIn()) {
|
||||
return id(new AphrontRedirectResponse())->setURI('/');
|
||||
}
|
||||
|
||||
$invite = $this->loadInvite();
|
||||
|
||||
$is_setup = false;
|
||||
if (strlen($account_key)) {
|
||||
$result = $this->loadAccountForRegistrationOrLinking($account_key);
|
||||
list($account, $provider, $response) = $result;
|
||||
$is_default = false;
|
||||
} else if ($this->isFirstTimeSetup()) {
|
||||
list($account, $provider, $response) = $this->loadSetupAccount();
|
||||
$account = null;
|
||||
$provider = null;
|
||||
$response = null;
|
||||
$is_default = true;
|
||||
$is_setup = true;
|
||||
} else {
|
||||
list($account, $provider, $response) = $this->loadDefaultAccount();
|
||||
list($account, $provider, $response) = $this->loadDefaultAccount($invite);
|
||||
$is_default = true;
|
||||
}
|
||||
|
||||
|
@ -33,24 +37,24 @@ final class PhabricatorAuthRegisterController
|
|||
return $response;
|
||||
}
|
||||
|
||||
$invite = $this->loadInvite();
|
||||
if (!$is_setup) {
|
||||
if (!$provider->shouldAllowRegistration()) {
|
||||
if ($invite) {
|
||||
// If the user has an invite, we allow them to register with any
|
||||
// provider, even a login-only provider.
|
||||
} else {
|
||||
// TODO: This is a routine error if you click "Login" on an external
|
||||
// auth source which doesn't allow registration. The error should be
|
||||
// more tailored.
|
||||
|
||||
if (!$provider->shouldAllowRegistration()) {
|
||||
if ($invite) {
|
||||
// If the user has an invite, we allow them to register with any
|
||||
// provider, even a login-only provider.
|
||||
} else {
|
||||
// TODO: This is a routine error if you click "Login" on an external
|
||||
// auth source which doesn't allow registration. The error should be
|
||||
// more tailored.
|
||||
|
||||
return $this->renderError(
|
||||
pht(
|
||||
'The account you are attempting to register with uses an '.
|
||||
'authentication provider ("%s") which does not allow '.
|
||||
'registration. An administrator may have recently disabled '.
|
||||
'registration with this provider.',
|
||||
$provider->getProviderName()));
|
||||
return $this->renderError(
|
||||
pht(
|
||||
'The account you are attempting to register with uses an '.
|
||||
'authentication provider ("%s") which does not allow '.
|
||||
'registration. An administrator may have recently disabled '.
|
||||
'registration with this provider.',
|
||||
$provider->getProviderName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,14 +62,19 @@ final class PhabricatorAuthRegisterController
|
|||
|
||||
$user = new PhabricatorUser();
|
||||
|
||||
$default_username = $account->getUsername();
|
||||
$default_realname = $account->getRealName();
|
||||
if ($is_setup) {
|
||||
$default_username = null;
|
||||
$default_realname = null;
|
||||
$default_email = null;
|
||||
} else {
|
||||
$default_username = $account->getUsername();
|
||||
$default_realname = $account->getRealName();
|
||||
$default_email = $account->getEmail();
|
||||
}
|
||||
|
||||
$account_type = PhabricatorAuthPassword::PASSWORD_TYPE_ACCOUNT;
|
||||
$content_source = PhabricatorContentSource::newFromRequest($request);
|
||||
|
||||
$default_email = $account->getEmail();
|
||||
|
||||
if ($invite) {
|
||||
$default_email = $invite->getEmailAddress();
|
||||
}
|
||||
|
@ -212,7 +221,11 @@ final class PhabricatorAuthRegisterController
|
|||
$can_edit_email = $profile->getCanEditEmail();
|
||||
$can_edit_realname = $profile->getCanEditRealName();
|
||||
|
||||
$must_set_password = $provider->shouldRequireRegistrationPassword();
|
||||
if ($is_setup) {
|
||||
$must_set_password = false;
|
||||
} else {
|
||||
$must_set_password = $provider->shouldRequireRegistrationPassword();
|
||||
}
|
||||
|
||||
$can_edit_anything = $profile->getCanEditAnything() || $must_set_password;
|
||||
$force_verify = $profile->getShouldVerifyEmail();
|
||||
|
@ -334,9 +347,11 @@ final class PhabricatorAuthRegisterController
|
|||
}
|
||||
|
||||
if (!$errors) {
|
||||
$image = $this->loadProfilePicture($account);
|
||||
if ($image) {
|
||||
$user->setProfileImagePHID($image->getPHID());
|
||||
if (!$is_setup) {
|
||||
$image = $this->loadProfilePicture($account);
|
||||
if ($image) {
|
||||
$user->setProfileImagePHID($image->getPHID());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -346,17 +361,19 @@ final class PhabricatorAuthRegisterController
|
|||
$verify_email = true;
|
||||
}
|
||||
|
||||
if ($value_email === $default_email) {
|
||||
if ($account->getEmailVerified()) {
|
||||
$verify_email = true;
|
||||
}
|
||||
if (!$is_setup) {
|
||||
if ($value_email === $default_email) {
|
||||
if ($account->getEmailVerified()) {
|
||||
$verify_email = true;
|
||||
}
|
||||
|
||||
if ($provider->shouldTrustEmails()) {
|
||||
$verify_email = true;
|
||||
}
|
||||
if ($provider->shouldTrustEmails()) {
|
||||
$verify_email = true;
|
||||
}
|
||||
|
||||
if ($invite) {
|
||||
$verify_email = true;
|
||||
if ($invite) {
|
||||
$verify_email = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -438,9 +455,11 @@ final class PhabricatorAuthRegisterController
|
|||
$transaction_editor->applyTransactions($user, $xactions);
|
||||
}
|
||||
|
||||
$account->setUserPHID($user->getPHID());
|
||||
$provider->willRegisterAccount($account);
|
||||
$account->save();
|
||||
if (!$is_setup) {
|
||||
$account->setUserPHID($user->getPHID());
|
||||
$provider->willRegisterAccount($account);
|
||||
$account->save();
|
||||
}
|
||||
|
||||
$user->saveTransaction();
|
||||
|
||||
|
@ -501,7 +520,6 @@ final class PhabricatorAuthRegisterController
|
|||
->setAuthProvider($provider)));
|
||||
}
|
||||
|
||||
|
||||
if ($can_edit_username) {
|
||||
$form->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
|
@ -595,7 +613,7 @@ final class PhabricatorAuthRegisterController
|
|||
pht(
|
||||
'Installation is complete. Register your administrator account '.
|
||||
'below to log in. You will be able to configure options and add '.
|
||||
'other authentication mechanisms (like LDAP or OAuth) later on.'));
|
||||
'authentication mechanisms later on.'));
|
||||
}
|
||||
|
||||
$object_box = id(new PHUIObjectBoxView())
|
||||
|
@ -612,11 +630,12 @@ final class PhabricatorAuthRegisterController
|
|||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setFooter(array(
|
||||
$welcome_view,
|
||||
$invite_header,
|
||||
$object_box,
|
||||
));
|
||||
->setFooter(
|
||||
array(
|
||||
$welcome_view,
|
||||
$invite_header,
|
||||
$object_box,
|
||||
));
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
|
@ -624,17 +643,20 @@ final class PhabricatorAuthRegisterController
|
|||
->appendChild($view);
|
||||
}
|
||||
|
||||
private function loadDefaultAccount() {
|
||||
private function loadDefaultAccount($invite) {
|
||||
$providers = PhabricatorAuthProvider::getAllEnabledProviders();
|
||||
$account = null;
|
||||
$provider = null;
|
||||
$response = null;
|
||||
|
||||
foreach ($providers as $key => $candidate_provider) {
|
||||
if (!$candidate_provider->shouldAllowRegistration()) {
|
||||
unset($providers[$key]);
|
||||
continue;
|
||||
if (!$invite) {
|
||||
if (!$candidate_provider->shouldAllowRegistration()) {
|
||||
unset($providers[$key]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$candidate_provider->isDefaultRegistrationProvider()) {
|
||||
unset($providers[$key]);
|
||||
}
|
||||
|
@ -652,24 +674,11 @@ final class PhabricatorAuthRegisterController
|
|||
}
|
||||
|
||||
$provider = head($providers);
|
||||
$account = $provider->getDefaultExternalAccount();
|
||||
$account = $provider->newDefaultExternalAccount();
|
||||
|
||||
return array($account, $provider, $response);
|
||||
}
|
||||
|
||||
private function loadSetupAccount() {
|
||||
$provider = new PhabricatorPasswordAuthProvider();
|
||||
$provider->attachProviderConfig(
|
||||
id(new PhabricatorAuthProviderConfig())
|
||||
->setShouldAllowRegistration(1)
|
||||
->setShouldAllowLogin(1)
|
||||
->setIsEnabled(true));
|
||||
|
||||
$account = $provider->getDefaultExternalAccount();
|
||||
$response = null;
|
||||
return array($account, $provider, $response);
|
||||
}
|
||||
|
||||
private function loadProfilePicture(PhabricatorExternalAccount $account) {
|
||||
$phid = $account->getProfileImagePHID();
|
||||
if (!$phid) {
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorAuthSetExternalController
|
||||
extends PhabricatorAuthController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$configs = id(new PhabricatorAuthProviderConfigQuery())
|
||||
->setViewer($viewer)
|
||||
->withIsEnabled(true)
|
||||
->execute();
|
||||
|
||||
$linkable = array();
|
||||
foreach ($configs as $config) {
|
||||
if (!$config->getShouldAllowLink()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// For now, only buttons get to appear here: for example, we can't
|
||||
// reasonably embed an entire LDAP form into this UI.
|
||||
$provider = $config->getProvider();
|
||||
if (!$provider->isLoginFormAButton()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$linkable[] = $config;
|
||||
}
|
||||
|
||||
if (!$linkable) {
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('No Linkable External Providers'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'Currently, there are no configured external auth providers '.
|
||||
'which you can link your account to.'))
|
||||
->addCancelButton('/');
|
||||
}
|
||||
|
||||
$text = PhabricatorAuthMessage::loadMessageText(
|
||||
$viewer,
|
||||
PhabricatorAuthLinkMessageType::MESSAGEKEY);
|
||||
if (!strlen($text)) {
|
||||
$text = pht(
|
||||
'You can link your Phabricator account to an external account to '.
|
||||
'allow you to log in more easily in the future. To continue, choose '.
|
||||
'an account to link below. If you prefer not to link your account, '.
|
||||
'you can skip this step.');
|
||||
}
|
||||
|
||||
$remarkup_view = new PHUIRemarkupView($viewer, $text);
|
||||
$remarkup_view = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'phui-object-box-instructions',
|
||||
),
|
||||
$remarkup_view);
|
||||
|
||||
PhabricatorCookies::setClientIDCookie($request);
|
||||
|
||||
$view = array();
|
||||
foreach ($configs as $config) {
|
||||
$provider = $config->getProvider();
|
||||
|
||||
$form = $provider->buildLinkForm($this);
|
||||
|
||||
if ($provider->isLoginFormAButton()) {
|
||||
require_celerity_resource('auth-css');
|
||||
$form = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'phabricator-link-button pl',
|
||||
),
|
||||
$form);
|
||||
}
|
||||
|
||||
$view[] = $form;
|
||||
}
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setViewer($viewer)
|
||||
->appendControl(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->addCancelButton('/', pht('Skip This Step')));
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Link External Account'));
|
||||
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setViewer($viewer)
|
||||
->setHeader($header)
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->appendChild($remarkup_view)
|
||||
->appendChild($view)
|
||||
->appendChild($form);
|
||||
|
||||
$main_view = id(new PHUITwoColumnView())
|
||||
->setFooter($box);
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs()
|
||||
->addTextCrumb(pht('Link External Account'))
|
||||
->setBorder(true);
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle(pht('Link External Account'))
|
||||
->setCrumbs($crumbs)
|
||||
->appendChild($main_view);
|
||||
}
|
||||
|
||||
}
|
|
@ -54,7 +54,7 @@ final class PhabricatorAuthStartController
|
|||
}
|
||||
|
||||
$redirect_uri = $request->getRequestURI();
|
||||
$redirect_uri->setQueryParam('cleared', 1);
|
||||
$redirect_uri->replaceQueryParam('cleared', 1);
|
||||
return id(new AphrontRedirectResponse())->setURI($redirect_uri);
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ final class PhabricatorAuthStartController
|
|||
// the workflow will continue normally.
|
||||
if ($did_clear) {
|
||||
$redirect_uri = $request->getRequestURI();
|
||||
$redirect_uri->setQueryParam('cleared', null);
|
||||
$redirect_uri->removeQueryParam('cleared');
|
||||
return id(new AphrontRedirectResponse())->setURI($redirect_uri);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,48 +3,45 @@
|
|||
final class PhabricatorAuthUnlinkController
|
||||
extends PhabricatorAuthController {
|
||||
|
||||
private $providerKey;
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
$this->providerKey = $request->getURIData('pkey');
|
||||
$id = $request->getURIData('id');
|
||||
|
||||
list($type, $domain) = explode(':', $this->providerKey, 2);
|
||||
|
||||
// Check that this account link actually exists. We don't require the
|
||||
// provider to exist because we want users to be able to delete links to
|
||||
// dead accounts if they want.
|
||||
$account = id(new PhabricatorExternalAccount())->loadOneWhere(
|
||||
'accountType = %s AND accountDomain = %s AND userPHID = %s',
|
||||
$type,
|
||||
$domain,
|
||||
$viewer->getPHID());
|
||||
$account = id(new PhabricatorExternalAccountQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
if (!$account) {
|
||||
return $this->renderNoAccountErrorDialog();
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
// Check that the provider (if it exists) allows accounts to be unlinked.
|
||||
$provider_key = $this->providerKey;
|
||||
$provider = PhabricatorAuthProvider::getEnabledProviderByKey($provider_key);
|
||||
if ($provider) {
|
||||
if (!$provider->shouldAllowAccountUnlink()) {
|
||||
return $this->renderNotUnlinkableErrorDialog($provider);
|
||||
}
|
||||
$done_uri = '/settings/panel/external/';
|
||||
|
||||
$config = $account->getProviderConfig();
|
||||
$provider = $config->getProvider();
|
||||
if (!$provider->shouldAllowAccountUnlink()) {
|
||||
return $this->renderNotUnlinkableErrorDialog($provider, $done_uri);
|
||||
}
|
||||
|
||||
$confirmations = $request->getStrList('confirmations');
|
||||
$confirmations = array_fuse($confirmations);
|
||||
|
||||
if (!$request->isFormPost() || !isset($confirmations['unlink'])) {
|
||||
return $this->renderConfirmDialog($confirmations);
|
||||
if (!$request->isFormOrHisecPost() || !isset($confirmations['unlink'])) {
|
||||
return $this->renderConfirmDialog($confirmations, $config, $done_uri);
|
||||
}
|
||||
|
||||
// Check that this account isn't the only account which can be used to
|
||||
// login. We warn you when you remove your only login account.
|
||||
if ($account->isUsableForLogin()) {
|
||||
$other_accounts = id(new PhabricatorExternalAccount())->loadAllWhere(
|
||||
'userPHID = %s',
|
||||
$viewer->getPHID());
|
||||
$other_accounts = id(new PhabricatorExternalAccountQuery())
|
||||
->setViewer($viewer)
|
||||
->withUserPHIDs(array($viewer->getPHID()))
|
||||
->execute();
|
||||
|
||||
$valid_accounts = 0;
|
||||
foreach ($other_accounts as $other_account) {
|
||||
|
@ -55,11 +52,21 @@ final class PhabricatorAuthUnlinkController
|
|||
|
||||
if ($valid_accounts < 2) {
|
||||
if (!isset($confirmations['only'])) {
|
||||
return $this->renderOnlyUsableAccountConfirmDialog($confirmations);
|
||||
return $this->renderOnlyUsableAccountConfirmDialog(
|
||||
$confirmations,
|
||||
$done_uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$workflow_key = sprintf(
|
||||
'account.unlink(%s)',
|
||||
$account->getPHID());
|
||||
|
||||
$hisec_token = id(new PhabricatorAuthSessionEngine())
|
||||
->setWorkflowKey($workflow_key)
|
||||
->requireHighSecurityToken($viewer, $request, $done_uri);
|
||||
|
||||
$account->delete();
|
||||
|
||||
id(new PhabricatorAuthSessionEngine())->terminateLoginSessions(
|
||||
|
@ -67,42 +74,27 @@ final class PhabricatorAuthUnlinkController
|
|||
new PhutilOpaqueEnvelope(
|
||||
$request->getCookie(PhabricatorCookies::COOKIE_SESSION)));
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI($this->getDoneURI());
|
||||
}
|
||||
|
||||
private function getDoneURI() {
|
||||
return '/settings/panel/external/';
|
||||
}
|
||||
|
||||
private function renderNoAccountErrorDialog() {
|
||||
$dialog = id(new AphrontDialogView())
|
||||
->setUser($this->getRequest()->getUser())
|
||||
->setTitle(pht('No Such Account'))
|
||||
->appendChild(
|
||||
pht(
|
||||
'You can not unlink this account because it is not linked.'))
|
||||
->addCancelButton($this->getDoneURI());
|
||||
|
||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||
return id(new AphrontRedirectResponse())->setURI($done_uri);
|
||||
}
|
||||
|
||||
private function renderNotUnlinkableErrorDialog(
|
||||
PhabricatorAuthProvider $provider) {
|
||||
PhabricatorAuthProvider $provider,
|
||||
$done_uri) {
|
||||
|
||||
$dialog = id(new AphrontDialogView())
|
||||
->setUser($this->getRequest()->getUser())
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Permanent Account Link'))
|
||||
->appendChild(
|
||||
pht(
|
||||
'You can not unlink this account because the administrator has '.
|
||||
'configured Phabricator to make links to %s accounts permanent.',
|
||||
'configured Phabricator to make links to "%s" accounts permanent.',
|
||||
$provider->getProviderName()))
|
||||
->addCancelButton($this->getDoneURI());
|
||||
|
||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||
->addCancelButton($done_uri);
|
||||
}
|
||||
|
||||
private function renderOnlyUsableAccountConfirmDialog(array $confirmations) {
|
||||
private function renderOnlyUsableAccountConfirmDialog(
|
||||
array $confirmations,
|
||||
$done_uri) {
|
||||
|
||||
$confirmations[] = 'only';
|
||||
|
||||
return $this->newDialog()
|
||||
|
@ -116,28 +108,23 @@ final class PhabricatorAuthUnlinkController
|
|||
pht(
|
||||
'If you lose access to your account, you can recover access by '.
|
||||
'sending yourself an email login link from the login screen.'))
|
||||
->addCancelButton($this->getDoneURI())
|
||||
->addCancelButton($done_uri)
|
||||
->addSubmitButton(pht('Unlink External Account'));
|
||||
}
|
||||
|
||||
private function renderConfirmDialog(array $confirmations) {
|
||||
private function renderConfirmDialog(
|
||||
array $confirmations,
|
||||
PhabricatorAuthProviderConfig $config,
|
||||
$done_uri) {
|
||||
|
||||
$confirmations[] = 'unlink';
|
||||
$provider = $config->getProvider();
|
||||
|
||||
$provider_key = $this->providerKey;
|
||||
$provider = PhabricatorAuthProvider::getEnabledProviderByKey($provider_key);
|
||||
|
||||
if ($provider) {
|
||||
$title = pht('Unlink "%s" Account?', $provider->getProviderName());
|
||||
$body = pht(
|
||||
'You will no longer be able to use your %s account to '.
|
||||
'log in to Phabricator.',
|
||||
$provider->getProviderName());
|
||||
} else {
|
||||
$title = pht('Unlink Account?');
|
||||
$body = pht(
|
||||
'You will no longer be able to use this account to log in '.
|
||||
'to Phabricator.');
|
||||
}
|
||||
$title = pht('Unlink "%s" Account?', $provider->getProviderName());
|
||||
$body = pht(
|
||||
'You will no longer be able to use your %s account to '.
|
||||
'log in to Phabricator.',
|
||||
$provider->getProviderName());
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle($title)
|
||||
|
@ -148,7 +135,7 @@ final class PhabricatorAuthUnlinkController
|
|||
'Note: Unlinking an authentication provider will terminate any '.
|
||||
'other active login sessions.'))
|
||||
->addSubmitButton(pht('Unlink Account'))
|
||||
->addCancelButton($this->getDoneURI());
|
||||
->addCancelButton($done_uri);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,20 +9,38 @@ final class PhabricatorEmailLoginController
|
|||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
$is_logged_in = $viewer->isLoggedIn();
|
||||
|
||||
$e_email = true;
|
||||
$e_captcha = true;
|
||||
$errors = array();
|
||||
|
||||
$v_email = $request->getStr('email');
|
||||
if ($is_logged_in) {
|
||||
if (!$this->isPasswordAuthEnabled()) {
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('No Password Auth'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'Password authentication is not enabled and you are already '.
|
||||
'logged in. There is nothing for you here.'))
|
||||
->addCancelButton('/', pht('Continue'));
|
||||
}
|
||||
|
||||
$v_email = $viewer->loadPrimaryEmailAddress();
|
||||
} else {
|
||||
$v_email = $request->getStr('email');
|
||||
}
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$e_email = null;
|
||||
$e_captcha = pht('Again');
|
||||
|
||||
$captcha_ok = AphrontFormRecaptchaControl::processCaptcha($request);
|
||||
if (!$captcha_ok) {
|
||||
$errors[] = pht('Captcha response is incorrect, try again.');
|
||||
$e_captcha = pht('Invalid');
|
||||
if (!$is_logged_in) {
|
||||
$captcha_ok = AphrontFormRecaptchaControl::processCaptcha($request);
|
||||
if (!$captcha_ok) {
|
||||
$errors[] = pht('Captcha response is incorrect, try again.');
|
||||
$e_captcha = pht('Invalid');
|
||||
}
|
||||
}
|
||||
|
||||
if (!strlen($v_email)) {
|
||||
|
@ -76,10 +94,24 @@ final class PhabricatorEmailLoginController
|
|||
}
|
||||
|
||||
if (!$errors) {
|
||||
$body = $this->newAccountLoginMailBody($target_user);
|
||||
$body = $this->newAccountLoginMailBody(
|
||||
$target_user,
|
||||
$is_logged_in);
|
||||
|
||||
if ($is_logged_in) {
|
||||
$subject = pht('[Phabricator] Account Password Link');
|
||||
$instructions = pht(
|
||||
'An email has been sent containing a link you can use to set '.
|
||||
'a password for your account.');
|
||||
} else {
|
||||
$subject = pht('[Phabricator] Account Login Link');
|
||||
$instructions = pht(
|
||||
'An email has been sent containing a link you can use to log '.
|
||||
'in to your account.');
|
||||
}
|
||||
|
||||
$mail = id(new PhabricatorMetaMTAMail())
|
||||
->setSubject(pht('[Phabricator] Account Login Link'))
|
||||
->setSubject($subject)
|
||||
->setForceDelivery(true)
|
||||
->addRawTos(array($target_email->getAddress()))
|
||||
->setBody($body)
|
||||
|
@ -88,8 +120,7 @@ final class PhabricatorEmailLoginController
|
|||
return $this->newDialog()
|
||||
->setTitle(pht('Check Your Email'))
|
||||
->setShortTitle(pht('Email Sent'))
|
||||
->appendParagraph(
|
||||
pht('An email has been sent with a link you can use to log in.'))
|
||||
->appendParagraph($instructions)
|
||||
->addCancelButton('/', pht('Done'));
|
||||
}
|
||||
}
|
||||
|
@ -99,33 +130,47 @@ final class PhabricatorEmailLoginController
|
|||
->setViewer($viewer);
|
||||
|
||||
if ($this->isPasswordAuthEnabled()) {
|
||||
$form->appendRemarkupInstructions(
|
||||
pht(
|
||||
'To reset your password, provide your email address. An email '.
|
||||
'with a login link will be sent to you.'));
|
||||
if ($is_logged_in) {
|
||||
$title = pht('Set Password');
|
||||
$form->appendRemarkupInstructions(
|
||||
pht(
|
||||
'A password reset link will be sent to your primary email '.
|
||||
'address. Follow the link to set an account password.'));
|
||||
} else {
|
||||
$title = pht('Password Reset');
|
||||
$form->appendRemarkupInstructions(
|
||||
pht(
|
||||
'To reset your password, provide your email address. An email '.
|
||||
'with a login link will be sent to you.'));
|
||||
}
|
||||
} else {
|
||||
$title = pht('Email Login');
|
||||
$form->appendRemarkupInstructions(
|
||||
pht(
|
||||
'To access your account, provide your email address. An email '.
|
||||
'with a login link will be sent to you.'));
|
||||
}
|
||||
|
||||
if ($is_logged_in) {
|
||||
$address_control = new AphrontFormStaticControl();
|
||||
} else {
|
||||
$address_control = id(new AphrontFormTextControl())
|
||||
->setName('email')
|
||||
->setError($e_email);
|
||||
}
|
||||
|
||||
$address_control
|
||||
->setLabel(pht('Email Address'))
|
||||
->setValue($v_email);
|
||||
|
||||
$form
|
||||
->appendControl(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel(pht('Email Address'))
|
||||
->setName('email')
|
||||
->setValue($v_email)
|
||||
->setError($e_email))
|
||||
->appendControl(
|
||||
->appendControl($address_control);
|
||||
|
||||
if (!$is_logged_in) {
|
||||
$form->appendControl(
|
||||
id(new AphrontFormRecaptchaControl())
|
||||
->setLabel(pht('Captcha'))
|
||||
->setError($e_captcha));
|
||||
|
||||
if ($this->isPasswordAuthEnabled()) {
|
||||
$title = pht('Password Reset');
|
||||
} else {
|
||||
$title = pht('Email Login');
|
||||
}
|
||||
|
||||
return $this->newDialog()
|
||||
|
@ -137,7 +182,10 @@ final class PhabricatorEmailLoginController
|
|||
->addSubmitButton(pht('Send Email'));
|
||||
}
|
||||
|
||||
private function newAccountLoginMailBody(PhabricatorUser $user) {
|
||||
private function newAccountLoginMailBody(
|
||||
PhabricatorUser $user,
|
||||
$is_logged_in) {
|
||||
|
||||
$engine = new PhabricatorAuthSessionEngine();
|
||||
$uri = $engine->getOneTimeLoginURI(
|
||||
$user,
|
||||
|
@ -148,7 +196,12 @@ final class PhabricatorEmailLoginController
|
|||
$have_passwords = $this->isPasswordAuthEnabled();
|
||||
|
||||
if ($have_passwords) {
|
||||
if ($is_serious) {
|
||||
if ($is_logged_in) {
|
||||
$body = pht(
|
||||
'You can use this link to set a password on your account:'.
|
||||
"\n\n %s\n",
|
||||
$uri);
|
||||
} else if ($is_serious) {
|
||||
$body = pht(
|
||||
"You can use this link to reset your Phabricator password:".
|
||||
"\n\n %s\n",
|
||||
|
|
|
@ -32,7 +32,7 @@ final class PhabricatorAuthNewController
|
|||
$provider_class = get_class($provider);
|
||||
|
||||
$provider_uri = id(new PhutilURI('/config/edit/'))
|
||||
->setQueryParam('provider', $provider_class);
|
||||
->replaceQueryParam('provider', $provider_class);
|
||||
$provider_uri = $this->getApplicationURI($provider_uri);
|
||||
|
||||
$already_exists = isset($configured_classes[get_class($provider)]);
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorAuthChallengeStatusController
|
||||
extends PhabricatorAuthController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
$id = $request->getURIData('id');
|
||||
$now = PhabricatorTime::getNow();
|
||||
|
||||
$result = new PhabricatorAuthChallengeUpdate();
|
||||
|
||||
$challenge = id(new PhabricatorAuthChallengeQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->withUserPHIDs(array($viewer->getPHID()))
|
||||
->withChallengeTTLBetween($now, null)
|
||||
->executeOne();
|
||||
if ($challenge) {
|
||||
$config = id(new PhabricatorAuthFactorConfigQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($challenge->getFactorPHID()))
|
||||
->executeOne();
|
||||
if ($config) {
|
||||
$provider = $config->getFactorProvider();
|
||||
$factor = $provider->getFactor();
|
||||
|
||||
$result = $factor->newChallengeStatusView(
|
||||
$config,
|
||||
$provider,
|
||||
$viewer,
|
||||
$challenge);
|
||||
}
|
||||
}
|
||||
|
||||
return id(new AphrontAjaxResponse())
|
||||
->setContent($result->newContent());
|
||||
}
|
||||
|
||||
}
|
|
@ -45,7 +45,7 @@ final class PhabricatorAuthFactorProviderEditController
|
|||
|
||||
foreach ($factors as $factor_key => $factor) {
|
||||
$factor_uri = id(new PhutilURI('/mfa/edit/'))
|
||||
->setQueryParam('providerFactorKey', $factor_key);
|
||||
->replaceQueryParam('providerFactorKey', $factor_key);
|
||||
$factor_uri = $this->getApplicationURI($factor_uri);
|
||||
|
||||
$is_enabled = $factor->canCreateNewProvider();
|
||||
|
|
|
@ -42,7 +42,7 @@ final class PhabricatorAuthMainMenuBarExtension
|
|||
$uri = new PhutilURI('/auth/start/');
|
||||
if ($controller) {
|
||||
$path = $controller->getRequest()->getPath();
|
||||
$uri->setQueryParam('next', $path);
|
||||
$uri->replaceQueryParam('next', $path);
|
||||
}
|
||||
|
||||
return id(new PHUIButtonView())
|
||||
|
|
|
@ -80,6 +80,14 @@ abstract class PhabricatorAuthFactor extends Phobject {
|
|||
return array();
|
||||
}
|
||||
|
||||
public function newChallengeStatusView(
|
||||
PhabricatorAuthFactorConfig $config,
|
||||
PhabricatorAuthFactorProvider $provider,
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorAuthChallenge $challenge) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a factor which depends on the user's contact number?
|
||||
*
|
||||
|
@ -210,8 +218,6 @@ abstract class PhabricatorAuthFactor extends Phobject {
|
|||
get_class($this)));
|
||||
}
|
||||
|
||||
$result->setIssuedChallenges($challenges);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
@ -242,8 +248,6 @@ abstract class PhabricatorAuthFactor extends Phobject {
|
|||
get_class($this)));
|
||||
}
|
||||
|
||||
$result->setIssuedChallenges($challenges);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
@ -339,9 +343,18 @@ abstract class PhabricatorAuthFactor extends Phobject {
|
|||
->setIcon('fa-commenting', 'green');
|
||||
}
|
||||
|
||||
return id(new PHUIFormTimerControl())
|
||||
$control = id(new PHUIFormTimerControl())
|
||||
->setIcon($icon)
|
||||
->appendChild($error);
|
||||
|
||||
$status_challenge = $result->getStatusChallenge();
|
||||
if ($status_challenge) {
|
||||
$id = $status_challenge->getID();
|
||||
$uri = "/auth/mfa/challenge/status/{$id}/";
|
||||
$control->setUpdateURI($uri);
|
||||
}
|
||||
|
||||
return $control;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ final class PhabricatorAuthFactorResult
|
|||
private $value;
|
||||
private $issuedChallenges = array();
|
||||
private $icon;
|
||||
private $statusChallenge;
|
||||
|
||||
public function setAnsweredChallenge(PhabricatorAuthChallenge $challenge) {
|
||||
if (!$challenge->getIsAnsweredChallenge()) {
|
||||
|
@ -34,6 +35,15 @@ final class PhabricatorAuthFactorResult
|
|||
return $this->answeredChallenge;
|
||||
}
|
||||
|
||||
public function setStatusChallenge(PhabricatorAuthChallenge $challenge) {
|
||||
$this->statusChallenge = $challenge;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStatusChallenge() {
|
||||
return $this->statusChallenge;
|
||||
}
|
||||
|
||||
public function getIsValid() {
|
||||
return (bool)$this->getAnsweredChallenge();
|
||||
}
|
||||
|
@ -83,16 +93,6 @@ final class PhabricatorAuthFactorResult
|
|||
return $this->value;
|
||||
}
|
||||
|
||||
public function setIssuedChallenges(array $issued_challenges) {
|
||||
assert_instances_of($issued_challenges, 'PhabricatorAuthChallenge');
|
||||
$this->issuedChallenges = $issued_challenges;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIssuedChallenges() {
|
||||
return $this->issuedChallenges;
|
||||
}
|
||||
|
||||
public function setIcon(PHUIIconView $icon) {
|
||||
$this->icon = $icon;
|
||||
return $this;
|
||||
|
|
|
@ -585,7 +585,7 @@ final class PhabricatorDuoAuthFactor
|
|||
$result = $this->newDuoFuture($provider)
|
||||
->setHTTPMethod('GET')
|
||||
->setMethod('auth_status', $parameters)
|
||||
->setTimeout(5)
|
||||
->setTimeout(3)
|
||||
->resolve();
|
||||
|
||||
$state = $result['response']['result'];
|
||||
|
@ -661,15 +661,6 @@ final class PhabricatorDuoAuthFactor
|
|||
PhabricatorAuthFactorResult $result) {
|
||||
|
||||
$control = $this->newAutomaticControl($result);
|
||||
if (!$control) {
|
||||
$result = $this->newResult()
|
||||
->setIsContinue(true)
|
||||
->setErrorMessage(
|
||||
pht(
|
||||
'A challenge has been sent to your phone. Open the Duo '.
|
||||
'application and confirm the challenge, then continue.'));
|
||||
$control = $this->newAutomaticControl($result);
|
||||
}
|
||||
|
||||
$control
|
||||
->setLabel(pht('Duo'))
|
||||
|
@ -689,7 +680,27 @@ final class PhabricatorDuoAuthFactor
|
|||
PhabricatorUser $viewer,
|
||||
AphrontRequest $request,
|
||||
array $challenges) {
|
||||
return $this->newResult();
|
||||
|
||||
$result = $this->newResult()
|
||||
->setIsContinue(true)
|
||||
->setErrorMessage(
|
||||
pht(
|
||||
'A challenge has been sent to your phone. Open the Duo '.
|
||||
'application and confirm the challenge, then continue.'));
|
||||
|
||||
$challenge = $this->getChallengeForCurrentContext(
|
||||
$config,
|
||||
$viewer,
|
||||
$challenges);
|
||||
if ($challenge) {
|
||||
$result
|
||||
->setStatusChallenge($challenge)
|
||||
->setIcon(
|
||||
id(new PHUIIconView())
|
||||
->setIcon('fa-refresh', 'green ph-spin'));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function newDuoFuture(PhabricatorAuthFactorProvider $provider) {
|
||||
|
@ -790,4 +801,54 @@ final class PhabricatorDuoAuthFactor
|
|||
$hostname));
|
||||
}
|
||||
|
||||
public function newChallengeStatusView(
|
||||
PhabricatorAuthFactorConfig $config,
|
||||
PhabricatorAuthFactorProvider $provider,
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorAuthChallenge $challenge) {
|
||||
|
||||
$duo_xaction = $challenge->getChallengeKey();
|
||||
|
||||
$parameters = array(
|
||||
'txid' => $duo_xaction,
|
||||
);
|
||||
|
||||
$default_result = id(new PhabricatorAuthChallengeUpdate())
|
||||
->setRetry(true);
|
||||
|
||||
try {
|
||||
$result = $this->newDuoFuture($provider)
|
||||
->setHTTPMethod('GET')
|
||||
->setMethod('auth_status', $parameters)
|
||||
->setTimeout(5)
|
||||
->resolve();
|
||||
|
||||
$state = $result['response']['result'];
|
||||
} catch (HTTPFutureCURLResponseStatus $exception) {
|
||||
// If we failed or timed out, retry. Usually, this is a timeout.
|
||||
return id(new PhabricatorAuthChallengeUpdate())
|
||||
->setRetry(true);
|
||||
}
|
||||
|
||||
// For now, don't update the view for anything but an "Allow". Updates
|
||||
// here are just about providing more visual feedback for user convenience.
|
||||
if ($state !== 'allow') {
|
||||
return id(new PhabricatorAuthChallengeUpdate())
|
||||
->setRetry(false);
|
||||
}
|
||||
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIcon('fa-check-circle-o', 'green');
|
||||
|
||||
$view = id(new PHUIFormTimerControl())
|
||||
->setIcon($icon)
|
||||
->appendChild(pht('You responded to this challenge correctly.'))
|
||||
->newTimerView();
|
||||
|
||||
return id(new PhabricatorAuthChallengeUpdate())
|
||||
->setState('allow')
|
||||
->setRetry(false)
|
||||
->setMarkup($view);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -80,11 +80,6 @@ final class PhabricatorDuoFuture
|
|||
$host = $this->apiHostname;
|
||||
$host = phutil_utf8_strtolower($host);
|
||||
|
||||
$uri = id(new PhutilURI(''))
|
||||
->setProtocol('https')
|
||||
->setDomain($host)
|
||||
->setPath($path);
|
||||
|
||||
$data = $this->parameters;
|
||||
$date = date('r');
|
||||
|
||||
|
@ -109,11 +104,19 @@ final class PhabricatorDuoFuture
|
|||
$signature = new PhutilOpaqueEnvelope($signature);
|
||||
|
||||
if ($http_method === 'GET') {
|
||||
$uri->setQueryParams($data);
|
||||
$data = array();
|
||||
$uri_data = $data;
|
||||
$body_data = array();
|
||||
} else {
|
||||
$uri_data = array();
|
||||
$body_data = $data;
|
||||
}
|
||||
|
||||
$future = id(new HTTPSFuture($uri, $data))
|
||||
$uri = id(new PhutilURI('', $uri_data))
|
||||
->setProtocol('https')
|
||||
->setDomain($host)
|
||||
->setPath($path);
|
||||
|
||||
$future = id(new HTTPSFuture($uri, $body_data))
|
||||
->setHTTPBasicAuthCredentials($this->integrationKey, $signature)
|
||||
->setMethod($http_method)
|
||||
->addHeader('Accept', 'application/json')
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorAuthLinkMessageType
|
||||
extends PhabricatorAuthMessageType {
|
||||
|
||||
const MESSAGEKEY = 'auth.link';
|
||||
|
||||
public function getDisplayName() {
|
||||
return pht('Unlinked Account Instructions');
|
||||
}
|
||||
|
||||
public function getShortDescription() {
|
||||
return pht(
|
||||
'Guidance shown after a user logs in with an email link and is '.
|
||||
'prompted to link an external account.');
|
||||
}
|
||||
|
||||
}
|
|
@ -161,7 +161,7 @@ abstract class PhabricatorAuthProvider extends Phobject {
|
|||
abstract public function processLoginRequest(
|
||||
PhabricatorAuthLoginController $controller);
|
||||
|
||||
public function buildLinkForm(PhabricatorAuthLinkController $controller) {
|
||||
public function buildLinkForm($controller) {
|
||||
return $this->renderLoginForm($controller->getRequest(), $mode = 'link');
|
||||
}
|
||||
|
||||
|
@ -220,9 +220,7 @@ abstract class PhabricatorAuthProvider extends Phobject {
|
|||
$adapter->getAdapterDomain(),
|
||||
$account_id);
|
||||
if (!$account) {
|
||||
$account = id(new PhabricatorExternalAccount())
|
||||
->setAccountType($adapter->getAdapterType())
|
||||
->setAccountDomain($adapter->getAdapterDomain())
|
||||
$account = $this->newExternalAccount()
|
||||
->setAccountID($account_id);
|
||||
}
|
||||
|
||||
|
@ -299,8 +297,18 @@ abstract class PhabricatorAuthProvider extends Phobject {
|
|||
return false;
|
||||
}
|
||||
|
||||
public function getDefaultExternalAccount() {
|
||||
throw new PhutilMethodNotImplementedException();
|
||||
public function newDefaultExternalAccount() {
|
||||
return $this->newExternalAccount();
|
||||
}
|
||||
|
||||
protected function newExternalAccount() {
|
||||
$config = $this->getProviderConfig();
|
||||
$adapter = $this->getAdapter();
|
||||
|
||||
return id(new PhabricatorExternalAccount())
|
||||
->setAccountType($adapter->getAdapterType())
|
||||
->setAccountDomain($adapter->getAdapterDomain())
|
||||
->setProviderConfigPHID($config->getPHID());
|
||||
}
|
||||
|
||||
public function getLoginOrder() {
|
||||
|
@ -438,12 +446,13 @@ abstract class PhabricatorAuthProvider extends Phobject {
|
|||
|
||||
$uri = $attributes['uri'];
|
||||
$uri = new PhutilURI($uri);
|
||||
$params = $uri->getQueryParams();
|
||||
$uri->setQueryParams(array());
|
||||
$params = $uri->getQueryParamsAsPairList();
|
||||
$uri->removeAllQueryParams();
|
||||
|
||||
$content = array($button);
|
||||
|
||||
foreach ($params as $key => $value) {
|
||||
foreach ($params as $pair) {
|
||||
list($key, $value) = $pair;
|
||||
$content[] = phutil_tag(
|
||||
'input',
|
||||
array(
|
||||
|
|
|
@ -159,8 +159,7 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider {
|
|||
return $dialog;
|
||||
}
|
||||
|
||||
public function buildLinkForm(
|
||||
PhabricatorAuthLinkController $controller) {
|
||||
public function buildLinkForm($controller) {
|
||||
throw new Exception(pht("Password providers can't be linked."));
|
||||
}
|
||||
|
||||
|
@ -359,14 +358,6 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider {
|
|||
return true;
|
||||
}
|
||||
|
||||
public function getDefaultExternalAccount() {
|
||||
$adapter = $this->getAdapter();
|
||||
|
||||
return id(new PhabricatorExternalAccount())
|
||||
->setAccountType($adapter->getAdapterType())
|
||||
->setAccountDomain($adapter->getAdapterDomain());
|
||||
}
|
||||
|
||||
protected function willSaveAccount(PhabricatorExternalAccount $account) {
|
||||
parent::willSaveAccount($account);
|
||||
$account->setUserPHID($account->getAccountID());
|
||||
|
|
|
@ -21,6 +21,7 @@ final class PhabricatorExternalAccountQuery
|
|||
private $userPHIDs;
|
||||
private $needImages;
|
||||
private $accountSecrets;
|
||||
private $providerConfigPHIDs;
|
||||
|
||||
public function withUserPHIDs(array $user_phids) {
|
||||
$this->userPHIDs = $user_phids;
|
||||
|
@ -62,6 +63,11 @@ final class PhabricatorExternalAccountQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withProviderConfigPHIDs(array $phids) {
|
||||
$this->providerConfigPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new PhabricatorExternalAccount();
|
||||
}
|
||||
|
@ -71,6 +77,26 @@ final class PhabricatorExternalAccountQuery
|
|||
}
|
||||
|
||||
protected function willFilterPage(array $accounts) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$configs = id(new PhabricatorAuthProviderConfigQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(mpull($accounts, 'getProviderConfigPHID'))
|
||||
->execute();
|
||||
$configs = mpull($configs, null, 'getPHID');
|
||||
|
||||
foreach ($accounts as $key => $account) {
|
||||
$config_phid = $account->getProviderConfigPHID();
|
||||
$config = idx($configs, $config_phid);
|
||||
|
||||
if (!$config) {
|
||||
unset($accounts[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$account->attachProviderConfig($config);
|
||||
}
|
||||
|
||||
if ($this->needImages) {
|
||||
$file_phids = mpull($accounts, 'getProfileImagePHID');
|
||||
$file_phids = array_filter($file_phids);
|
||||
|
@ -161,6 +187,13 @@ final class PhabricatorExternalAccountQuery
|
|||
$this->accountSecrets);
|
||||
}
|
||||
|
||||
if ($this->providerConfigPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'providerConfigPHID IN (%Ls)',
|
||||
$this->providerConfigPHIDs);
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorAuthChallengeUpdate
|
||||
extends Phobject {
|
||||
|
||||
private $retry = false;
|
||||
private $state;
|
||||
private $markup;
|
||||
|
||||
public function setRetry($retry) {
|
||||
$this->retry = $retry;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRetry() {
|
||||
return $this->retry;
|
||||
}
|
||||
|
||||
public function setState($state) {
|
||||
$this->state = $state;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getState() {
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
public function setMarkup($markup) {
|
||||
$this->markup = $markup;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMarkup() {
|
||||
return $this->markup;
|
||||
}
|
||||
|
||||
public function newContent() {
|
||||
return array(
|
||||
'retry' => $this->getRetry(),
|
||||
'state' => $this->getState(),
|
||||
'markup' => $this->getMarkup(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -234,7 +234,7 @@ final class PhabricatorCalendarImportViewController
|
|||
|
||||
$all_uri = $this->getApplicationURI('import/log/');
|
||||
$all_uri = (string)id(new PhutilURI($all_uri))
|
||||
->setQueryParam('importSourcePHID', $import->getPHID());
|
||||
->replaceQueryParam('importSourcePHID', $import->getPHID());
|
||||
|
||||
$all_button = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
|
@ -273,8 +273,8 @@ final class PhabricatorCalendarImportViewController
|
|||
|
||||
$all_uri = $this->getApplicationURI();
|
||||
$all_uri = (string)id(new PhutilURI($all_uri))
|
||||
->setQueryParam('importSourcePHID', $import->getPHID())
|
||||
->setQueryParam('display', 'list');
|
||||
->replaceQueryParam('importSourcePHID', $import->getPHID())
|
||||
->replaceQueryParam('display', 'list');
|
||||
|
||||
$all_button = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
|
|
|
@ -11,8 +11,7 @@ final class PhabricatorChatLogChannelLogController
|
|||
$viewer = $request->getViewer();
|
||||
$id = $request->getURIData('channelID');
|
||||
|
||||
$uri = clone $request->getRequestURI();
|
||||
$uri->setQueryParams(array());
|
||||
$uri = new PhutilURI($request->getPath());
|
||||
|
||||
$pager = new AphrontCursorPagerView();
|
||||
$pager->setURI($uri);
|
||||
|
|
|
@ -15,6 +15,9 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
|
|||
|
||||
$defined_keys = PhabricatorApplicationConfigOptions::loadAllOptions();
|
||||
|
||||
$stack = PhabricatorEnv::getConfigSourceStack();
|
||||
$stack = $stack->getStack();
|
||||
|
||||
foreach ($all_keys as $key) {
|
||||
if (isset($defined_keys[$key])) {
|
||||
continue;
|
||||
|
@ -48,9 +51,6 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
|
|||
->setName($name)
|
||||
->setSummary($summary);
|
||||
|
||||
$stack = PhabricatorEnv::getConfigSourceStack();
|
||||
$stack = $stack->getStack();
|
||||
|
||||
$found = array();
|
||||
$found_local = false;
|
||||
$found_database = false;
|
||||
|
@ -85,6 +85,101 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
|
|||
}
|
||||
}
|
||||
|
||||
$options = PhabricatorApplicationConfigOptions::loadAllOptions();
|
||||
foreach ($defined_keys as $key => $value) {
|
||||
$option = idx($options, $key);
|
||||
if (!$option) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$option->getLocked()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$found_database = false;
|
||||
foreach ($stack as $source_key => $source) {
|
||||
$value = $source->getKeys(array($key));
|
||||
if ($value) {
|
||||
if ($source instanceof PhabricatorConfigDatabaseSource) {
|
||||
$found_database = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$found_database) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE: These are values which we don't let you edit directly, but edit
|
||||
// via other UI workflows. For now, don't raise this warning about them.
|
||||
// In the future, before we stop reading database configuration for
|
||||
// locked values, we either need to add a flag which lets these values
|
||||
// continue reading from the database or move them to some other storage
|
||||
// mechanism.
|
||||
$soft_locks = array(
|
||||
'phabricator.uninstalled-applications',
|
||||
'phabricator.application-settings',
|
||||
'config.ignore-issues',
|
||||
);
|
||||
$soft_locks = array_fuse($soft_locks);
|
||||
if (isset($soft_locks[$key])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$doc_name = 'Configuration Guide: Locked and Hidden Configuration';
|
||||
$doc_href = PhabricatorEnv::getDoclink($doc_name);
|
||||
|
||||
$set_command = phutil_tag(
|
||||
'tt',
|
||||
array(),
|
||||
csprintf(
|
||||
'bin/config set %R <value>',
|
||||
$key));
|
||||
|
||||
$summary = pht(
|
||||
'Configuration value "%s" is locked, but has a value in the database.',
|
||||
$key);
|
||||
$message = pht(
|
||||
'The configuration value "%s" is locked (so it can not be edited '.
|
||||
'from the web UI), but has a database value. Usually, this means '.
|
||||
'that it was previously not locked, you set it using the web UI, '.
|
||||
'and it later became locked.'.
|
||||
"\n\n".
|
||||
'You should copy this configuration value in a local configuration '.
|
||||
'source (usually by using %s) and then remove it from the database '.
|
||||
'with the command below.'.
|
||||
"\n\n".
|
||||
'For more information on locked and hidden configuration, including '.
|
||||
'details about this setup issue, see %s.'.
|
||||
"\n\n".
|
||||
'This database value is currently respected, but a future version '.
|
||||
'of Phabricator will stop respecting database values for locked '.
|
||||
'configuration options.',
|
||||
$key,
|
||||
$set_command,
|
||||
phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $doc_href,
|
||||
'target' => '_blank',
|
||||
),
|
||||
$doc_name));
|
||||
$command = csprintf(
|
||||
'phabricator/ $ ./bin/config delete --database %R',
|
||||
$key);
|
||||
|
||||
$this->newIssue('config.locked.'.$key)
|
||||
->setShortName(pht('Deprecated Config Source'))
|
||||
->setName(
|
||||
pht(
|
||||
'Locked Configuration Option "%s" Has Database Value',
|
||||
$key))
|
||||
->setSummary($summary)
|
||||
->setMessage($message)
|
||||
->addCommand($command)
|
||||
->addPhabricatorConfig($key);
|
||||
}
|
||||
|
||||
if (PhabricatorEnv::getEnvConfig('feed.http-hooks')) {
|
||||
$this->newIssue('config.deprecated.feed.http-hooks')
|
||||
|
|
|
@ -40,7 +40,7 @@ final class PhabricatorWebServerSetupCheck extends PhabricatorSetupCheck {
|
|||
|
||||
$base_uri = id(new PhutilURI($base_uri))
|
||||
->setPath($send_path)
|
||||
->setQueryParam($expect_key, $expect_value);
|
||||
->replaceQueryParam($expect_key, $expect_value);
|
||||
|
||||
$self_future = id(new HTTPSFuture($base_uri))
|
||||
->addHeader('X-Phabricator-SelfCheck', 1)
|
||||
|
|
|
@ -41,7 +41,12 @@ final class PhabricatorPHDConfigOptions
|
|||
"If you are running a cluster, this limit applies separately ".
|
||||
"to each instance of `phd`. For example, if this limit is set ".
|
||||
"to `4` and you have three hosts running daemons, the effective ".
|
||||
"global limit will be 12.")),
|
||||
"global limit will be 12.".
|
||||
"\n\n".
|
||||
"After changing this value, you must restart the daemons. Most ".
|
||||
"configuration changes are picked up by the daemons ".
|
||||
"automatically, but pool sizes can not be changed without a ".
|
||||
"restart.")),
|
||||
$this->newOption('phd.verbose', 'bool', false)
|
||||
->setLocked(true)
|
||||
->setBoolOptions(
|
||||
|
|
|
@ -188,7 +188,7 @@ final class ConpherenceViewController extends
|
|||
} else {
|
||||
// user not logged in so give them a login button.
|
||||
$login_href = id(new PhutilURI('/auth/start/'))
|
||||
->setQueryParam('next', '/'.$conpherence->getMonogram());
|
||||
->replaceQueryParam('next', '/'.$conpherence->getMonogram());
|
||||
return id(new PHUIFormLayoutView())
|
||||
->addClass('login-to-participate')
|
||||
->appendInstructions(pht('Log in to join this room and participate.'))
|
||||
|
|
|
@ -287,7 +287,7 @@ final class PhabricatorDashboardPanelRenderingEngine extends Phobject {
|
|||
$edit_uri = "/dashboard/panel/edit/{$panel_id}/";
|
||||
$edit_uri = new PhutilURI($edit_uri);
|
||||
if ($dashboard_id) {
|
||||
$edit_uri->setQueryParam('dashboardID', $dashboard_id);
|
||||
$edit_uri->replaceQueryParam('dashboardID', $dashboard_id);
|
||||
}
|
||||
|
||||
$action_edit = id(new PHUIIconView())
|
||||
|
@ -303,7 +303,7 @@ final class PhabricatorDashboardPanelRenderingEngine extends Phobject {
|
|||
|
||||
$remove_uri = "/dashboard/removepanel/{$dashboard_id}/";
|
||||
$remove_uri = id(new PhutilURI($remove_uri))
|
||||
->setQueryParam('panelPHID', $panel_phid);
|
||||
->replaceQueryParam('panelPHID', $panel_phid);
|
||||
|
||||
$action_remove = id(new PHUIIconView())
|
||||
->setIcon('fa-trash-o')
|
||||
|
|
|
@ -113,11 +113,11 @@ final class PhabricatorDashboardRenderingEngine extends Phobject {
|
|||
$dashboard_id = $this->dashboard->getID();
|
||||
|
||||
$create_uri = id(new PhutilURI('/dashboard/panel/create/'))
|
||||
->setQueryParam('dashboardID', $dashboard_id)
|
||||
->setQueryParam('column', $column);
|
||||
->replaceQueryParam('dashboardID', $dashboard_id)
|
||||
->replaceQueryParam('column', $column);
|
||||
|
||||
$add_uri = id(new PhutilURI('/dashboard/addpanel/'.$dashboard_id.'/'))
|
||||
->setQueryParam('column', $column);
|
||||
->replaceQueryParam('column', $column);
|
||||
|
||||
$create_button = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
|
|
|
@ -71,7 +71,7 @@ final class DifferentialDiffCreateController extends DifferentialController {
|
|||
$uri = $this->getApplicationURI("diff/{$diff_id}/");
|
||||
$uri = new PhutilURI($uri);
|
||||
if ($revision) {
|
||||
$uri->setQueryParam('revisionID', $revision->getID());
|
||||
$uri->replaceQueryParam('revisionID', $revision->getID());
|
||||
}
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI($uri);
|
||||
|
|
|
@ -1098,7 +1098,8 @@ final class DifferentialRevisionViewController
|
|||
// D123.vs123.id123.whitespaceignore-all.diff
|
||||
// lame but nice to include these options
|
||||
$file_name = ltrim($request_uri->getPath(), '/').'.';
|
||||
foreach ($request_uri->getQueryParams() as $key => $value) {
|
||||
foreach ($request_uri->getQueryParamsAsPairList() as $pair) {
|
||||
list($key, $value) = $pair;
|
||||
if ($key == 'download') {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,11 @@ final class DifferentialBuildableEngine
|
|||
$object = $this->getObject();
|
||||
|
||||
if ($object instanceof DifferentialDiff) {
|
||||
return $object->getRevision();
|
||||
if ($object->getRevisionID()) {
|
||||
return $object->getRevision();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return $object;
|
||||
|
|
|
@ -358,7 +358,7 @@ final class DifferentialChangesetListView extends AphrontView {
|
|||
|
||||
if ($this->standaloneURI) {
|
||||
$uri = new PhutilURI($this->standaloneURI);
|
||||
$uri->setQueryParams($uri->getQueryParams() + $qparams);
|
||||
$uri = $this->appendDefaultQueryParams($uri, $qparams);
|
||||
$meta['standaloneURI'] = (string)$uri;
|
||||
}
|
||||
|
||||
|
@ -381,7 +381,7 @@ final class DifferentialChangesetListView extends AphrontView {
|
|||
if ($this->leftRawFileURI) {
|
||||
if ($change != DifferentialChangeType::TYPE_ADD) {
|
||||
$uri = new PhutilURI($this->leftRawFileURI);
|
||||
$uri->setQueryParams($uri->getQueryParams() + $qparams);
|
||||
$uri = $this->appendDefaultQueryParams($uri, $qparams);
|
||||
$meta['leftURI'] = (string)$uri;
|
||||
}
|
||||
}
|
||||
|
@ -390,7 +390,7 @@ final class DifferentialChangesetListView extends AphrontView {
|
|||
if ($change != DifferentialChangeType::TYPE_DELETE &&
|
||||
$change != DifferentialChangeType::TYPE_MULTICOPY) {
|
||||
$uri = new PhutilURI($this->rightRawFileURI);
|
||||
$uri->setQueryParams($uri->getQueryParams() + $qparams);
|
||||
$uri = $this->appendDefaultQueryParams($uri, $qparams);
|
||||
$meta['rightURI'] = (string)$uri;
|
||||
}
|
||||
}
|
||||
|
@ -421,4 +421,23 @@ final class DifferentialChangesetListView extends AphrontView {
|
|||
|
||||
}
|
||||
|
||||
private function appendDefaultQueryParams(PhutilURI $uri, array $params) {
|
||||
// Add these default query parameters to the query string if they do not
|
||||
// already exist.
|
||||
|
||||
$have = array();
|
||||
foreach ($uri->getQueryParamsAsPairList() as $pair) {
|
||||
list($key, $value) = $pair;
|
||||
$have[$key] = true;
|
||||
}
|
||||
|
||||
foreach ($params as $key => $value) {
|
||||
if (!isset($have[$key])) {
|
||||
$uri->appendQueryParam($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -709,8 +709,6 @@ final class DiffusionBrowseController extends DiffusionController {
|
|||
'path' => $path,
|
||||
));
|
||||
|
||||
$before_uri->setQueryParams($request->getRequestURI()->getQueryParams());
|
||||
$before_uri = $before_uri->alter('before', null);
|
||||
$before_uri = $before_uri->alter('renamed', $renamed);
|
||||
$before_uri = $before_uri->alter('follow', $follow);
|
||||
|
||||
|
|
|
@ -81,12 +81,12 @@ abstract class DiffusionView extends AphrontView {
|
|||
}
|
||||
|
||||
if (isset($details['external'])) {
|
||||
$href = id(new PhutilURI('/diffusion/external/'))
|
||||
->setQueryParams(
|
||||
array(
|
||||
'uri' => idx($details, 'external'),
|
||||
'id' => idx($details, 'hash'),
|
||||
));
|
||||
$params = array(
|
||||
'uri' => idx($details, 'external'),
|
||||
'id' => idx($details, 'hash'),
|
||||
);
|
||||
|
||||
$href = new PhutilURI('/diffusion/external/', $params);
|
||||
$tip = pht('Browse External');
|
||||
} else {
|
||||
$href = $this->getDiffusionRequest()->generateURI(
|
||||
|
|
|
@ -111,15 +111,15 @@ final class DivinerSymbolRemarkupRule extends PhutilRemarkupRule {
|
|||
// Here, we're generating comment text or something like that. Just
|
||||
// link to Diviner and let it sort things out.
|
||||
|
||||
$href = id(new PhutilURI('/diviner/find/'))
|
||||
->setQueryParams(
|
||||
array(
|
||||
'book' => $ref->getBook(),
|
||||
'name' => $ref->getName(),
|
||||
'type' => $ref->getType(),
|
||||
'context' => $ref->getContext(),
|
||||
'jump' => true,
|
||||
));
|
||||
$params = array(
|
||||
'book' => $ref->getBook(),
|
||||
'name' => $ref->getName(),
|
||||
'type' => $ref->getType(),
|
||||
'context' => $ref->getContext(),
|
||||
'jump' => true,
|
||||
);
|
||||
|
||||
$href = new PhutilURI('/diviner/find/', $params);
|
||||
}
|
||||
|
||||
// TODO: This probably is not the best place to do this. Move it somewhere
|
||||
|
|
|
@ -11,7 +11,7 @@ final class PhabricatorFactHomeController extends PhabricatorFactController {
|
|||
|
||||
if ($request->isFormPost()) {
|
||||
$uri = new PhutilURI('/fact/chart/');
|
||||
$uri->setQueryParam('y1', $request->getStr('y1'));
|
||||
$uri->replaceQueryParam('y1', $request->getStr('y1'));
|
||||
return id(new AphrontRedirectResponse())->setURI($uri);
|
||||
}
|
||||
|
||||
|
|
|
@ -135,7 +135,7 @@ final class PhabricatorFileDataController extends PhabricatorFileController {
|
|||
$request_uri = id(clone $request->getAbsoluteRequestURI())
|
||||
->setPath(null)
|
||||
->setFragment(null)
|
||||
->setQueryParams(array());
|
||||
->removeAllQueryParams();
|
||||
|
||||
$response->addContentSecurityPolicyURI(
|
||||
'object-src',
|
||||
|
|
|
@ -70,7 +70,7 @@ final class PhabricatorFileLightboxController
|
|||
|
||||
if (!$viewer->isLoggedIn()) {
|
||||
$login_href = id(new PhutilURI('/auth/start/'))
|
||||
->setQueryParam('next', '/'.$file->getMonogram());
|
||||
->replaceQueryParam('next', '/'.$file->getMonogram());
|
||||
return id(new PHUIFormLayoutView())
|
||||
->addClass('phui-comment-panel-empty')
|
||||
->appendChild(
|
||||
|
|
|
@ -61,7 +61,7 @@ final class PhabricatorFileTransformListController
|
|||
|
||||
$view_href = $file->getURIForTransform($xform);
|
||||
$view_href = new PhutilURI($view_href);
|
||||
$view_href->setQueryParam('regenerate', 'true');
|
||||
$view_href->replaceQueryParam('regenerate', 'true');
|
||||
|
||||
$view_text = pht('Regenerate');
|
||||
|
||||
|
|
|
@ -149,7 +149,7 @@ final class PhabricatorImageRemarkupRule extends PhutilRemarkupRule {
|
|||
));
|
||||
} else {
|
||||
$src_uri = id(new PhutilURI('/file/imageproxy/'))
|
||||
->setQueryParam('uri', $uri);
|
||||
->replaceQueryParam('uri', $uri);
|
||||
|
||||
$img = id(new PHUIRemarkupImageView())
|
||||
->setURI($src_uri)
|
||||
|
|
|
@ -81,13 +81,13 @@ final class HeraldNewController extends HeraldController {
|
|||
}
|
||||
|
||||
if (!$errors && $done) {
|
||||
$uri = id(new PhutilURI('edit/'))
|
||||
->setQueryParams(
|
||||
array(
|
||||
'content_type' => $content_type,
|
||||
'rule_type' => $rule_type,
|
||||
'targetPHID' => $target_phid,
|
||||
));
|
||||
$params = array(
|
||||
'content_type' => $content_type,
|
||||
'rule_type' => $rule_type,
|
||||
'targetPHID' => $target_phid,
|
||||
);
|
||||
|
||||
$uri = new PhutilURI('edit/', $params);
|
||||
$uri = $this->getApplicationURI($uri);
|
||||
return id(new AphrontRedirectResponse())->setURI($uri);
|
||||
}
|
||||
|
@ -126,13 +126,13 @@ final class HeraldNewController extends HeraldController {
|
|||
->addHiddenInput('step', 2)
|
||||
->appendChild($rule_types);
|
||||
|
||||
$params = array(
|
||||
'content_type' => $content_type,
|
||||
'step' => '0',
|
||||
);
|
||||
|
||||
$cancel_text = pht('Back');
|
||||
$cancel_uri = id(new PhutilURI('new/'))
|
||||
->setQueryParams(
|
||||
array(
|
||||
'content_type' => $content_type,
|
||||
'step' => 0,
|
||||
));
|
||||
$cancel_uri = new PhutilURI('new/', $params);
|
||||
$cancel_uri = $this->getApplicationURI($cancel_uri);
|
||||
$title = pht('Create Herald Rule: %s',
|
||||
idx($content_type_map, $content_type));
|
||||
|
@ -173,14 +173,14 @@ final class HeraldNewController extends HeraldController {
|
|||
->setValue($request->getStr('objectName'))
|
||||
->setLabel(pht('Object')));
|
||||
|
||||
$params = array(
|
||||
'content_type' => $content_type,
|
||||
'rule_type' => $rule_type,
|
||||
'step' => 1,
|
||||
);
|
||||
|
||||
$cancel_text = pht('Back');
|
||||
$cancel_uri = id(new PhutilURI('new/'))
|
||||
->setQueryParams(
|
||||
array(
|
||||
'content_type' => $content_type,
|
||||
'rule_type' => $rule_type,
|
||||
'step' => 1,
|
||||
));
|
||||
$cancel_uri = new PhutilURI('new/', $params);
|
||||
$cancel_uri = $this->getApplicationURI($cancel_uri);
|
||||
$title = pht('Create Herald Rule: %s',
|
||||
idx($content_type_map, $content_type));
|
||||
|
|
|
@ -120,7 +120,7 @@ final class HeraldWebhookRequest
|
|||
public function getErrorTypeForDisplay() {
|
||||
$map = array(
|
||||
self::ERRORTYPE_HOOK => pht('Hook Error'),
|
||||
self::ERRORTYPE_HTTP => pht('HTTP Error'),
|
||||
self::ERRORTYPE_HTTP => pht('HTTP Status Code'),
|
||||
self::ERRORTYPE_TIMEOUT => pht('Request Timeout'),
|
||||
);
|
||||
|
||||
|
|
|
@ -47,10 +47,13 @@ final class PhabricatorMemeEngine extends Phobject {
|
|||
}
|
||||
|
||||
public function getGenerateURI() {
|
||||
return id(new PhutilURI('/macro/meme/'))
|
||||
->alter('macro', $this->getTemplate())
|
||||
->alter('above', $this->getAboveText())
|
||||
->alter('below', $this->getBelowText());
|
||||
$params = array(
|
||||
'macro' => $this->getTemplate(),
|
||||
'above' => $this->getAboveText(),
|
||||
'below' => $this->getBelowText(),
|
||||
);
|
||||
|
||||
return new PhutilURI('/macro/meme/', $params);
|
||||
}
|
||||
|
||||
public function newAsset() {
|
||||
|
|
|
@ -55,6 +55,7 @@ final class PhabricatorManiphestApplication extends PhabricatorApplication {
|
|||
'subtask/(?P<id>[1-9]\d*)/' => 'ManiphestTaskSubtaskController',
|
||||
),
|
||||
'subpriority/' => 'ManiphestSubpriorityController',
|
||||
'graph/(?P<id>[1-9]\d*)/' => 'ManiphestTaskGraphController',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -61,4 +61,102 @@ abstract class ManiphestController extends PhabricatorController {
|
|||
return $view;
|
||||
}
|
||||
|
||||
final protected function newTaskGraphDropdownMenu(
|
||||
ManiphestTask $task,
|
||||
$has_parents,
|
||||
$has_subtasks,
|
||||
$include_standalone) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$parents_uri = urisprintf(
|
||||
'/?subtaskIDs=%d#R',
|
||||
$task->getID());
|
||||
$parents_uri = $this->getApplicationURI($parents_uri);
|
||||
|
||||
$subtasks_uri = urisprintf(
|
||||
'/?parentIDs=%d#R',
|
||||
$task->getID());
|
||||
$subtasks_uri = $this->getApplicationURI($subtasks_uri);
|
||||
|
||||
$dropdown_menu = id(new PhabricatorActionListView())
|
||||
->setViewer($viewer)
|
||||
->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setHref($parents_uri)
|
||||
->setName(pht('Search Parent Tasks'))
|
||||
->setDisabled(!$has_parents)
|
||||
->setIcon('fa-chevron-circle-up'))
|
||||
->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setHref($subtasks_uri)
|
||||
->setName(pht('Search Subtasks'))
|
||||
->setDisabled(!$has_subtasks)
|
||||
->setIcon('fa-chevron-circle-down'));
|
||||
|
||||
if ($include_standalone) {
|
||||
$standalone_uri = urisprintf('/graph/%d/', $task->getID());
|
||||
$standalone_uri = $this->getApplicationURI($standalone_uri);
|
||||
|
||||
$dropdown_menu->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setHref($standalone_uri)
|
||||
->setName(pht('View Standalone Graph'))
|
||||
->setIcon('fa-code-fork'));
|
||||
}
|
||||
|
||||
$graph_menu = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setIcon('fa-search')
|
||||
->setText(pht('Search...'))
|
||||
->setDropdownMenu($dropdown_menu);
|
||||
|
||||
return $graph_menu;
|
||||
}
|
||||
|
||||
final protected function newTaskGraphOverflowView(
|
||||
ManiphestTask $task,
|
||||
$overflow_message,
|
||||
$include_standalone) {
|
||||
|
||||
$id = $task->getID();
|
||||
|
||||
if ($include_standalone) {
|
||||
$standalone_uri = $this->getApplicationURI("graph/{$id}/");
|
||||
|
||||
$standalone_link = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setHref($standalone_uri)
|
||||
->setColor(PHUIButtonView::GREY)
|
||||
->setIcon('fa-code-fork')
|
||||
->setText(pht('View Standalone Graph'));
|
||||
} else {
|
||||
$standalone_link = null;
|
||||
}
|
||||
|
||||
$standalone_icon = id(new PHUIIconView())
|
||||
->setIcon('fa-exclamation-triangle', 'yellow')
|
||||
->addClass('object-graph-header-icon');
|
||||
|
||||
$standalone_view = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'object-graph-header',
|
||||
),
|
||||
array(
|
||||
$standalone_link,
|
||||
$standalone_icon,
|
||||
phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'object-graph-header-message',
|
||||
),
|
||||
array(
|
||||
$overflow_message,
|
||||
)),
|
||||
));
|
||||
|
||||
return $standalone_view;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -80,7 +80,8 @@ final class ManiphestTaskDetailController extends ManiphestController {
|
|||
$related_tabs = array();
|
||||
$graph_menu = null;
|
||||
|
||||
$graph_limit = 100;
|
||||
$graph_limit = 200;
|
||||
$overflow_message = null;
|
||||
$task_graph = id(new ManiphestTaskGraph())
|
||||
->setViewer($viewer)
|
||||
->setSeedPHID($task->getPHID())
|
||||
|
@ -96,61 +97,55 @@ final class ManiphestTaskDetailController extends ManiphestController {
|
|||
$has_parents = (bool)$parent_list;
|
||||
$has_subtasks = (bool)$subtask_list;
|
||||
|
||||
$search_text = pht('Search...');
|
||||
|
||||
// First, get a count of direct parent tasks and subtasks. If there
|
||||
// are too many of these, we just don't draw anything. You can use
|
||||
// the search button to browse tasks with the search UI instead.
|
||||
$direct_count = count($parent_list) + count($subtask_list);
|
||||
|
||||
if ($direct_count > $graph_limit) {
|
||||
$message = pht(
|
||||
'Task graph too large to display (this task is directly connected '.
|
||||
'to more than %s other tasks). Use %s to explore connected tasks.',
|
||||
$graph_limit,
|
||||
phutil_tag('strong', array(), $search_text));
|
||||
$message = phutil_tag('em', array(), $message);
|
||||
$graph_table = id(new PHUIPropertyListView())
|
||||
->addTextContent($message);
|
||||
$overflow_message = pht(
|
||||
'This task is directly connected to more than %s other tasks. '.
|
||||
'Use %s to browse parents or subtasks, or %s to show more of the '.
|
||||
'graph.',
|
||||
new PhutilNumber($graph_limit),
|
||||
phutil_tag('strong', array(), pht('Search...')),
|
||||
phutil_tag('strong', array(), pht('View Standalone Graph')));
|
||||
|
||||
$graph_table = null;
|
||||
} else {
|
||||
// If there aren't too many direct tasks, but there are too many total
|
||||
// tasks, we'll only render directly connected tasks.
|
||||
if ($task_graph->isOverLimit()) {
|
||||
$task_graph->setRenderOnlyAdjacentNodes(true);
|
||||
|
||||
$overflow_message = pht(
|
||||
'This task is connected to more than %s other tasks. '.
|
||||
'Only direct parents and subtasks are shown here. Use '.
|
||||
'%s to show more of the graph.',
|
||||
new PhutilNumber($graph_limit),
|
||||
phutil_tag('strong', array(), pht('View Standalone Graph')));
|
||||
}
|
||||
|
||||
$graph_table = $task_graph->newGraphTable();
|
||||
}
|
||||
|
||||
$parents_uri = urisprintf(
|
||||
'/?subtaskIDs=%d#R',
|
||||
$task->getID());
|
||||
$parents_uri = $this->getApplicationURI($parents_uri);
|
||||
if ($overflow_message) {
|
||||
$overflow_view = $this->newTaskGraphOverflowView(
|
||||
$task,
|
||||
$overflow_message,
|
||||
true);
|
||||
|
||||
$subtasks_uri = urisprintf(
|
||||
'/?parentIDs=%d#R',
|
||||
$task->getID());
|
||||
$subtasks_uri = $this->getApplicationURI($subtasks_uri);
|
||||
$graph_table = array(
|
||||
$overflow_view,
|
||||
$graph_table,
|
||||
);
|
||||
}
|
||||
|
||||
$dropdown_menu = id(new PhabricatorActionListView())
|
||||
->setViewer($viewer)
|
||||
->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setHref($parents_uri)
|
||||
->setName(pht('Search Parent Tasks'))
|
||||
->setDisabled(!$has_parents)
|
||||
->setIcon('fa-chevron-circle-up'))
|
||||
->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setHref($subtasks_uri)
|
||||
->setName(pht('Search Subtasks'))
|
||||
->setDisabled(!$has_subtasks)
|
||||
->setIcon('fa-chevron-circle-down'));
|
||||
|
||||
$graph_menu = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setIcon('fa-search')
|
||||
->setText($search_text)
|
||||
->setDropdownMenu($dropdown_menu);
|
||||
$graph_menu = $this->newTaskGraphDropdownMenu(
|
||||
$task,
|
||||
$has_parents,
|
||||
$has_subtasks,
|
||||
true);
|
||||
|
||||
$related_tabs[] = id(new PHUITabView())
|
||||
->setName(pht('Task Graph'))
|
||||
|
@ -300,9 +295,9 @@ final class ManiphestTaskDetailController extends ManiphestController {
|
|||
$subtask_form = head($subtask_options);
|
||||
$form_key = $subtask_form->getIdentifier();
|
||||
$subtask_uri = id(new PhutilURI("/task/edit/form/{$form_key}/"))
|
||||
->setQueryParam('parent', $id)
|
||||
->setQueryParam('template', $id)
|
||||
->setQueryParam('status', ManiphestTaskStatus::getDefaultStatus());
|
||||
->replaceQueryParam('parent', $id)
|
||||
->replaceQueryParam('template', $id)
|
||||
->replaceQueryParam('status', ManiphestTaskStatus::getDefaultStatus());
|
||||
$subtask_workflow = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
final class ManiphestTaskGraphController
|
||||
extends ManiphestController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
$id = $request->getURIData('id');
|
||||
|
||||
$task = id(new ManiphestTaskQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
if (!$task) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs()
|
||||
->addTextCrumb($task->getMonogram(), $task->getURI())
|
||||
->addTextCrumb(pht('Graph'))
|
||||
->setBorder(true);
|
||||
|
||||
$graph_limit = 2000;
|
||||
$overflow_message = null;
|
||||
$task_graph = id(new ManiphestTaskGraph())
|
||||
->setViewer($viewer)
|
||||
->setSeedPHID($task->getPHID())
|
||||
->setLimit($graph_limit)
|
||||
->loadGraph();
|
||||
if (!$task_graph->isEmpty()) {
|
||||
$parent_type = ManiphestTaskDependedOnByTaskEdgeType::EDGECONST;
|
||||
$subtask_type = ManiphestTaskDependsOnTaskEdgeType::EDGECONST;
|
||||
$parent_map = $task_graph->getEdges($parent_type);
|
||||
$subtask_map = $task_graph->getEdges($subtask_type);
|
||||
$parent_list = idx($parent_map, $task->getPHID(), array());
|
||||
$subtask_list = idx($subtask_map, $task->getPHID(), array());
|
||||
$has_parents = (bool)$parent_list;
|
||||
$has_subtasks = (bool)$subtask_list;
|
||||
|
||||
// First, get a count of direct parent tasks and subtasks. If there
|
||||
// are too many of these, we just don't draw anything. You can use
|
||||
// the search button to browse tasks with the search UI instead.
|
||||
$direct_count = count($parent_list) + count($subtask_list);
|
||||
|
||||
if ($direct_count > $graph_limit) {
|
||||
$overflow_message = pht(
|
||||
'This task is directly connected to more than %s other tasks, '.
|
||||
'which is too many tasks to display. Use %s to browse parents '.
|
||||
'or subtasks.',
|
||||
new PhutilNumber($graph_limit),
|
||||
phutil_tag('strong', array(), pht('Search...')));
|
||||
|
||||
$graph_table = null;
|
||||
} else {
|
||||
// If there aren't too many direct tasks, but there are too many total
|
||||
// tasks, we'll only render directly connected tasks.
|
||||
if ($task_graph->isOverLimit()) {
|
||||
$task_graph->setRenderOnlyAdjacentNodes(true);
|
||||
|
||||
$overflow_message = pht(
|
||||
'This task is connected to more than %s other tasks. '.
|
||||
'Only direct parents and subtasks are shown here.',
|
||||
new PhutilNumber($graph_limit));
|
||||
}
|
||||
|
||||
$graph_table = $task_graph->newGraphTable();
|
||||
}
|
||||
|
||||
$graph_menu = $this->newTaskGraphDropdownMenu(
|
||||
$task,
|
||||
$has_parents,
|
||||
$has_subtasks,
|
||||
false);
|
||||
} else {
|
||||
$graph_menu = null;
|
||||
$graph_table = null;
|
||||
|
||||
$overflow_message = pht(
|
||||
'This task has no parent tasks and no subtasks, so there is no '.
|
||||
'graph to draw.');
|
||||
}
|
||||
|
||||
if ($overflow_message) {
|
||||
$overflow_view = $this->newTaskGraphOverflowView(
|
||||
$task,
|
||||
$overflow_message,
|
||||
false);
|
||||
|
||||
$graph_table = array(
|
||||
$overflow_view,
|
||||
$graph_table,
|
||||
);
|
||||
}
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Task Graph'));
|
||||
|
||||
if ($graph_menu) {
|
||||
$header->addActionLink($graph_menu);
|
||||
}
|
||||
|
||||
$tab_view = id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->appendChild($graph_table);
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setFooter($tab_view);
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle(
|
||||
array(
|
||||
$task->getMonogram(),
|
||||
pht('Graph'),
|
||||
))
|
||||
->setCrumbs($crumbs)
|
||||
->appendChild($view);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -47,9 +47,9 @@ final class ManiphestTaskSubtaskController
|
|||
$subtype = $subtype_map->getSubtype($subtype_key);
|
||||
|
||||
$subtask_uri = id(new PhutilURI("/task/edit/form/{$form_key}/"))
|
||||
->setQueryParam('parent', $id)
|
||||
->setQueryParam('template', $id)
|
||||
->setQueryParam('status', ManiphestTaskStatus::getDefaultStatus());
|
||||
->replaceQueryParam('parent', $id)
|
||||
->replaceQueryParam('template', $id)
|
||||
->replaceQueryParam('status', ManiphestTaskStatus::getDefaultStatus());
|
||||
$subtask_uri = $this->getApplicationURI($subtask_uri);
|
||||
|
||||
$item = id(new PHUIObjectItemView())
|
||||
|
|
|
@ -135,7 +135,7 @@ final class ManiphestTaskListView extends ManiphestView {
|
|||
if ($this->showBatchControls) {
|
||||
$href = new PhutilURI('/maniphest/task/edit/'.$task->getID().'/');
|
||||
if (!$this->showSubpriorityControls) {
|
||||
$href->setQueryParam('ungrippable', 'true');
|
||||
$href->replaceQueryParam('ungrippable', 'true');
|
||||
}
|
||||
$item->addAction(
|
||||
id(new PHUIListItemView())
|
||||
|
|
|
@ -24,6 +24,7 @@ final class PhabricatorMailMailgunAdapter
|
|||
array(
|
||||
'api-key' => 'string',
|
||||
'domain' => 'string',
|
||||
'api-hostname' => 'string',
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -31,12 +32,14 @@ final class PhabricatorMailMailgunAdapter
|
|||
return array(
|
||||
'api-key' => null,
|
||||
'domain' => null,
|
||||
'api-hostname' => 'api.mailgun.net',
|
||||
);
|
||||
}
|
||||
|
||||
public function sendMessage(PhabricatorMailExternalMessage $message) {
|
||||
$api_key = $this->getOption('api-key');
|
||||
$domain = $this->getOption('domain');
|
||||
$api_hostname = $this->getOption('api-hostname');
|
||||
$params = array();
|
||||
|
||||
$subject = $message->getSubject();
|
||||
|
@ -92,7 +95,8 @@ final class PhabricatorMailMailgunAdapter
|
|||
}
|
||||
|
||||
$mailgun_uri = urisprintf(
|
||||
'https://api.mailgun.net/v2/%s/messages',
|
||||
'https://%s/v2/%s/messages',
|
||||
$api_hostname,
|
||||
$domain);
|
||||
|
||||
$future = id(new HTTPSFuture($mailgun_uri, $params))
|
||||
|
|
|
@ -54,8 +54,7 @@ final class PhabricatorMetaMTAApplicationEmailPanel
|
|||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$uri = $request->getRequestURI();
|
||||
$uri->setQueryParams(array());
|
||||
$uri = new PhutilURI($request->getPath());
|
||||
|
||||
$new = $request->getStr('new');
|
||||
$edit = $request->getInt('edit');
|
||||
|
|
|
@ -302,11 +302,11 @@ final class MultimeterSampleController extends MultimeterController {
|
|||
if (!strlen($group)) {
|
||||
$group = null;
|
||||
}
|
||||
$uri->setQueryParam('group', $group);
|
||||
$uri->replaceQueryParam('group', $group);
|
||||
|
||||
if ($wipe) {
|
||||
foreach ($this->getColumnMap() as $key => $column) {
|
||||
$uri->setQueryParam($key, null);
|
||||
$uri->removeQueryParam($key);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,7 +317,7 @@ final class MultimeterSampleController extends MultimeterController {
|
|||
$value = (array)$value;
|
||||
|
||||
$uri = clone $this->getRequest()->getRequestURI();
|
||||
$uri->setQueryParam($key, implode(',', $value));
|
||||
$uri->replaceQueryParam($key, implode(',', $value));
|
||||
|
||||
return phutil_tag(
|
||||
'a',
|
||||
|
|
|
@ -153,7 +153,7 @@ final class PhabricatorNotificationServerRef
|
|||
|
||||
$instance = PhabricatorEnv::getEnvConfig('cluster.instance');
|
||||
if (strlen($instance)) {
|
||||
$uri->setQueryParam('instance', $instance);
|
||||
$uri->replaceQueryParam('instance', $instance);
|
||||
}
|
||||
|
||||
return $uri;
|
||||
|
|
|
@ -25,7 +25,7 @@ final class PhabricatorNotificationPanelController
|
|||
|
||||
$notifications_view = $builder->buildView();
|
||||
$content = $notifications_view->render();
|
||||
$clear_uri->setQueryParam(
|
||||
$clear_uri->replaceQueryParam(
|
||||
'chronoKey',
|
||||
head($stories)->getChronologicalKey());
|
||||
} else {
|
||||
|
|
|
@ -111,7 +111,7 @@ final class PhabricatorNotificationSearchEngine
|
|||
->setUser($viewer);
|
||||
|
||||
$view = $builder->buildView();
|
||||
$clear_uri->setQueryParam(
|
||||
$clear_uri->replaceQueryParam(
|
||||
'chronoKey',
|
||||
head($notifications)->getChronologicalKey());
|
||||
} else {
|
||||
|
|
|
@ -36,7 +36,7 @@ final class PhabricatorOAuthResponse extends AphrontResponse {
|
|||
$base_uri = $this->getClientURI();
|
||||
$query_params = $this->buildResponseDict();
|
||||
foreach ($query_params as $key => $value) {
|
||||
$base_uri->setQueryParam($key, $value);
|
||||
$base_uri->replaceQueryParam($key, $value);
|
||||
}
|
||||
return $base_uri;
|
||||
}
|
||||
|
|
|
@ -256,8 +256,8 @@ final class PhabricatorOAuthServer extends Phobject {
|
|||
|
||||
// Any query parameters present in the first URI must be exactly present
|
||||
// in the second URI.
|
||||
$need_params = $primary_uri->getQueryParams();
|
||||
$have_params = $secondary_uri->getQueryParams();
|
||||
$need_params = $primary_uri->getQueryParamsAsMap();
|
||||
$have_params = $secondary_uri->getQueryParamsAsMap();
|
||||
|
||||
foreach ($need_params as $key => $value) {
|
||||
if (!array_key_exists($key, $have_params)) {
|
||||
|
|
|
@ -306,7 +306,7 @@ final class PhabricatorOAuthServerAuthController
|
|||
|
||||
foreach ($params as $key => $value) {
|
||||
if (strlen($value)) {
|
||||
$full_uri->setQueryParam($key, $value);
|
||||
$full_uri->replaceQueryParam($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,11 +65,11 @@ final class PhabricatorOwnersDetailController
|
|||
|
||||
$commit_views = array();
|
||||
|
||||
$commit_uri = id(new PhutilURI('/diffusion/commit/'))
|
||||
->setQueryParams(
|
||||
array(
|
||||
'package' => $package->getPHID(),
|
||||
));
|
||||
$params = array(
|
||||
'package' => $package->getPHID(),
|
||||
);
|
||||
|
||||
$commit_uri = new PhutilURI('/diffusion/commit/', $params);
|
||||
|
||||
$status_concern = DiffusionCommitAuditStatus::CONCERN_RAISED;
|
||||
|
||||
|
|
|
@ -63,7 +63,6 @@ final class PhabricatorPeopleApplication extends PhabricatorApplication {
|
|||
'welcome/(?P<id>[1-9]\d*)/' => 'PhabricatorPeopleWelcomeController',
|
||||
'create/' => 'PhabricatorPeopleCreateController',
|
||||
'new/(?P<type>[^/]+)/' => 'PhabricatorPeopleNewController',
|
||||
'ldap/' => 'PhabricatorPeopleLdapController',
|
||||
'editprofile/(?P<id>[1-9]\d*)/' =>
|
||||
'PhabricatorPeopleProfileEditController',
|
||||
'badges/(?P<id>[1-9]\d*)/' =>
|
||||
|
|
|
@ -28,10 +28,6 @@ abstract class PhabricatorPeopleController extends PhabricatorController {
|
|||
|
||||
if ($viewer->getIsAdmin()) {
|
||||
$nav->addLabel(pht('User Administration'));
|
||||
if (PhabricatorLDAPAuthProvider::getLDAPProvider()) {
|
||||
$nav->addFilter('ldap', pht('Import from LDAP'));
|
||||
}
|
||||
|
||||
$nav->addFilter('logs', pht('Activity Logs'));
|
||||
$nav->addFilter('invite', pht('Email Invitations'));
|
||||
}
|
||||
|
|
|
@ -1,214 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPeopleLdapController
|
||||
extends PhabricatorPeopleController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$this->requireApplicationCapability(
|
||||
PeopleCreateUsersCapability::CAPABILITY);
|
||||
$admin = $request->getUser();
|
||||
|
||||
$content = array();
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setAction($request->getRequestURI()
|
||||
->alter('search', 'true')->alter('import', null))
|
||||
->setUser($admin)
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel(pht('LDAP username'))
|
||||
->setName('username'))
|
||||
->appendChild(
|
||||
id(new AphrontFormPasswordControl())
|
||||
->setDisableAutocomplete(true)
|
||||
->setLabel(pht('Password'))
|
||||
->setName('password'))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel(pht('LDAP query'))
|
||||
->setCaption(pht('A filter such as %s.', '(objectClass=*)'))
|
||||
->setName('query'))
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue(pht('Search')));
|
||||
|
||||
$panel = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Import LDAP Users'))
|
||||
->setForm($form);
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb(
|
||||
pht('Import LDAP Users'),
|
||||
$this->getApplicationURI('/ldap/'));
|
||||
|
||||
$nav = $this->buildSideNavView();
|
||||
$nav->selectFilter('ldap');
|
||||
$nav->appendChild($content);
|
||||
|
||||
if ($request->getStr('import')) {
|
||||
$nav->appendChild($this->processImportRequest($request));
|
||||
}
|
||||
|
||||
$nav->appendChild($panel);
|
||||
|
||||
if ($request->getStr('search')) {
|
||||
$nav->appendChild($this->processSearchRequest($request));
|
||||
}
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle(pht('Import LDAP Users'))
|
||||
->setCrumbs($crumbs)
|
||||
->setNavigation($nav);
|
||||
}
|
||||
|
||||
private function processImportRequest($request) {
|
||||
$admin = $request->getUser();
|
||||
$usernames = $request->getArr('usernames');
|
||||
$emails = $request->getArr('email');
|
||||
$names = $request->getArr('name');
|
||||
|
||||
$notice_view = new PHUIInfoView();
|
||||
$notice_view->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
|
||||
$notice_view->setTitle(pht('Import Successful'));
|
||||
$notice_view->setErrors(array(
|
||||
pht('Successfully imported users from LDAP'),
|
||||
));
|
||||
|
||||
$list = new PHUIObjectItemListView();
|
||||
$list->setNoDataString(pht('No users imported?'));
|
||||
|
||||
foreach ($usernames as $username) {
|
||||
$user = new PhabricatorUser();
|
||||
$user->setUsername($username);
|
||||
$user->setRealname($names[$username]);
|
||||
|
||||
$email_obj = id(new PhabricatorUserEmail())
|
||||
->setAddress($emails[$username])
|
||||
->setIsVerified(1);
|
||||
try {
|
||||
id(new PhabricatorUserEditor())
|
||||
->setActor($admin)
|
||||
->createNewUser($user, $email_obj);
|
||||
|
||||
id(new PhabricatorExternalAccount())
|
||||
->setUserPHID($user->getPHID())
|
||||
->setAccountType('ldap')
|
||||
->setAccountDomain('self')
|
||||
->setAccountID($username)
|
||||
->save();
|
||||
|
||||
$header = pht('Successfully added %s', $username);
|
||||
$attribute = null;
|
||||
$color = 'fa-check green';
|
||||
} catch (Exception $ex) {
|
||||
$header = pht('Failed to add %s', $username);
|
||||
$attribute = $ex->getMessage();
|
||||
$color = 'fa-times red';
|
||||
}
|
||||
|
||||
$item = id(new PHUIObjectItemView())
|
||||
->setHeader($header)
|
||||
->addAttribute($attribute)
|
||||
->setStatusIcon($color);
|
||||
|
||||
$list->addItem($item);
|
||||
}
|
||||
|
||||
return array(
|
||||
$notice_view,
|
||||
$list,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
private function processSearchRequest($request) {
|
||||
$panel = new PHUIBoxView();
|
||||
$admin = $request->getUser();
|
||||
|
||||
$search = $request->getStr('query');
|
||||
|
||||
$ldap_provider = PhabricatorLDAPAuthProvider::getLDAPProvider();
|
||||
if (!$ldap_provider) {
|
||||
throw new Exception(pht('No LDAP provider enabled!'));
|
||||
}
|
||||
|
||||
$ldap_adapter = $ldap_provider->getAdapter();
|
||||
$ldap_adapter->setLoginUsername($request->getStr('username'));
|
||||
$ldap_adapter->setLoginPassword(
|
||||
new PhutilOpaqueEnvelope($request->getStr('password')));
|
||||
|
||||
// This causes us to connect and bind.
|
||||
// TODO: Clean up this discard mode stuff.
|
||||
DarkConsoleErrorLogPluginAPI::enableDiscardMode();
|
||||
$ldap_adapter->getAccountID();
|
||||
DarkConsoleErrorLogPluginAPI::disableDiscardMode();
|
||||
|
||||
$results = $ldap_adapter->searchLDAP('%Q', $search);
|
||||
|
||||
foreach ($results as $key => $record) {
|
||||
$account_id = $ldap_adapter->readLDAPRecordAccountID($record);
|
||||
if (!$account_id) {
|
||||
unset($results[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$info = array(
|
||||
$account_id,
|
||||
$ldap_adapter->readLDAPRecordEmail($record),
|
||||
$ldap_adapter->readLDAPRecordRealName($record),
|
||||
);
|
||||
$results[$key] = $info;
|
||||
$results[$key][] = $this->renderUserInputs($info);
|
||||
}
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($admin);
|
||||
|
||||
$table = new AphrontTableView($results);
|
||||
$table->setHeaders(
|
||||
array(
|
||||
pht('Username'),
|
||||
pht('Email'),
|
||||
pht('Real Name'),
|
||||
pht('Import?'),
|
||||
));
|
||||
$form->appendChild($table);
|
||||
$form->setAction($request->getRequestURI()
|
||||
->alter('import', 'true')->alter('search', null))
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue(pht('Import')));
|
||||
|
||||
$panel->appendChild($form);
|
||||
|
||||
return $panel;
|
||||
}
|
||||
|
||||
private function renderUserInputs($user) {
|
||||
$username = $user[0];
|
||||
return hsprintf(
|
||||
'%s%s%s',
|
||||
phutil_tag(
|
||||
'input',
|
||||
array(
|
||||
'type' => 'checkbox',
|
||||
'name' => 'usernames[]',
|
||||
'value' => $username,
|
||||
)),
|
||||
phutil_tag(
|
||||
'input',
|
||||
array(
|
||||
'type' => 'hidden',
|
||||
'name' => "email[$username]",
|
||||
'value' => $user[1],
|
||||
)),
|
||||
phutil_tag(
|
||||
'input',
|
||||
array(
|
||||
'type' => 'hidden',
|
||||
'name' => "name[$username]",
|
||||
'value' => $user[2],
|
||||
)));
|
||||
}
|
||||
|
||||
}
|
|
@ -157,13 +157,10 @@ final class PhabricatorPeopleProfilePictureController
|
|||
continue;
|
||||
}
|
||||
|
||||
$provider = PhabricatorAuthProvider::getEnabledProviderByKey(
|
||||
$account->getProviderKey());
|
||||
if ($provider) {
|
||||
$tip = pht('Picture From %s', $provider->getProviderName());
|
||||
} else {
|
||||
$tip = pht('Picture From External Account');
|
||||
}
|
||||
$config = $account->getProviderConfig();
|
||||
$provider = $config->getProvider();
|
||||
|
||||
$tip = pht('Picture From %s', $provider->getProviderName());
|
||||
|
||||
if ($file->isTransformableImage()) {
|
||||
$images[$file->getPHID()] = array(
|
||||
|
|
|
@ -16,8 +16,10 @@ final class PhabricatorExternalAccount extends PhabricatorUserDAO
|
|||
protected $accountURI;
|
||||
protected $profileImagePHID;
|
||||
protected $properties = array();
|
||||
protected $providerConfigPHID;
|
||||
|
||||
private $profileImageFile = self::ATTACHABLE;
|
||||
private $providerConfig = self::ATTACHABLE;
|
||||
|
||||
public function getProfileImageFile() {
|
||||
return $this->assertAttached($this->profileImageFile);
|
||||
|
@ -65,13 +67,6 @@ final class PhabricatorExternalAccount extends PhabricatorUserDAO
|
|||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
public function getPhabricatorUser() {
|
||||
$tmp_usr = id(new PhabricatorUser())
|
||||
->makeEphemeral()
|
||||
->setPHID($this->getPHID());
|
||||
return $tmp_usr;
|
||||
}
|
||||
|
||||
public function getProviderKey() {
|
||||
return $this->getAccountType().':'.$this->getAccountDomain();
|
||||
}
|
||||
|
@ -93,13 +88,12 @@ final class PhabricatorExternalAccount extends PhabricatorUserDAO
|
|||
}
|
||||
|
||||
public function isUsableForLogin() {
|
||||
$key = $this->getProviderKey();
|
||||
$provider = PhabricatorAuthProvider::getEnabledProviderByKey($key);
|
||||
|
||||
if (!$provider) {
|
||||
$config = $this->getProviderConfig();
|
||||
if (!$config->getIsEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$provider = $config->getProvider();
|
||||
if (!$provider->shouldAllowLogin()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -125,6 +119,14 @@ final class PhabricatorExternalAccount extends PhabricatorUserDAO
|
|||
return idx($map, $type, pht('"%s" User', $type));
|
||||
}
|
||||
|
||||
public function attachProviderConfig(PhabricatorAuthProviderConfig $config) {
|
||||
$this->providerConfig = $config;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getProviderConfig() {
|
||||
return $this->assertAttached($this->providerConfig);
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
|
|
@ -557,7 +557,7 @@ final class PhabricatorUser
|
|||
|
||||
public static function describeValidUsername() {
|
||||
return pht(
|
||||
'Usernames must contain only numbers, letters, period, underscore and '.
|
||||
'Usernames must contain only numbers, letters, period, underscore, and '.
|
||||
'hyphen, and can not end with a period. They must have no more than %d '.
|
||||
'characters.',
|
||||
new PhutilNumber(self::MAXIMUM_USERNAME_LENGTH));
|
||||
|
|
|
@ -83,9 +83,8 @@ final class PhabricatorUserEmail extends PhabricatorUserDAO {
|
|||
*/
|
||||
public static function describeValidAddresses() {
|
||||
return pht(
|
||||
"Email addresses should be in the form '%s'. The maximum ".
|
||||
"length of an email address is %s character(s).",
|
||||
'user@domain.com',
|
||||
'Email addresses should be in the form "user@domain.com". The maximum '.
|
||||
'length of an email address is %s characters.',
|
||||
new PhutilNumber(self::MAX_ADDRESS_LENGTH));
|
||||
}
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ final class PholioMockImagesView extends AphrontView {
|
|||
);
|
||||
|
||||
$login_uri = id(new PhutilURI('/login/'))
|
||||
->setQueryParam('next', (string)$this->getRequestURI());
|
||||
->replaceQueryParam('next', (string)$this->getRequestURI());
|
||||
|
||||
$config = array(
|
||||
'mockID' => $mock->getID(),
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
final class PhortuneAddPaymentMethodAction
|
||||
extends PhabricatorSystemAction {
|
||||
|
||||
const TYPECONST = 'phortune.payment-method.add';
|
||||
|
||||
public function getActionConstant() {
|
||||
return self::TYPECONST;
|
||||
}
|
||||
|
||||
public function getScoreThreshold() {
|
||||
return 60 / phutil_units('1 hour in seconds');
|
||||
}
|
||||
|
||||
public function getLimitExplanation() {
|
||||
return pht(
|
||||
'You are making too many attempts to add payment methods in a short '.
|
||||
'period of time.');
|
||||
}
|
||||
|
||||
}
|
|
@ -134,13 +134,13 @@ final class PhortuneCartCheckoutController
|
|||
|
||||
$account_id = $account->getID();
|
||||
|
||||
$params = array(
|
||||
'merchantID' => $merchant->getID(),
|
||||
'cartID' => $cart->getID(),
|
||||
);
|
||||
|
||||
$payment_method_uri = $this->getApplicationURI("{$account_id}/card/new/");
|
||||
$payment_method_uri = new PhutilURI($payment_method_uri);
|
||||
$payment_method_uri->setQueryParams(
|
||||
array(
|
||||
'merchantID' => $merchant->getID(),
|
||||
'cartID' => $cart->getID(),
|
||||
));
|
||||
$payment_method_uri = new PhutilURI($payment_method_uri, $params);
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
|
|
|
@ -82,6 +82,15 @@ final class PhortunePaymentMethodCreateController
|
|||
->setProviderPHID($provider->getProviderConfig()->getPHID())
|
||||
->setStatus(PhortunePaymentMethod::STATUS_ACTIVE);
|
||||
|
||||
// Limit the rate at which you can attempt to add payment methods. This
|
||||
// is intended as a line of defense against using Phortune to validate a
|
||||
// large list of stolen credit card numbers.
|
||||
|
||||
PhabricatorSystemActionEngine::willTakeAction(
|
||||
array($viewer->getPHID()),
|
||||
new PhortuneAddPaymentMethodAction(),
|
||||
1);
|
||||
|
||||
if (!$errors) {
|
||||
$errors = $this->processClientErrors(
|
||||
$provider,
|
||||
|
@ -134,7 +143,7 @@ final class PhortunePaymentMethodCreateController
|
|||
"cart/{$cart_id}/checkout/?paymentMethodID=".$method->getID());
|
||||
} else if ($subscription_id) {
|
||||
$next_uri = new PhutilURI($cancel_uri);
|
||||
$next_uri->setQueryParam('added', true);
|
||||
$next_uri->replaceQueryParam('added', true);
|
||||
} else {
|
||||
$account_uri = $this->getApplicationURI($account->getID().'/');
|
||||
$next_uri = new PhutilURI($account_uri);
|
||||
|
|
|
@ -118,8 +118,8 @@ final class PhortuneSubscriptionEditController extends PhortuneController {
|
|||
|
||||
$uri = $this->getApplicationURI($account->getID().'/card/new/');
|
||||
$uri = new PhutilURI($uri);
|
||||
$uri->setQueryParam('merchantID', $merchant->getID());
|
||||
$uri->setQueryParam('subscriptionID', $subscription->getID());
|
||||
$uri->replaceQueryParam('merchantID', $merchant->getID());
|
||||
$uri->replaceQueryParam('subscriptionID', $subscription->getID());
|
||||
|
||||
$add_method_button = phutil_tag(
|
||||
'a',
|
||||
|
|
|
@ -348,12 +348,14 @@ final class PhortunePayPalPaymentProvider extends PhortunePaymentProvider {
|
|||
->setRawPayPalQuery('SetExpressCheckout', $params)
|
||||
->resolve();
|
||||
|
||||
$uri = new PhutilURI('https://www.sandbox.paypal.com/cgi-bin/webscr');
|
||||
$uri->setQueryParams(
|
||||
array(
|
||||
'cmd' => '_express-checkout',
|
||||
'token' => $result['TOKEN'],
|
||||
));
|
||||
$params = array(
|
||||
'cmd' => '_express-checkout',
|
||||
'token' => $result['TOKEN'],
|
||||
);
|
||||
|
||||
$uri = new PhutilURI(
|
||||
'https://www.sandbox.paypal.com/cgi-bin/webscr',
|
||||
$params);
|
||||
|
||||
$cart->setMetadataValue('provider.checkoutURI', (string)$uri);
|
||||
$cart->save();
|
||||
|
|
|
@ -273,8 +273,7 @@ abstract class PhortunePaymentProvider extends Phobject {
|
|||
$app = PhabricatorApplication::getByClass('PhabricatorPhortuneApplication');
|
||||
$path = $app->getBaseURI().'provider/'.$id.'/'.$action.'/';
|
||||
|
||||
$uri = new PhutilURI($path);
|
||||
$uri->setQueryParams($params);
|
||||
$uri = new PhutilURI($path, $params);
|
||||
|
||||
if ($local) {
|
||||
return $uri;
|
||||
|
|
|
@ -229,9 +229,14 @@ final class PhrictionTransactionEditor
|
|||
foreach ($xactions as $xaction) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhrictionDocumentContentTransaction::TRANSACTIONTYPE:
|
||||
$uri = id(new PhutilURI('/phriction/diff/'.$object->getID().'/'))
|
||||
->alter('l', $this->getOldContent()->getVersion())
|
||||
->alter('r', $this->getNewContent()->getVersion());
|
||||
$params = array(
|
||||
'l' => $this->getOldContent()->getVersion(),
|
||||
'r' => $this->getNewContent()->getVersion(),
|
||||
);
|
||||
|
||||
$path = '/phriction/diff/'.$object->getID().'/';
|
||||
$uri = new PhutilURI($path, $params);
|
||||
|
||||
$this->contentDiffURI = (string)$uri;
|
||||
break 2;
|
||||
default:
|
||||
|
|
|
@ -66,7 +66,7 @@ final class PonderAddAnswerView extends AphrontView {
|
|||
|
||||
if (!$viewer->isLoggedIn()) {
|
||||
$login_href = id(new PhutilURI('/auth/start/'))
|
||||
->setQueryParam('next', '/Q'.$question->getID());
|
||||
->replaceQueryParam('next', '/Q'.$question->getID());
|
||||
$form = id(new PHUIFormLayoutView())
|
||||
->addClass('login-to-participate')
|
||||
->appendChild(
|
||||
|
|
|
@ -284,7 +284,7 @@ final class PhabricatorProjectBoardViewController
|
|||
$query_key = $saved_query->getQueryKey();
|
||||
|
||||
$bulk_uri = new PhutilURI("/maniphest/bulk/query/{$query_key}/");
|
||||
$bulk_uri->setQueryParam('board', $this->id);
|
||||
$bulk_uri->replaceQueryParam('board', $this->id);
|
||||
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI($bulk_uri);
|
||||
|
@ -878,7 +878,7 @@ final class PhabricatorProjectBoardViewController
|
|||
}
|
||||
|
||||
$uri = $this->getURIWithState($uri)
|
||||
->setQueryParam('filter', null);
|
||||
->removeQueryParam('filter');
|
||||
$item->setHref($uri);
|
||||
|
||||
$items[] = $item;
|
||||
|
@ -966,12 +966,12 @@ final class PhabricatorProjectBoardViewController
|
|||
|
||||
if ($show_hidden) {
|
||||
$hidden_uri = $this->getURIWithState()
|
||||
->setQueryParam('hidden', null);
|
||||
->removeQueryParam('hidden');
|
||||
$hidden_icon = 'fa-eye-slash';
|
||||
$hidden_text = pht('Hide Hidden Columns');
|
||||
} else {
|
||||
$hidden_uri = $this->getURIWithState()
|
||||
->setQueryParam('hidden', 'true');
|
||||
->replaceQueryParam('hidden', 'true');
|
||||
$hidden_icon = 'fa-eye';
|
||||
$hidden_text = pht('Show Hidden Columns');
|
||||
}
|
||||
|
@ -999,7 +999,7 @@ final class PhabricatorProjectBoardViewController
|
|||
->setHref($manage_uri);
|
||||
|
||||
$batch_edit_uri = $request->getRequestURI();
|
||||
$batch_edit_uri->setQueryParam('batch', self::BATCH_EDIT_ALL);
|
||||
$batch_edit_uri->replaceQueryParam('batch', self::BATCH_EDIT_ALL);
|
||||
$can_batch_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
PhabricatorApplication::getByClass('PhabricatorManiphestApplication'),
|
||||
|
@ -1090,7 +1090,7 @@ final class PhabricatorProjectBoardViewController
|
|||
}
|
||||
|
||||
$batch_edit_uri = $request->getRequestURI();
|
||||
$batch_edit_uri->setQueryParam('batch', $column->getID());
|
||||
$batch_edit_uri->replaceQueryParam('batch', $column->getID());
|
||||
$can_batch_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
PhabricatorApplication::getByClass('PhabricatorManiphestApplication'),
|
||||
|
@ -1103,7 +1103,7 @@ final class PhabricatorProjectBoardViewController
|
|||
->setDisabled(!$can_batch_edit);
|
||||
|
||||
$batch_move_uri = $request->getRequestURI();
|
||||
$batch_move_uri->setQueryParam('move', $column->getID());
|
||||
$batch_move_uri->replaceQueryParam('move', $column->getID());
|
||||
$column_items[] = id(new PhabricatorActionView())
|
||||
->setIcon('fa-arrow-right')
|
||||
->setName(pht('Move Tasks to Column...'))
|
||||
|
@ -1111,7 +1111,7 @@ final class PhabricatorProjectBoardViewController
|
|||
->setWorkflow(true);
|
||||
|
||||
$query_uri = $request->getRequestURI();
|
||||
$query_uri->setQueryParam('queryColumnID', $column->getID());
|
||||
$query_uri->replaceQueryParam('queryColumnID', $column->getID());
|
||||
|
||||
$column_items[] = id(new PhabricatorActionView())
|
||||
->setName(pht('View as Query'))
|
||||
|
@ -1188,18 +1188,22 @@ final class PhabricatorProjectBoardViewController
|
|||
$base = new PhutilURI($base);
|
||||
|
||||
if ($force || ($this->sortKey != $this->getDefaultSort($project))) {
|
||||
$base->setQueryParam('order', $this->sortKey);
|
||||
$base->replaceQueryParam('order', $this->sortKey);
|
||||
} else {
|
||||
$base->setQueryParam('order', null);
|
||||
$base->removeQueryParam('order');
|
||||
}
|
||||
|
||||
if ($force || ($this->queryKey != $this->getDefaultFilter($project))) {
|
||||
$base->setQueryParam('filter', $this->queryKey);
|
||||
$base->replaceQueryParam('filter', $this->queryKey);
|
||||
} else {
|
||||
$base->setQueryParam('filter', null);
|
||||
$base->removeQueryParam('filter');
|
||||
}
|
||||
|
||||
$base->setQueryParam('hidden', $this->showHidden ? 'true' : null);
|
||||
if ($this->showHidden) {
|
||||
$base->replaceQueryParam('hidden', 'true');
|
||||
} else {
|
||||
$base->removeQueryParam('hidden');
|
||||
}
|
||||
|
||||
return $base;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ final class PhabricatorProjectColumnHideController
|
|||
$view_uri = $this->getApplicationURI('/board/'.$project_id.'/');
|
||||
$view_uri = new PhutilURI($view_uri);
|
||||
foreach ($request->getPassthroughRequestData() as $key => $value) {
|
||||
$view_uri->setQueryParam($key, $value);
|
||||
$view_uri->replaceQueryParam($key, $value);
|
||||
}
|
||||
|
||||
if ($column->isDefaultColumn()) {
|
||||
|
|
|
@ -54,7 +54,7 @@ final class PhabricatorProjectDefaultController
|
|||
$view_uri = $this->getApplicationURI("board/{$id}/");
|
||||
$view_uri = new PhutilURI($view_uri);
|
||||
foreach ($request->getPassthroughRequestData() as $key => $value) {
|
||||
$view_uri->setQueryParam($key, $value);
|
||||
$view_uri->replaceQueryParam($key, $value);
|
||||
}
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
|
|
|
@ -96,7 +96,7 @@ final class ReleephRequestDifferentialCreateController
|
|||
private function buildReleephRequestURI(ReleephBranch $branch) {
|
||||
$uri = $branch->getURI('request/');
|
||||
return id(new PhutilURI($uri))
|
||||
->setQueryParam('D', $this->revision->getID());
|
||||
->replaceQueryParam('D', $this->revision->getID());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -820,8 +820,6 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
return $uri;
|
||||
}
|
||||
|
||||
$uri = new PhutilURI($uri);
|
||||
|
||||
if (isset($params['lint'])) {
|
||||
$params['params'] = idx($params, 'params', array()) + array(
|
||||
'lint' => $params['lint'],
|
||||
|
@ -830,11 +828,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
|
||||
$query = idx($params, 'params', array()) + $query;
|
||||
|
||||
if ($query) {
|
||||
$uri->setQueryParams($query);
|
||||
}
|
||||
|
||||
return $uri;
|
||||
return new PhutilURI($uri, $query);
|
||||
}
|
||||
|
||||
public function updateURIIndex() {
|
||||
|
|
|
@ -905,7 +905,7 @@ final class PhabricatorApplicationSearchController
|
|||
$engine = $this->getSearchEngine();
|
||||
$nux_uri = $engine->getQueryBaseURI();
|
||||
$nux_uri = id(new PhutilURI($nux_uri))
|
||||
->setQueryParam('nux', true);
|
||||
->replaceQueryParam('nux', true);
|
||||
|
||||
$actions[] = id(new PhabricatorActionView())
|
||||
->setIcon('fa-user-plus')
|
||||
|
@ -915,7 +915,7 @@ final class PhabricatorApplicationSearchController
|
|||
|
||||
if ($is_dev) {
|
||||
$overheated_uri = $this->getRequest()->getRequestURI()
|
||||
->setQueryParam('overheated', true);
|
||||
->replaceQueryParam('overheated', true);
|
||||
|
||||
$actions[] = id(new PhabricatorActionView())
|
||||
->setIcon('fa-fire')
|
||||
|
|
|
@ -14,7 +14,7 @@ final class PhabricatorSearchWorker extends PhabricatorWorker {
|
|||
'parameters' => $parameters,
|
||||
),
|
||||
array(
|
||||
'priority' => parent::PRIORITY_IMPORT,
|
||||
'priority' => parent::PRIORITY_INDEX,
|
||||
'objectPHID' => $phid,
|
||||
));
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue