mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-02 03:32:42 +01:00
(stable) Promote 2015 Week 36
This commit is contained in:
commit
edf7c85e7a
133 changed files with 2527 additions and 1299 deletions
2
externals/phpqrcode/phpqrcode.php
vendored
2
externals/phpqrcode/phpqrcode.php
vendored
|
@ -2937,7 +2937,7 @@
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
public function getCode()
|
public function getCode()
|
||||||
{
|
{
|
||||||
$ret;
|
$ret = null;
|
||||||
|
|
||||||
if($this->count < $this->dataLength) {
|
if($this->count < $this->dataLength) {
|
||||||
$row = $this->count % $this->blocks;
|
$row = $this->count % $this->blocks;
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
*/
|
*/
|
||||||
return array(
|
return array(
|
||||||
'names' => array(
|
'names' => array(
|
||||||
'core.pkg.css' => 'aced76a5',
|
'core.pkg.css' => 'eb8c668d',
|
||||||
'core.pkg.js' => 'a590b451',
|
'core.pkg.js' => '47dc9ebb',
|
||||||
'darkconsole.pkg.js' => 'e7393ebb',
|
'darkconsole.pkg.js' => 'e7393ebb',
|
||||||
'differential.pkg.css' => '2de124c9',
|
'differential.pkg.css' => '2de124c9',
|
||||||
'differential.pkg.js' => '813c1633',
|
'differential.pkg.js' => '813c1633',
|
||||||
|
@ -36,18 +36,18 @@ return array(
|
||||||
'rsrc/css/application/base/notification-menu.css' => 'f31c0bde',
|
'rsrc/css/application/base/notification-menu.css' => 'f31c0bde',
|
||||||
'rsrc/css/application/base/phabricator-application-launch-view.css' => '95351601',
|
'rsrc/css/application/base/phabricator-application-launch-view.css' => '95351601',
|
||||||
'rsrc/css/application/base/phui-theme.css' => '6b451f24',
|
'rsrc/css/application/base/phui-theme.css' => '6b451f24',
|
||||||
'rsrc/css/application/base/standard-page-view.css' => '4d176b67',
|
'rsrc/css/application/base/standard-page-view.css' => '1f53d056',
|
||||||
'rsrc/css/application/calendar/calendar-icon.css' => 'c69aa59f',
|
'rsrc/css/application/calendar/calendar-icon.css' => 'c69aa59f',
|
||||||
'rsrc/css/application/chatlog/chatlog.css' => 'd295b020',
|
'rsrc/css/application/chatlog/chatlog.css' => 'd295b020',
|
||||||
'rsrc/css/application/conduit/conduit-api.css' => '7bc725c4',
|
'rsrc/css/application/conduit/conduit-api.css' => '7bc725c4',
|
||||||
'rsrc/css/application/config/config-options.css' => '7fedf08b',
|
'rsrc/css/application/config/config-options.css' => '0ede4c9b',
|
||||||
'rsrc/css/application/config/config-template.css' => '8e6c6fcd',
|
'rsrc/css/application/config/config-template.css' => '8e6c6fcd',
|
||||||
'rsrc/css/application/config/config-welcome.css' => '6abd79be',
|
'rsrc/css/application/config/config-welcome.css' => '6abd79be',
|
||||||
'rsrc/css/application/config/setup-issue.css' => 'db7e9c40',
|
'rsrc/css/application/config/setup-issue.css' => 'db7e9c40',
|
||||||
'rsrc/css/application/config/unhandled-exception.css' => '4c96257a',
|
'rsrc/css/application/config/unhandled-exception.css' => '4c96257a',
|
||||||
'rsrc/css/application/conpherence/durable-column.css' => '86396117',
|
'rsrc/css/application/conpherence/durable-column.css' => '86396117',
|
||||||
'rsrc/css/application/conpherence/menu.css' => 'f99fee4c',
|
'rsrc/css/application/conpherence/menu.css' => 'f99fee4c',
|
||||||
'rsrc/css/application/conpherence/message-pane.css' => 'dd4f8a3b',
|
'rsrc/css/application/conpherence/message-pane.css' => '5897d3ac',
|
||||||
'rsrc/css/application/conpherence/notification.css' => '6cdcc253',
|
'rsrc/css/application/conpherence/notification.css' => '6cdcc253',
|
||||||
'rsrc/css/application/conpherence/transaction.css' => '85d0974c',
|
'rsrc/css/application/conpherence/transaction.css' => '85d0974c',
|
||||||
'rsrc/css/application/conpherence/update.css' => 'faf6be09',
|
'rsrc/css/application/conpherence/update.css' => 'faf6be09',
|
||||||
|
@ -93,7 +93,7 @@ return array(
|
||||||
'rsrc/css/application/policy/policy-edit.css' => '815c66f7',
|
'rsrc/css/application/policy/policy-edit.css' => '815c66f7',
|
||||||
'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43',
|
'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43',
|
||||||
'rsrc/css/application/policy/policy.css' => '957ea14c',
|
'rsrc/css/application/policy/policy.css' => '957ea14c',
|
||||||
'rsrc/css/application/ponder/ponder-view.css' => 'bef48f86',
|
'rsrc/css/application/ponder/ponder-view.css' => '7b0df4da',
|
||||||
'rsrc/css/application/projects/project-icon.css' => '4e3eaa5a',
|
'rsrc/css/application/projects/project-icon.css' => '4e3eaa5a',
|
||||||
'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733',
|
'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733',
|
||||||
'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5',
|
'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5',
|
||||||
|
@ -104,14 +104,14 @@ return array(
|
||||||
'rsrc/css/application/tokens/tokens.css' => '3d0f239e',
|
'rsrc/css/application/tokens/tokens.css' => '3d0f239e',
|
||||||
'rsrc/css/application/uiexample/example.css' => '528b19de',
|
'rsrc/css/application/uiexample/example.css' => '528b19de',
|
||||||
'rsrc/css/core/core.css' => 'a76cefc9',
|
'rsrc/css/core/core.css' => 'a76cefc9',
|
||||||
'rsrc/css/core/remarkup.css' => '73fc4395',
|
'rsrc/css/core/remarkup.css' => 'ef286a6e',
|
||||||
'rsrc/css/core/syntax.css' => '9fd11da8',
|
'rsrc/css/core/syntax.css' => '9fd11da8',
|
||||||
'rsrc/css/core/z-index.css' => '57ddcaa2',
|
'rsrc/css/core/z-index.css' => '57ddcaa2',
|
||||||
'rsrc/css/diviner/diviner-shared.css' => '5a337049',
|
'rsrc/css/diviner/diviner-shared.css' => '5a337049',
|
||||||
'rsrc/css/font/font-awesome.css' => 'd2fc4e8d',
|
'rsrc/css/font/font-awesome.css' => 'd2fc4e8d',
|
||||||
'rsrc/css/font/font-lato.css' => '5ab1a46a',
|
'rsrc/css/font/font-lato.css' => '5ab1a46a',
|
||||||
'rsrc/css/font/font-roboto-slab.css' => 'f24a53cb',
|
'rsrc/css/font/font-roboto-slab.css' => 'f24a53cb',
|
||||||
'rsrc/css/font/phui-font-icon-base.css' => '3dad2ae3',
|
'rsrc/css/font/phui-font-icon-base.css' => 'ecbbb4c2',
|
||||||
'rsrc/css/layout/phabricator-filetree-view.css' => 'fccf9f82',
|
'rsrc/css/layout/phabricator-filetree-view.css' => 'fccf9f82',
|
||||||
'rsrc/css/layout/phabricator-hovercard-view.css' => '1239cd52',
|
'rsrc/css/layout/phabricator-hovercard-view.css' => '1239cd52',
|
||||||
'rsrc/css/layout/phabricator-side-menu-view.css' => 'bec2458e',
|
'rsrc/css/layout/phabricator-side-menu-view.css' => 'bec2458e',
|
||||||
|
@ -138,10 +138,10 @@ return array(
|
||||||
'rsrc/css/phui/phui-info-view.css' => '5b16bac6',
|
'rsrc/css/phui/phui-info-view.css' => '5b16bac6',
|
||||||
'rsrc/css/phui/phui-list.css' => '125599df',
|
'rsrc/css/phui/phui-list.css' => '125599df',
|
||||||
'rsrc/css/phui/phui-object-box.css' => '407eaf5a',
|
'rsrc/css/phui/phui-object-box.css' => '407eaf5a',
|
||||||
'rsrc/css/phui/phui-object-item-list-view.css' => '36ce366c',
|
'rsrc/css/phui/phui-object-item-list-view.css' => 'ab1bf393',
|
||||||
'rsrc/css/phui/phui-pager.css' => 'bea33d23',
|
'rsrc/css/phui/phui-pager.css' => 'bea33d23',
|
||||||
'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
|
'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
|
||||||
'rsrc/css/phui/phui-property-list-view.css' => '15bbe0b0',
|
'rsrc/css/phui/phui-property-list-view.css' => '03904f6b',
|
||||||
'rsrc/css/phui/phui-remarkup-preview.css' => '867f85b3',
|
'rsrc/css/phui/phui-remarkup-preview.css' => '867f85b3',
|
||||||
'rsrc/css/phui/phui-spacing.css' => '042804d6',
|
'rsrc/css/phui/phui-spacing.css' => '042804d6',
|
||||||
'rsrc/css/phui/phui-status.css' => '888cedb8',
|
'rsrc/css/phui/phui-status.css' => '888cedb8',
|
||||||
|
@ -210,7 +210,7 @@ return array(
|
||||||
'rsrc/externals/javelin/ext/view/__tests__/ViewInterpreter.js' => '7a94d6a5',
|
'rsrc/externals/javelin/ext/view/__tests__/ViewInterpreter.js' => '7a94d6a5',
|
||||||
'rsrc/externals/javelin/ext/view/__tests__/ViewRenderer.js' => '6ea96ac9',
|
'rsrc/externals/javelin/ext/view/__tests__/ViewRenderer.js' => '6ea96ac9',
|
||||||
'rsrc/externals/javelin/lib/Cookie.js' => '62dfea03',
|
'rsrc/externals/javelin/lib/Cookie.js' => '62dfea03',
|
||||||
'rsrc/externals/javelin/lib/DOM.js' => '147805fa',
|
'rsrc/externals/javelin/lib/DOM.js' => '805b806a',
|
||||||
'rsrc/externals/javelin/lib/History.js' => 'd4505101',
|
'rsrc/externals/javelin/lib/History.js' => 'd4505101',
|
||||||
'rsrc/externals/javelin/lib/JSON.js' => '69adf288',
|
'rsrc/externals/javelin/lib/JSON.js' => '69adf288',
|
||||||
'rsrc/externals/javelin/lib/Leader.js' => '331b1611',
|
'rsrc/externals/javelin/lib/Leader.js' => '331b1611',
|
||||||
|
@ -505,11 +505,11 @@ return array(
|
||||||
'calendar-icon-css' => 'c69aa59f',
|
'calendar-icon-css' => 'c69aa59f',
|
||||||
'changeset-view-manager' => '58562350',
|
'changeset-view-manager' => '58562350',
|
||||||
'conduit-api-css' => '7bc725c4',
|
'conduit-api-css' => '7bc725c4',
|
||||||
'config-options-css' => '7fedf08b',
|
'config-options-css' => '0ede4c9b',
|
||||||
'config-welcome-css' => '6abd79be',
|
'config-welcome-css' => '6abd79be',
|
||||||
'conpherence-durable-column-view' => '86396117',
|
'conpherence-durable-column-view' => '86396117',
|
||||||
'conpherence-menu-css' => 'f99fee4c',
|
'conpherence-menu-css' => 'f99fee4c',
|
||||||
'conpherence-message-pane-css' => 'dd4f8a3b',
|
'conpherence-message-pane-css' => '5897d3ac',
|
||||||
'conpherence-notification-css' => '6cdcc253',
|
'conpherence-notification-css' => '6cdcc253',
|
||||||
'conpherence-thread-manager' => '01774ab2',
|
'conpherence-thread-manager' => '01774ab2',
|
||||||
'conpherence-transaction-css' => '85d0974c',
|
'conpherence-transaction-css' => '85d0974c',
|
||||||
|
@ -659,7 +659,7 @@ return array(
|
||||||
'javelin-color' => '7e41274a',
|
'javelin-color' => '7e41274a',
|
||||||
'javelin-cookie' => '62dfea03',
|
'javelin-cookie' => '62dfea03',
|
||||||
'javelin-diffusion-locate-file-source' => 'b42eddc7',
|
'javelin-diffusion-locate-file-source' => 'b42eddc7',
|
||||||
'javelin-dom' => '147805fa',
|
'javelin-dom' => '805b806a',
|
||||||
'javelin-dynval' => 'f6555212',
|
'javelin-dynval' => 'f6555212',
|
||||||
'javelin-event' => '85ea0626',
|
'javelin-event' => '85ea0626',
|
||||||
'javelin-fx' => '54b612ba',
|
'javelin-fx' => '54b612ba',
|
||||||
|
@ -737,13 +737,13 @@ return array(
|
||||||
'phabricator-object-selector-css' => '85ee8ce6',
|
'phabricator-object-selector-css' => '85ee8ce6',
|
||||||
'phabricator-phtize' => 'd254d646',
|
'phabricator-phtize' => 'd254d646',
|
||||||
'phabricator-prefab' => '6920d200',
|
'phabricator-prefab' => '6920d200',
|
||||||
'phabricator-remarkup-css' => '73fc4395',
|
'phabricator-remarkup-css' => 'ef286a6e',
|
||||||
'phabricator-search-results-css' => '7dea472c',
|
'phabricator-search-results-css' => '7dea472c',
|
||||||
'phabricator-shaped-request' => '7cbe244b',
|
'phabricator-shaped-request' => '7cbe244b',
|
||||||
'phabricator-side-menu-view-css' => 'bec2458e',
|
'phabricator-side-menu-view-css' => 'bec2458e',
|
||||||
'phabricator-slowvote-css' => '475b4bd2',
|
'phabricator-slowvote-css' => '475b4bd2',
|
||||||
'phabricator-source-code-view-css' => '5e0178de',
|
'phabricator-source-code-view-css' => '5e0178de',
|
||||||
'phabricator-standard-page-view' => '4d176b67',
|
'phabricator-standard-page-view' => '1f53d056',
|
||||||
'phabricator-textareautils' => '5c93c52c',
|
'phabricator-textareautils' => '5c93c52c',
|
||||||
'phabricator-title' => 'df5e11d2',
|
'phabricator-title' => 'df5e11d2',
|
||||||
'phabricator-tooltip' => '1d298e3a',
|
'phabricator-tooltip' => '1d298e3a',
|
||||||
|
@ -779,7 +779,7 @@ return array(
|
||||||
'phui-crumbs-view-css' => 'd842f867',
|
'phui-crumbs-view-css' => 'd842f867',
|
||||||
'phui-document-view-css' => '0267054b',
|
'phui-document-view-css' => '0267054b',
|
||||||
'phui-feed-story-css' => 'b7b26d23',
|
'phui-feed-story-css' => 'b7b26d23',
|
||||||
'phui-font-icon-base-css' => '3dad2ae3',
|
'phui-font-icon-base-css' => 'ecbbb4c2',
|
||||||
'phui-fontkit-css' => 'cb8ae7ad',
|
'phui-fontkit-css' => 'cb8ae7ad',
|
||||||
'phui-form-css' => 'afdb2c6e',
|
'phui-form-css' => 'afdb2c6e',
|
||||||
'phui-form-view-css' => '621b21c5',
|
'phui-form-view-css' => '621b21c5',
|
||||||
|
@ -791,10 +791,10 @@ return array(
|
||||||
'phui-inline-comment-view-css' => '0fdb3667',
|
'phui-inline-comment-view-css' => '0fdb3667',
|
||||||
'phui-list-view-css' => '125599df',
|
'phui-list-view-css' => '125599df',
|
||||||
'phui-object-box-css' => '407eaf5a',
|
'phui-object-box-css' => '407eaf5a',
|
||||||
'phui-object-item-list-view-css' => '36ce366c',
|
'phui-object-item-list-view-css' => 'ab1bf393',
|
||||||
'phui-pager-css' => 'bea33d23',
|
'phui-pager-css' => 'bea33d23',
|
||||||
'phui-pinboard-view-css' => '2495140e',
|
'phui-pinboard-view-css' => '2495140e',
|
||||||
'phui-property-list-view-css' => '15bbe0b0',
|
'phui-property-list-view-css' => '03904f6b',
|
||||||
'phui-remarkup-preview-css' => '867f85b3',
|
'phui-remarkup-preview-css' => '867f85b3',
|
||||||
'phui-spacing-css' => '042804d6',
|
'phui-spacing-css' => '042804d6',
|
||||||
'phui-status-list-view-css' => '888cedb8',
|
'phui-status-list-view-css' => '888cedb8',
|
||||||
|
@ -811,7 +811,7 @@ return array(
|
||||||
'policy-css' => '957ea14c',
|
'policy-css' => '957ea14c',
|
||||||
'policy-edit-css' => '815c66f7',
|
'policy-edit-css' => '815c66f7',
|
||||||
'policy-transaction-detail-css' => '82100a43',
|
'policy-transaction-detail-css' => '82100a43',
|
||||||
'ponder-view-css' => 'bef48f86',
|
'ponder-view-css' => '7b0df4da',
|
||||||
'project-icon-css' => '4e3eaa5a',
|
'project-icon-css' => '4e3eaa5a',
|
||||||
'raphael-core' => '51ee6b43',
|
'raphael-core' => '51ee6b43',
|
||||||
'raphael-g' => '40dde778',
|
'raphael-g' => '40dde778',
|
||||||
|
@ -918,13 +918,6 @@ return array(
|
||||||
'javelin-uri',
|
'javelin-uri',
|
||||||
'phabricator-textareautils',
|
'phabricator-textareautils',
|
||||||
),
|
),
|
||||||
'147805fa' => array(
|
|
||||||
'javelin-magical-init',
|
|
||||||
'javelin-install',
|
|
||||||
'javelin-util',
|
|
||||||
'javelin-vector',
|
|
||||||
'javelin-stratcom',
|
|
||||||
),
|
|
||||||
'1499a8cb' => array(
|
'1499a8cb' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
|
@ -1451,6 +1444,13 @@ return array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-history',
|
'javelin-history',
|
||||||
),
|
),
|
||||||
|
'805b806a' => array(
|
||||||
|
'javelin-magical-init',
|
||||||
|
'javelin-install',
|
||||||
|
'javelin-util',
|
||||||
|
'javelin-vector',
|
||||||
|
'javelin-stratcom',
|
||||||
|
),
|
||||||
82439934 => array(
|
82439934 => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
|
2
resources/sql/autopatches/20150828.ponder.wiki.1.sql
Normal file
2
resources/sql/autopatches/20150828.ponder.wiki.1.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_ponder.ponder_question
|
||||||
|
ADD answerWiki LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL;
|
2
resources/sql/autopatches/20150829.ponder.dupe.1.sql
Normal file
2
resources/sql/autopatches/20150829.ponder.dupe.1.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
UPDATE {$NAMESPACE}_ponder.ponder_question
|
||||||
|
SET status = 'invalid' WHERE status = 'duplicate';
|
52
resources/sql/autopatches/20150904.herald.1.sql
Normal file
52
resources/sql/autopatches/20150904.herald.1.sql
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
/* The "20150730.herald.5.sql" patch incorrectly swapped blocking and
|
||||||
|
non-blocking "Add Reviewer" rules. This swaps back any rules which
|
||||||
|
were last modified before the patch was applied. */
|
||||||
|
|
||||||
|
UPDATE {$NAMESPACE}_herald.herald_action a
|
||||||
|
JOIN {$NAMESPACE}_herald.herald_rule r
|
||||||
|
ON a.ruleID = r.id
|
||||||
|
SET a.action = 'differential.reviewers.blocking.tmp'
|
||||||
|
WHERE a.action = 'differential.reviewers.add'
|
||||||
|
AND r.dateModified <=
|
||||||
|
(SELECT applied FROM {$NAMESPACE}_meta_data.patch_status
|
||||||
|
WHERE patch = 'phabricator:20150730.herald.5.sql');
|
||||||
|
|
||||||
|
UPDATE {$NAMESPACE}_herald.herald_action a
|
||||||
|
JOIN {$NAMESPACE}_herald.herald_rule r
|
||||||
|
ON a.ruleID = r.id
|
||||||
|
SET a.action = 'differential.reviewers.add'
|
||||||
|
WHERE a.action = 'differential.reviewers.blocking'
|
||||||
|
AND r.dateModified <=
|
||||||
|
(SELECT applied FROM {$NAMESPACE}_meta_data.patch_status
|
||||||
|
WHERE patch = 'phabricator:20150730.herald.5.sql');
|
||||||
|
|
||||||
|
UPDATE {$NAMESPACE}_herald.herald_action a
|
||||||
|
JOIN {$NAMESPACE}_herald.herald_rule r
|
||||||
|
ON a.ruleID = r.id
|
||||||
|
SET a.action = 'differential.reviewers.blocking'
|
||||||
|
WHERE a.action = 'differential.reviewers.blocking.tmp';
|
||||||
|
|
||||||
|
|
||||||
|
UPDATE {$NAMESPACE}_herald.herald_action a
|
||||||
|
JOIN {$NAMESPACE}_herald.herald_rule r
|
||||||
|
ON a.ruleID = r.id
|
||||||
|
SET a.action = 'differential.reviewers.self.blocking.tmp'
|
||||||
|
WHERE a.action = 'differential.reviewers.self.add'
|
||||||
|
AND r.dateModified <=
|
||||||
|
(SELECT applied FROM {$NAMESPACE}_meta_data.patch_status
|
||||||
|
WHERE patch = 'phabricator:20150730.herald.5.sql');
|
||||||
|
|
||||||
|
UPDATE {$NAMESPACE}_herald.herald_action a
|
||||||
|
JOIN {$NAMESPACE}_herald.herald_rule r
|
||||||
|
ON a.ruleID = r.id
|
||||||
|
SET a.action = 'differential.reviewers.self.add'
|
||||||
|
WHERE a.action = 'differential.reviewers.self.blocking'
|
||||||
|
AND r.dateModified <=
|
||||||
|
(SELECT applied FROM {$NAMESPACE}_meta_data.patch_status
|
||||||
|
WHERE patch = 'phabricator:20150730.herald.5.sql');
|
||||||
|
|
||||||
|
UPDATE {$NAMESPACE}_herald.herald_action a
|
||||||
|
JOIN {$NAMESPACE}_herald.herald_rule r
|
||||||
|
ON a.ruleID = r.id
|
||||||
|
SET a.action = 'differential.reviewers.self.blocking'
|
||||||
|
WHERE a.action = 'differential.reviewers.self.blocking.tmp';
|
|
@ -1,28 +0,0 @@
|
||||||
#!/usr/bin/env php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
$root = dirname(dirname(dirname(__FILE__)));
|
|
||||||
require_once $root.'/scripts/__init_script__.php';
|
|
||||||
|
|
||||||
if ($argc !== 2 || $argv[1] === '--help') {
|
|
||||||
echo pht('Usage: %s', 'aphrontpath.php <url>')."\n";
|
|
||||||
echo pht(
|
|
||||||
"Purpose: Print controller which will process passed %s.\n",
|
|
||||||
'<url>');
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$url = parse_url($argv[1]);
|
|
||||||
$path = '/'.(isset($url['path']) ? ltrim($url['path'], '/') : '');
|
|
||||||
|
|
||||||
$config_key = 'aphront.default-application-configuration-class';
|
|
||||||
$application = PhabricatorEnv::newObjectFromConfig($config_key);
|
|
||||||
$application->setRequest(new AphrontRequest('', $path));
|
|
||||||
|
|
||||||
list($controller) = $application->buildControllerForPath($path);
|
|
||||||
if (!$controller && substr($path, -1) !== '/') {
|
|
||||||
list($controller) = $application->buildControllerForPath($path.'/');
|
|
||||||
}
|
|
||||||
if ($controller) {
|
|
||||||
echo get_class($controller)."\n";
|
|
||||||
}
|
|
|
@ -143,6 +143,7 @@ phutil_register_library_map(array(
|
||||||
'AphrontJavelinView' => 'view/AphrontJavelinView.php',
|
'AphrontJavelinView' => 'view/AphrontJavelinView.php',
|
||||||
'AphrontKeyboardShortcutsAvailableView' => 'view/widget/AphrontKeyboardShortcutsAvailableView.php',
|
'AphrontKeyboardShortcutsAvailableView' => 'view/widget/AphrontKeyboardShortcutsAvailableView.php',
|
||||||
'AphrontListFilterView' => 'view/layout/AphrontListFilterView.php',
|
'AphrontListFilterView' => 'view/layout/AphrontListFilterView.php',
|
||||||
|
'AphrontMalformedRequestException' => 'aphront/exception/AphrontMalformedRequestException.php',
|
||||||
'AphrontMoreView' => 'view/layout/AphrontMoreView.php',
|
'AphrontMoreView' => 'view/layout/AphrontMoreView.php',
|
||||||
'AphrontMultiColumnView' => 'view/layout/AphrontMultiColumnView.php',
|
'AphrontMultiColumnView' => 'view/layout/AphrontMultiColumnView.php',
|
||||||
'AphrontMySQLDatabaseConnectionTestCase' => 'infrastructure/storage/__tests__/AphrontMySQLDatabaseConnectionTestCase.php',
|
'AphrontMySQLDatabaseConnectionTestCase' => 'infrastructure/storage/__tests__/AphrontMySQLDatabaseConnectionTestCase.php',
|
||||||
|
@ -156,8 +157,12 @@ phutil_register_library_map(array(
|
||||||
'AphrontRedirectResponseTestCase' => 'aphront/response/__tests__/AphrontRedirectResponseTestCase.php',
|
'AphrontRedirectResponseTestCase' => 'aphront/response/__tests__/AphrontRedirectResponseTestCase.php',
|
||||||
'AphrontReloadResponse' => 'aphront/response/AphrontReloadResponse.php',
|
'AphrontReloadResponse' => 'aphront/response/AphrontReloadResponse.php',
|
||||||
'AphrontRequest' => 'aphront/AphrontRequest.php',
|
'AphrontRequest' => 'aphront/AphrontRequest.php',
|
||||||
|
'AphrontRequestExceptionHandler' => 'aphront/handler/AphrontRequestExceptionHandler.php',
|
||||||
'AphrontRequestTestCase' => 'aphront/__tests__/AphrontRequestTestCase.php',
|
'AphrontRequestTestCase' => 'aphront/__tests__/AphrontRequestTestCase.php',
|
||||||
'AphrontResponse' => 'aphront/response/AphrontResponse.php',
|
'AphrontResponse' => 'aphront/response/AphrontResponse.php',
|
||||||
|
'AphrontResponseProducerInterface' => 'aphront/interface/AphrontResponseProducerInterface.php',
|
||||||
|
'AphrontRoutingMap' => 'aphront/site/AphrontRoutingMap.php',
|
||||||
|
'AphrontRoutingResult' => 'aphront/site/AphrontRoutingResult.php',
|
||||||
'AphrontSideNavFilterView' => 'view/layout/AphrontSideNavFilterView.php',
|
'AphrontSideNavFilterView' => 'view/layout/AphrontSideNavFilterView.php',
|
||||||
'AphrontSite' => 'aphront/site/AphrontSite.php',
|
'AphrontSite' => 'aphront/site/AphrontSite.php',
|
||||||
'AphrontStackTraceView' => 'view/widget/AphrontStackTraceView.php',
|
'AphrontStackTraceView' => 'view/widget/AphrontStackTraceView.php',
|
||||||
|
@ -166,9 +171,7 @@ phutil_register_library_map(array(
|
||||||
'AphrontTagView' => 'view/AphrontTagView.php',
|
'AphrontTagView' => 'view/AphrontTagView.php',
|
||||||
'AphrontTokenizerTemplateView' => 'view/control/AphrontTokenizerTemplateView.php',
|
'AphrontTokenizerTemplateView' => 'view/control/AphrontTokenizerTemplateView.php',
|
||||||
'AphrontTypeaheadTemplateView' => 'view/control/AphrontTypeaheadTemplateView.php',
|
'AphrontTypeaheadTemplateView' => 'view/control/AphrontTypeaheadTemplateView.php',
|
||||||
'AphrontURIMapper' => 'aphront/AphrontURIMapper.php',
|
|
||||||
'AphrontUnhandledExceptionResponse' => 'aphront/response/AphrontUnhandledExceptionResponse.php',
|
'AphrontUnhandledExceptionResponse' => 'aphront/response/AphrontUnhandledExceptionResponse.php',
|
||||||
'AphrontUsageException' => 'aphront/exception/AphrontUsageException.php',
|
|
||||||
'AphrontView' => 'view/AphrontView.php',
|
'AphrontView' => 'view/AphrontView.php',
|
||||||
'AphrontWebpageResponse' => 'aphront/response/AphrontWebpageResponse.php',
|
'AphrontWebpageResponse' => 'aphront/response/AphrontWebpageResponse.php',
|
||||||
'ArcanistConduitAPIMethod' => 'applications/arcanist/conduit/ArcanistConduitAPIMethod.php',
|
'ArcanistConduitAPIMethod' => 'applications/arcanist/conduit/ArcanistConduitAPIMethod.php',
|
||||||
|
@ -495,6 +498,7 @@ phutil_register_library_map(array(
|
||||||
'DifferentialUpdateRevisionConduitAPIMethod' => 'applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php',
|
'DifferentialUpdateRevisionConduitAPIMethod' => 'applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php',
|
||||||
'DifferentialViewPolicyField' => 'applications/differential/customfield/DifferentialViewPolicyField.php',
|
'DifferentialViewPolicyField' => 'applications/differential/customfield/DifferentialViewPolicyField.php',
|
||||||
'DiffusionAuditorDatasource' => 'applications/diffusion/typeahead/DiffusionAuditorDatasource.php',
|
'DiffusionAuditorDatasource' => 'applications/diffusion/typeahead/DiffusionAuditorDatasource.php',
|
||||||
|
'DiffusionAuditorFunctionDatasource' => 'applications/diffusion/typeahead/DiffusionAuditorFunctionDatasource.php',
|
||||||
'DiffusionAuditorsAddAuditorsHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsAddAuditorsHeraldAction.php',
|
'DiffusionAuditorsAddAuditorsHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsAddAuditorsHeraldAction.php',
|
||||||
'DiffusionAuditorsAddSelfHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsAddSelfHeraldAction.php',
|
'DiffusionAuditorsAddSelfHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsAddSelfHeraldAction.php',
|
||||||
'DiffusionAuditorsHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsHeraldAction.php',
|
'DiffusionAuditorsHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsHeraldAction.php',
|
||||||
|
@ -1481,6 +1485,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorActionView' => 'view/layout/PhabricatorActionView.php',
|
'PhabricatorActionView' => 'view/layout/PhabricatorActionView.php',
|
||||||
'PhabricatorActivitySettingsPanel' => 'applications/settings/panel/PhabricatorActivitySettingsPanel.php',
|
'PhabricatorActivitySettingsPanel' => 'applications/settings/panel/PhabricatorActivitySettingsPanel.php',
|
||||||
'PhabricatorAdministratorsPolicyRule' => 'applications/policy/rule/PhabricatorAdministratorsPolicyRule.php',
|
'PhabricatorAdministratorsPolicyRule' => 'applications/policy/rule/PhabricatorAdministratorsPolicyRule.php',
|
||||||
|
'PhabricatorAjaxRequestExceptionHandler' => 'aphront/handler/PhabricatorAjaxRequestExceptionHandler.php',
|
||||||
'PhabricatorAlmanacApplication' => 'applications/almanac/application/PhabricatorAlmanacApplication.php',
|
'PhabricatorAlmanacApplication' => 'applications/almanac/application/PhabricatorAlmanacApplication.php',
|
||||||
'PhabricatorAmazonAuthProvider' => 'applications/auth/provider/PhabricatorAmazonAuthProvider.php',
|
'PhabricatorAmazonAuthProvider' => 'applications/auth/provider/PhabricatorAmazonAuthProvider.php',
|
||||||
'PhabricatorAnchorView' => 'view/layout/PhabricatorAnchorView.php',
|
'PhabricatorAnchorView' => 'view/layout/PhabricatorAnchorView.php',
|
||||||
|
@ -1608,6 +1613,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorAuthLinkController' => 'applications/auth/controller/PhabricatorAuthLinkController.php',
|
'PhabricatorAuthLinkController' => 'applications/auth/controller/PhabricatorAuthLinkController.php',
|
||||||
'PhabricatorAuthListController' => 'applications/auth/controller/config/PhabricatorAuthListController.php',
|
'PhabricatorAuthListController' => 'applications/auth/controller/config/PhabricatorAuthListController.php',
|
||||||
'PhabricatorAuthLoginController' => 'applications/auth/controller/PhabricatorAuthLoginController.php',
|
'PhabricatorAuthLoginController' => 'applications/auth/controller/PhabricatorAuthLoginController.php',
|
||||||
|
'PhabricatorAuthLoginHandler' => 'applications/auth/handler/PhabricatorAuthLoginHandler.php',
|
||||||
'PhabricatorAuthManagementCachePKCS8Workflow' => 'applications/auth/management/PhabricatorAuthManagementCachePKCS8Workflow.php',
|
'PhabricatorAuthManagementCachePKCS8Workflow' => 'applications/auth/management/PhabricatorAuthManagementCachePKCS8Workflow.php',
|
||||||
'PhabricatorAuthManagementLDAPWorkflow' => 'applications/auth/management/PhabricatorAuthManagementLDAPWorkflow.php',
|
'PhabricatorAuthManagementLDAPWorkflow' => 'applications/auth/management/PhabricatorAuthManagementLDAPWorkflow.php',
|
||||||
'PhabricatorAuthManagementListFactorsWorkflow' => 'applications/auth/management/PhabricatorAuthManagementListFactorsWorkflow.php',
|
'PhabricatorAuthManagementListFactorsWorkflow' => 'applications/auth/management/PhabricatorAuthManagementListFactorsWorkflow.php',
|
||||||
|
@ -1783,6 +1789,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorConduitLogQuery' => 'applications/conduit/query/PhabricatorConduitLogQuery.php',
|
'PhabricatorConduitLogQuery' => 'applications/conduit/query/PhabricatorConduitLogQuery.php',
|
||||||
'PhabricatorConduitMethodCallLog' => 'applications/conduit/storage/PhabricatorConduitMethodCallLog.php',
|
'PhabricatorConduitMethodCallLog' => 'applications/conduit/storage/PhabricatorConduitMethodCallLog.php',
|
||||||
'PhabricatorConduitMethodQuery' => 'applications/conduit/query/PhabricatorConduitMethodQuery.php',
|
'PhabricatorConduitMethodQuery' => 'applications/conduit/query/PhabricatorConduitMethodQuery.php',
|
||||||
|
'PhabricatorConduitRequestExceptionHandler' => 'aphront/handler/PhabricatorConduitRequestExceptionHandler.php',
|
||||||
'PhabricatorConduitSearchEngine' => 'applications/conduit/query/PhabricatorConduitSearchEngine.php',
|
'PhabricatorConduitSearchEngine' => 'applications/conduit/query/PhabricatorConduitSearchEngine.php',
|
||||||
'PhabricatorConduitTestCase' => '__tests__/PhabricatorConduitTestCase.php',
|
'PhabricatorConduitTestCase' => '__tests__/PhabricatorConduitTestCase.php',
|
||||||
'PhabricatorConduitToken' => 'applications/conduit/storage/PhabricatorConduitToken.php',
|
'PhabricatorConduitToken' => 'applications/conduit/storage/PhabricatorConduitToken.php',
|
||||||
|
@ -1835,6 +1842,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorConfigOptionType' => 'applications/config/custom/PhabricatorConfigOptionType.php',
|
'PhabricatorConfigOptionType' => 'applications/config/custom/PhabricatorConfigOptionType.php',
|
||||||
'PhabricatorConfigPHIDModule' => 'applications/config/module/PhabricatorConfigPHIDModule.php',
|
'PhabricatorConfigPHIDModule' => 'applications/config/module/PhabricatorConfigPHIDModule.php',
|
||||||
'PhabricatorConfigProxySource' => 'infrastructure/env/PhabricatorConfigProxySource.php',
|
'PhabricatorConfigProxySource' => 'infrastructure/env/PhabricatorConfigProxySource.php',
|
||||||
|
'PhabricatorConfigRequestExceptionHandlerModule' => 'applications/config/module/PhabricatorConfigRequestExceptionHandlerModule.php',
|
||||||
'PhabricatorConfigResponse' => 'applications/config/response/PhabricatorConfigResponse.php',
|
'PhabricatorConfigResponse' => 'applications/config/response/PhabricatorConfigResponse.php',
|
||||||
'PhabricatorConfigSchemaQuery' => 'applications/config/schema/PhabricatorConfigSchemaQuery.php',
|
'PhabricatorConfigSchemaQuery' => 'applications/config/schema/PhabricatorConfigSchemaQuery.php',
|
||||||
'PhabricatorConfigSchemaSpec' => 'applications/config/schema/PhabricatorConfigSchemaSpec.php',
|
'PhabricatorConfigSchemaSpec' => 'applications/config/schema/PhabricatorConfigSchemaSpec.php',
|
||||||
|
@ -1990,6 +1998,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorDatabaseSetupCheck' => 'applications/config/check/PhabricatorDatabaseSetupCheck.php',
|
'PhabricatorDatabaseSetupCheck' => 'applications/config/check/PhabricatorDatabaseSetupCheck.php',
|
||||||
'PhabricatorDateTimeSettingsPanel' => 'applications/settings/panel/PhabricatorDateTimeSettingsPanel.php',
|
'PhabricatorDateTimeSettingsPanel' => 'applications/settings/panel/PhabricatorDateTimeSettingsPanel.php',
|
||||||
'PhabricatorDebugController' => 'applications/system/controller/PhabricatorDebugController.php',
|
'PhabricatorDebugController' => 'applications/system/controller/PhabricatorDebugController.php',
|
||||||
|
'PhabricatorDefaultRequestExceptionHandler' => 'aphront/handler/PhabricatorDefaultRequestExceptionHandler.php',
|
||||||
'PhabricatorDesktopNotificationsSettingsPanel' => 'applications/settings/panel/PhabricatorDesktopNotificationsSettingsPanel.php',
|
'PhabricatorDesktopNotificationsSettingsPanel' => 'applications/settings/panel/PhabricatorDesktopNotificationsSettingsPanel.php',
|
||||||
'PhabricatorDestructibleInterface' => 'applications/system/interface/PhabricatorDestructibleInterface.php',
|
'PhabricatorDestructibleInterface' => 'applications/system/interface/PhabricatorDestructibleInterface.php',
|
||||||
'PhabricatorDestructionEngine' => 'applications/system/engine/PhabricatorDestructionEngine.php',
|
'PhabricatorDestructionEngine' => 'applications/system/engine/PhabricatorDestructionEngine.php',
|
||||||
|
@ -2180,6 +2189,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorHelpEditorProtocolController' => 'applications/help/controller/PhabricatorHelpEditorProtocolController.php',
|
'PhabricatorHelpEditorProtocolController' => 'applications/help/controller/PhabricatorHelpEditorProtocolController.php',
|
||||||
'PhabricatorHelpKeyboardShortcutController' => 'applications/help/controller/PhabricatorHelpKeyboardShortcutController.php',
|
'PhabricatorHelpKeyboardShortcutController' => 'applications/help/controller/PhabricatorHelpKeyboardShortcutController.php',
|
||||||
'PhabricatorHeraldApplication' => 'applications/herald/application/PhabricatorHeraldApplication.php',
|
'PhabricatorHeraldApplication' => 'applications/herald/application/PhabricatorHeraldApplication.php',
|
||||||
|
'PhabricatorHighSecurityRequestExceptionHandler' => 'aphront/handler/PhabricatorHighSecurityRequestExceptionHandler.php',
|
||||||
'PhabricatorHomeApplication' => 'applications/home/application/PhabricatorHomeApplication.php',
|
'PhabricatorHomeApplication' => 'applications/home/application/PhabricatorHomeApplication.php',
|
||||||
'PhabricatorHomeController' => 'applications/home/controller/PhabricatorHomeController.php',
|
'PhabricatorHomeController' => 'applications/home/controller/PhabricatorHomeController.php',
|
||||||
'PhabricatorHomeMainController' => 'applications/home/controller/PhabricatorHomeMainController.php',
|
'PhabricatorHomeMainController' => 'applications/home/controller/PhabricatorHomeMainController.php',
|
||||||
|
@ -2435,6 +2445,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorOwnersOwner' => 'applications/owners/storage/PhabricatorOwnersOwner.php',
|
'PhabricatorOwnersOwner' => 'applications/owners/storage/PhabricatorOwnersOwner.php',
|
||||||
'PhabricatorOwnersPackage' => 'applications/owners/storage/PhabricatorOwnersPackage.php',
|
'PhabricatorOwnersPackage' => 'applications/owners/storage/PhabricatorOwnersPackage.php',
|
||||||
'PhabricatorOwnersPackageDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php',
|
'PhabricatorOwnersPackageDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php',
|
||||||
|
'PhabricatorOwnersPackageFunctionDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php',
|
||||||
|
'PhabricatorOwnersPackageOwnerDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php',
|
||||||
'PhabricatorOwnersPackagePHIDType' => 'applications/owners/phid/PhabricatorOwnersPackagePHIDType.php',
|
'PhabricatorOwnersPackagePHIDType' => 'applications/owners/phid/PhabricatorOwnersPackagePHIDType.php',
|
||||||
'PhabricatorOwnersPackageQuery' => 'applications/owners/query/PhabricatorOwnersPackageQuery.php',
|
'PhabricatorOwnersPackageQuery' => 'applications/owners/query/PhabricatorOwnersPackageQuery.php',
|
||||||
'PhabricatorOwnersPackageSearchEngine' => 'applications/owners/query/PhabricatorOwnersPackageSearchEngine.php',
|
'PhabricatorOwnersPackageSearchEngine' => 'applications/owners/query/PhabricatorOwnersPackageSearchEngine.php',
|
||||||
|
@ -2575,6 +2587,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPolicyManagementWorkflow' => 'applications/policy/management/PhabricatorPolicyManagementWorkflow.php',
|
'PhabricatorPolicyManagementWorkflow' => 'applications/policy/management/PhabricatorPolicyManagementWorkflow.php',
|
||||||
'PhabricatorPolicyPHIDTypePolicy' => 'applications/policy/phid/PhabricatorPolicyPHIDTypePolicy.php',
|
'PhabricatorPolicyPHIDTypePolicy' => 'applications/policy/phid/PhabricatorPolicyPHIDTypePolicy.php',
|
||||||
'PhabricatorPolicyQuery' => 'applications/policy/query/PhabricatorPolicyQuery.php',
|
'PhabricatorPolicyQuery' => 'applications/policy/query/PhabricatorPolicyQuery.php',
|
||||||
|
'PhabricatorPolicyRequestExceptionHandler' => 'aphront/handler/PhabricatorPolicyRequestExceptionHandler.php',
|
||||||
'PhabricatorPolicyRule' => 'applications/policy/rule/PhabricatorPolicyRule.php',
|
'PhabricatorPolicyRule' => 'applications/policy/rule/PhabricatorPolicyRule.php',
|
||||||
'PhabricatorPolicyTestCase' => 'applications/policy/__tests__/PhabricatorPolicyTestCase.php',
|
'PhabricatorPolicyTestCase' => 'applications/policy/__tests__/PhabricatorPolicyTestCase.php',
|
||||||
'PhabricatorPolicyTestObject' => 'applications/policy/__tests__/PhabricatorPolicyTestObject.php',
|
'PhabricatorPolicyTestObject' => 'applications/policy/__tests__/PhabricatorPolicyTestObject.php',
|
||||||
|
@ -2631,6 +2644,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectNoProjectsDatasource' => 'applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php',
|
'PhabricatorProjectNoProjectsDatasource' => 'applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php',
|
||||||
'PhabricatorProjectObjectHasProjectEdgeType' => 'applications/project/edge/PhabricatorProjectObjectHasProjectEdgeType.php',
|
'PhabricatorProjectObjectHasProjectEdgeType' => 'applications/project/edge/PhabricatorProjectObjectHasProjectEdgeType.php',
|
||||||
'PhabricatorProjectOrUserDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserDatasource.php',
|
'PhabricatorProjectOrUserDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserDatasource.php',
|
||||||
|
'PhabricatorProjectOrUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserFunctionDatasource.php',
|
||||||
'PhabricatorProjectProfileController' => 'applications/project/controller/PhabricatorProjectProfileController.php',
|
'PhabricatorProjectProfileController' => 'applications/project/controller/PhabricatorProjectProfileController.php',
|
||||||
'PhabricatorProjectProjectHasMemberEdgeType' => 'applications/project/edge/PhabricatorProjectProjectHasMemberEdgeType.php',
|
'PhabricatorProjectProjectHasMemberEdgeType' => 'applications/project/edge/PhabricatorProjectProjectHasMemberEdgeType.php',
|
||||||
'PhabricatorProjectProjectHasObjectEdgeType' => 'applications/project/edge/PhabricatorProjectProjectHasObjectEdgeType.php',
|
'PhabricatorProjectProjectHasObjectEdgeType' => 'applications/project/edge/PhabricatorProjectProjectHasObjectEdgeType.php',
|
||||||
|
@ -2650,6 +2664,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectTransactionQuery' => 'applications/project/query/PhabricatorProjectTransactionQuery.php',
|
'PhabricatorProjectTransactionQuery' => 'applications/project/query/PhabricatorProjectTransactionQuery.php',
|
||||||
'PhabricatorProjectUIEventListener' => 'applications/project/events/PhabricatorProjectUIEventListener.php',
|
'PhabricatorProjectUIEventListener' => 'applications/project/events/PhabricatorProjectUIEventListener.php',
|
||||||
'PhabricatorProjectUpdateController' => 'applications/project/controller/PhabricatorProjectUpdateController.php',
|
'PhabricatorProjectUpdateController' => 'applications/project/controller/PhabricatorProjectUpdateController.php',
|
||||||
|
'PhabricatorProjectUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php',
|
||||||
'PhabricatorProjectViewController' => 'applications/project/controller/PhabricatorProjectViewController.php',
|
'PhabricatorProjectViewController' => 'applications/project/controller/PhabricatorProjectViewController.php',
|
||||||
'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php',
|
'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php',
|
||||||
'PhabricatorProjectsPolicyRule' => 'applications/policy/rule/PhabricatorProjectsPolicyRule.php',
|
'PhabricatorProjectsPolicyRule' => 'applications/policy/rule/PhabricatorProjectsPolicyRule.php',
|
||||||
|
@ -2660,6 +2675,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorQueryOrderItem' => 'infrastructure/query/order/PhabricatorQueryOrderItem.php',
|
'PhabricatorQueryOrderItem' => 'infrastructure/query/order/PhabricatorQueryOrderItem.php',
|
||||||
'PhabricatorQueryOrderTestCase' => 'infrastructure/query/order/__tests__/PhabricatorQueryOrderTestCase.php',
|
'PhabricatorQueryOrderTestCase' => 'infrastructure/query/order/__tests__/PhabricatorQueryOrderTestCase.php',
|
||||||
'PhabricatorQueryOrderVector' => 'infrastructure/query/order/PhabricatorQueryOrderVector.php',
|
'PhabricatorQueryOrderVector' => 'infrastructure/query/order/PhabricatorQueryOrderVector.php',
|
||||||
|
'PhabricatorRateLimitRequestExceptionHandler' => 'aphront/handler/PhabricatorRateLimitRequestExceptionHandler.php',
|
||||||
'PhabricatorRecaptchaConfigOptions' => 'applications/config/option/PhabricatorRecaptchaConfigOptions.php',
|
'PhabricatorRecaptchaConfigOptions' => 'applications/config/option/PhabricatorRecaptchaConfigOptions.php',
|
||||||
'PhabricatorRecipientHasBadgeEdgeType' => 'applications/badges/edge/PhabricatorRecipientHasBadgeEdgeType.php',
|
'PhabricatorRecipientHasBadgeEdgeType' => 'applications/badges/edge/PhabricatorRecipientHasBadgeEdgeType.php',
|
||||||
'PhabricatorRedirectController' => 'applications/base/controller/PhabricatorRedirectController.php',
|
'PhabricatorRedirectController' => 'applications/base/controller/PhabricatorRedirectController.php',
|
||||||
|
@ -2751,6 +2767,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorRepositoryURITestCase' => 'applications/repository/storage/__tests__/PhabricatorRepositoryURITestCase.php',
|
'PhabricatorRepositoryURITestCase' => 'applications/repository/storage/__tests__/PhabricatorRepositoryURITestCase.php',
|
||||||
'PhabricatorRepositoryVCSPassword' => 'applications/repository/storage/PhabricatorRepositoryVCSPassword.php',
|
'PhabricatorRepositoryVCSPassword' => 'applications/repository/storage/PhabricatorRepositoryVCSPassword.php',
|
||||||
'PhabricatorRepositoryVersion' => 'applications/repository/constants/PhabricatorRepositoryVersion.php',
|
'PhabricatorRepositoryVersion' => 'applications/repository/constants/PhabricatorRepositoryVersion.php',
|
||||||
|
'PhabricatorRequestExceptionHandler' => 'aphront/handler/PhabricatorRequestExceptionHandler.php',
|
||||||
'PhabricatorResourceSite' => 'aphront/site/PhabricatorResourceSite.php',
|
'PhabricatorResourceSite' => 'aphront/site/PhabricatorResourceSite.php',
|
||||||
'PhabricatorRobotsController' => 'applications/system/controller/PhabricatorRobotsController.php',
|
'PhabricatorRobotsController' => 'applications/system/controller/PhabricatorRobotsController.php',
|
||||||
'PhabricatorS3FileStorageEngine' => 'applications/files/engine/PhabricatorS3FileStorageEngine.php',
|
'PhabricatorS3FileStorageEngine' => 'applications/files/engine/PhabricatorS3FileStorageEngine.php',
|
||||||
|
@ -3126,7 +3143,6 @@ phutil_register_library_map(array(
|
||||||
'PhameBlogListController' => 'applications/phame/controller/blog/PhameBlogListController.php',
|
'PhameBlogListController' => 'applications/phame/controller/blog/PhameBlogListController.php',
|
||||||
'PhameBlogLiveController' => 'applications/phame/controller/blog/PhameBlogLiveController.php',
|
'PhameBlogLiveController' => 'applications/phame/controller/blog/PhameBlogLiveController.php',
|
||||||
'PhameBlogQuery' => 'applications/phame/query/PhameBlogQuery.php',
|
'PhameBlogQuery' => 'applications/phame/query/PhameBlogQuery.php',
|
||||||
'PhameBlogResourceSite' => 'applications/phame/site/PhameBlogResourceSite.php',
|
|
||||||
'PhameBlogSearchEngine' => 'applications/phame/query/PhameBlogSearchEngine.php',
|
'PhameBlogSearchEngine' => 'applications/phame/query/PhameBlogSearchEngine.php',
|
||||||
'PhameBlogSite' => 'applications/phame/site/PhameBlogSite.php',
|
'PhameBlogSite' => 'applications/phame/site/PhameBlogSite.php',
|
||||||
'PhameBlogSkin' => 'applications/phame/skins/PhameBlogSkin.php',
|
'PhameBlogSkin' => 'applications/phame/skins/PhameBlogSkin.php',
|
||||||
|
@ -3727,7 +3743,10 @@ phutil_register_library_map(array(
|
||||||
'AphrontCursorPagerView' => 'AphrontView',
|
'AphrontCursorPagerView' => 'AphrontView',
|
||||||
'AphrontDefaultApplicationConfiguration' => 'AphrontApplicationConfiguration',
|
'AphrontDefaultApplicationConfiguration' => 'AphrontApplicationConfiguration',
|
||||||
'AphrontDialogResponse' => 'AphrontResponse',
|
'AphrontDialogResponse' => 'AphrontResponse',
|
||||||
'AphrontDialogView' => 'AphrontView',
|
'AphrontDialogView' => array(
|
||||||
|
'AphrontView',
|
||||||
|
'AphrontResponseProducerInterface',
|
||||||
|
),
|
||||||
'AphrontException' => 'Exception',
|
'AphrontException' => 'Exception',
|
||||||
'AphrontFileResponse' => 'AphrontResponse',
|
'AphrontFileResponse' => 'AphrontResponse',
|
||||||
'AphrontFormCheckboxControl' => 'AphrontFormControl',
|
'AphrontFormCheckboxControl' => 'AphrontFormControl',
|
||||||
|
@ -3762,6 +3781,7 @@ phutil_register_library_map(array(
|
||||||
'AphrontJavelinView' => 'AphrontView',
|
'AphrontJavelinView' => 'AphrontView',
|
||||||
'AphrontKeyboardShortcutsAvailableView' => 'AphrontView',
|
'AphrontKeyboardShortcutsAvailableView' => 'AphrontView',
|
||||||
'AphrontListFilterView' => 'AphrontView',
|
'AphrontListFilterView' => 'AphrontView',
|
||||||
|
'AphrontMalformedRequestException' => 'AphrontException',
|
||||||
'AphrontMoreView' => 'AphrontView',
|
'AphrontMoreView' => 'AphrontView',
|
||||||
'AphrontMultiColumnView' => 'AphrontView',
|
'AphrontMultiColumnView' => 'AphrontView',
|
||||||
'AphrontMySQLDatabaseConnectionTestCase' => 'PhabricatorTestCase',
|
'AphrontMySQLDatabaseConnectionTestCase' => 'PhabricatorTestCase',
|
||||||
|
@ -3770,13 +3790,19 @@ phutil_register_library_map(array(
|
||||||
'AphrontPageView' => 'AphrontView',
|
'AphrontPageView' => 'AphrontView',
|
||||||
'AphrontPlainTextResponse' => 'AphrontResponse',
|
'AphrontPlainTextResponse' => 'AphrontResponse',
|
||||||
'AphrontProgressBarView' => 'AphrontBarView',
|
'AphrontProgressBarView' => 'AphrontBarView',
|
||||||
'AphrontProxyResponse' => 'AphrontResponse',
|
'AphrontProxyResponse' => array(
|
||||||
|
'AphrontResponse',
|
||||||
|
'AphrontResponseProducerInterface',
|
||||||
|
),
|
||||||
'AphrontRedirectResponse' => 'AphrontResponse',
|
'AphrontRedirectResponse' => 'AphrontResponse',
|
||||||
'AphrontRedirectResponseTestCase' => 'PhabricatorTestCase',
|
'AphrontRedirectResponseTestCase' => 'PhabricatorTestCase',
|
||||||
'AphrontReloadResponse' => 'AphrontRedirectResponse',
|
'AphrontReloadResponse' => 'AphrontRedirectResponse',
|
||||||
'AphrontRequest' => 'Phobject',
|
'AphrontRequest' => 'Phobject',
|
||||||
|
'AphrontRequestExceptionHandler' => 'Phobject',
|
||||||
'AphrontRequestTestCase' => 'PhabricatorTestCase',
|
'AphrontRequestTestCase' => 'PhabricatorTestCase',
|
||||||
'AphrontResponse' => 'Phobject',
|
'AphrontResponse' => 'Phobject',
|
||||||
|
'AphrontRoutingMap' => 'Phobject',
|
||||||
|
'AphrontRoutingResult' => 'Phobject',
|
||||||
'AphrontSideNavFilterView' => 'AphrontView',
|
'AphrontSideNavFilterView' => 'AphrontView',
|
||||||
'AphrontSite' => 'Phobject',
|
'AphrontSite' => 'Phobject',
|
||||||
'AphrontStackTraceView' => 'AphrontView',
|
'AphrontStackTraceView' => 'AphrontView',
|
||||||
|
@ -3785,9 +3811,7 @@ phutil_register_library_map(array(
|
||||||
'AphrontTagView' => 'AphrontView',
|
'AphrontTagView' => 'AphrontView',
|
||||||
'AphrontTokenizerTemplateView' => 'AphrontView',
|
'AphrontTokenizerTemplateView' => 'AphrontView',
|
||||||
'AphrontTypeaheadTemplateView' => 'AphrontView',
|
'AphrontTypeaheadTemplateView' => 'AphrontView',
|
||||||
'AphrontURIMapper' => 'Phobject',
|
|
||||||
'AphrontUnhandledExceptionResponse' => 'AphrontStandaloneHTMLResponse',
|
'AphrontUnhandledExceptionResponse' => 'AphrontStandaloneHTMLResponse',
|
||||||
'AphrontUsageException' => 'AphrontException',
|
|
||||||
'AphrontView' => array(
|
'AphrontView' => array(
|
||||||
'Phobject',
|
'Phobject',
|
||||||
'PhutilSafeHTMLProducerInterface',
|
'PhutilSafeHTMLProducerInterface',
|
||||||
|
@ -4157,6 +4181,7 @@ phutil_register_library_map(array(
|
||||||
'DifferentialUpdateRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod',
|
'DifferentialUpdateRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod',
|
||||||
'DifferentialViewPolicyField' => 'DifferentialCoreCustomField',
|
'DifferentialViewPolicyField' => 'DifferentialCoreCustomField',
|
||||||
'DiffusionAuditorDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
'DiffusionAuditorDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||||
|
'DiffusionAuditorFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||||
'DiffusionAuditorsAddAuditorsHeraldAction' => 'DiffusionAuditorsHeraldAction',
|
'DiffusionAuditorsAddAuditorsHeraldAction' => 'DiffusionAuditorsHeraldAction',
|
||||||
'DiffusionAuditorsAddSelfHeraldAction' => 'DiffusionAuditorsHeraldAction',
|
'DiffusionAuditorsAddSelfHeraldAction' => 'DiffusionAuditorsHeraldAction',
|
||||||
'DiffusionAuditorsHeraldAction' => 'HeraldAction',
|
'DiffusionAuditorsHeraldAction' => 'HeraldAction',
|
||||||
|
@ -4369,7 +4394,7 @@ phutil_register_library_map(array(
|
||||||
'DiffusionSearchQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
|
'DiffusionSearchQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
|
||||||
'DiffusionServeController' => 'DiffusionController',
|
'DiffusionServeController' => 'DiffusionController',
|
||||||
'DiffusionSetPasswordSettingsPanel' => 'PhabricatorSettingsPanel',
|
'DiffusionSetPasswordSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||||
'DiffusionSetupException' => 'AphrontUsageException',
|
'DiffusionSetupException' => 'Exception',
|
||||||
'DiffusionSubversionSSHWorkflow' => 'DiffusionSSHWorkflow',
|
'DiffusionSubversionSSHWorkflow' => 'DiffusionSSHWorkflow',
|
||||||
'DiffusionSubversionServeSSHWorkflow' => 'DiffusionSubversionSSHWorkflow',
|
'DiffusionSubversionServeSSHWorkflow' => 'DiffusionSubversionSSHWorkflow',
|
||||||
'DiffusionSubversionWireProtocol' => 'Phobject',
|
'DiffusionSubversionWireProtocol' => 'Phobject',
|
||||||
|
@ -5289,6 +5314,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorActionView' => 'AphrontView',
|
'PhabricatorActionView' => 'AphrontView',
|
||||||
'PhabricatorActivitySettingsPanel' => 'PhabricatorSettingsPanel',
|
'PhabricatorActivitySettingsPanel' => 'PhabricatorSettingsPanel',
|
||||||
'PhabricatorAdministratorsPolicyRule' => 'PhabricatorPolicyRule',
|
'PhabricatorAdministratorsPolicyRule' => 'PhabricatorPolicyRule',
|
||||||
|
'PhabricatorAjaxRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
|
||||||
'PhabricatorAlmanacApplication' => 'PhabricatorApplication',
|
'PhabricatorAlmanacApplication' => 'PhabricatorApplication',
|
||||||
'PhabricatorAmazonAuthProvider' => 'PhabricatorOAuth2AuthProvider',
|
'PhabricatorAmazonAuthProvider' => 'PhabricatorOAuth2AuthProvider',
|
||||||
'PhabricatorAnchorView' => 'AphrontView',
|
'PhabricatorAnchorView' => 'AphrontView',
|
||||||
|
@ -5433,6 +5459,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorAuthLinkController' => 'PhabricatorAuthController',
|
'PhabricatorAuthLinkController' => 'PhabricatorAuthController',
|
||||||
'PhabricatorAuthListController' => 'PhabricatorAuthProviderConfigController',
|
'PhabricatorAuthListController' => 'PhabricatorAuthProviderConfigController',
|
||||||
'PhabricatorAuthLoginController' => 'PhabricatorAuthController',
|
'PhabricatorAuthLoginController' => 'PhabricatorAuthController',
|
||||||
|
'PhabricatorAuthLoginHandler' => 'Phobject',
|
||||||
'PhabricatorAuthManagementCachePKCS8Workflow' => 'PhabricatorAuthManagementWorkflow',
|
'PhabricatorAuthManagementCachePKCS8Workflow' => 'PhabricatorAuthManagementWorkflow',
|
||||||
'PhabricatorAuthManagementLDAPWorkflow' => 'PhabricatorAuthManagementWorkflow',
|
'PhabricatorAuthManagementLDAPWorkflow' => 'PhabricatorAuthManagementWorkflow',
|
||||||
'PhabricatorAuthManagementListFactorsWorkflow' => 'PhabricatorAuthManagementWorkflow',
|
'PhabricatorAuthManagementListFactorsWorkflow' => 'PhabricatorAuthManagementWorkflow',
|
||||||
|
@ -5653,6 +5680,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPolicyInterface',
|
'PhabricatorPolicyInterface',
|
||||||
),
|
),
|
||||||
'PhabricatorConduitMethodQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'PhabricatorConduitMethodQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
|
'PhabricatorConduitRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
|
||||||
'PhabricatorConduitSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
'PhabricatorConduitSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||||
'PhabricatorConduitTestCase' => 'PhabricatorTestCase',
|
'PhabricatorConduitTestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorConduitToken' => array(
|
'PhabricatorConduitToken' => array(
|
||||||
|
@ -5715,6 +5743,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorConfigOptionType' => 'Phobject',
|
'PhabricatorConfigOptionType' => 'Phobject',
|
||||||
'PhabricatorConfigPHIDModule' => 'PhabricatorConfigModule',
|
'PhabricatorConfigPHIDModule' => 'PhabricatorConfigModule',
|
||||||
'PhabricatorConfigProxySource' => 'PhabricatorConfigSource',
|
'PhabricatorConfigProxySource' => 'PhabricatorConfigSource',
|
||||||
|
'PhabricatorConfigRequestExceptionHandlerModule' => 'PhabricatorConfigModule',
|
||||||
'PhabricatorConfigResponse' => 'AphrontStandaloneHTMLResponse',
|
'PhabricatorConfigResponse' => 'AphrontStandaloneHTMLResponse',
|
||||||
'PhabricatorConfigSchemaQuery' => 'Phobject',
|
'PhabricatorConfigSchemaQuery' => 'Phobject',
|
||||||
'PhabricatorConfigSchemaSpec' => 'Phobject',
|
'PhabricatorConfigSchemaSpec' => 'Phobject',
|
||||||
|
@ -5899,6 +5928,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorDatabaseSetupCheck' => 'PhabricatorSetupCheck',
|
'PhabricatorDatabaseSetupCheck' => 'PhabricatorSetupCheck',
|
||||||
'PhabricatorDateTimeSettingsPanel' => 'PhabricatorSettingsPanel',
|
'PhabricatorDateTimeSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||||
'PhabricatorDebugController' => 'PhabricatorController',
|
'PhabricatorDebugController' => 'PhabricatorController',
|
||||||
|
'PhabricatorDefaultRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
|
||||||
'PhabricatorDesktopNotificationsSettingsPanel' => 'PhabricatorSettingsPanel',
|
'PhabricatorDesktopNotificationsSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||||
'PhabricatorDestructionEngine' => 'Phobject',
|
'PhabricatorDestructionEngine' => 'Phobject',
|
||||||
'PhabricatorDeveloperConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
'PhabricatorDeveloperConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||||
|
@ -6124,6 +6154,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorHelpEditorProtocolController' => 'PhabricatorHelpController',
|
'PhabricatorHelpEditorProtocolController' => 'PhabricatorHelpController',
|
||||||
'PhabricatorHelpKeyboardShortcutController' => 'PhabricatorHelpController',
|
'PhabricatorHelpKeyboardShortcutController' => 'PhabricatorHelpController',
|
||||||
'PhabricatorHeraldApplication' => 'PhabricatorApplication',
|
'PhabricatorHeraldApplication' => 'PhabricatorApplication',
|
||||||
|
'PhabricatorHighSecurityRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
|
||||||
'PhabricatorHomeApplication' => 'PhabricatorApplication',
|
'PhabricatorHomeApplication' => 'PhabricatorApplication',
|
||||||
'PhabricatorHomeController' => 'PhabricatorController',
|
'PhabricatorHomeController' => 'PhabricatorController',
|
||||||
'PhabricatorHomeMainController' => 'PhabricatorHomeController',
|
'PhabricatorHomeMainController' => 'PhabricatorHomeController',
|
||||||
|
@ -6347,7 +6378,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorOAuthServer' => 'Phobject',
|
'PhabricatorOAuthServer' => 'Phobject',
|
||||||
'PhabricatorOAuthServerAccessToken' => 'PhabricatorOAuthServerDAO',
|
'PhabricatorOAuthServerAccessToken' => 'PhabricatorOAuthServerDAO',
|
||||||
'PhabricatorOAuthServerApplication' => 'PhabricatorApplication',
|
'PhabricatorOAuthServerApplication' => 'PhabricatorApplication',
|
||||||
'PhabricatorOAuthServerAuthController' => 'PhabricatorAuthController',
|
'PhabricatorOAuthServerAuthController' => 'PhabricatorOAuthServerController',
|
||||||
'PhabricatorOAuthServerAuthorizationCode' => 'PhabricatorOAuthServerDAO',
|
'PhabricatorOAuthServerAuthorizationCode' => 'PhabricatorOAuthServerDAO',
|
||||||
'PhabricatorOAuthServerAuthorizationsSettingsPanel' => 'PhabricatorSettingsPanel',
|
'PhabricatorOAuthServerAuthorizationsSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||||
'PhabricatorOAuthServerClient' => array(
|
'PhabricatorOAuthServerClient' => array(
|
||||||
|
@ -6365,7 +6396,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorOAuthServerScope' => 'Phobject',
|
'PhabricatorOAuthServerScope' => 'Phobject',
|
||||||
'PhabricatorOAuthServerTestCase' => 'PhabricatorTestCase',
|
'PhabricatorOAuthServerTestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorOAuthServerTestController' => 'PhabricatorOAuthServerController',
|
'PhabricatorOAuthServerTestController' => 'PhabricatorOAuthServerController',
|
||||||
'PhabricatorOAuthServerTokenController' => 'PhabricatorAuthController',
|
'PhabricatorOAuthServerTokenController' => 'PhabricatorOAuthServerController',
|
||||||
'PhabricatorObjectHandle' => array(
|
'PhabricatorObjectHandle' => array(
|
||||||
'Phobject',
|
'Phobject',
|
||||||
'PhabricatorPolicyInterface',
|
'PhabricatorPolicyInterface',
|
||||||
|
@ -6406,6 +6437,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorApplicationTransactionInterface',
|
'PhabricatorApplicationTransactionInterface',
|
||||||
),
|
),
|
||||||
'PhabricatorOwnersPackageDatasource' => 'PhabricatorTypeaheadDatasource',
|
'PhabricatorOwnersPackageDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||||
|
'PhabricatorOwnersPackageFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||||
|
'PhabricatorOwnersPackageOwnerDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||||
'PhabricatorOwnersPackagePHIDType' => 'PhabricatorPHIDType',
|
'PhabricatorOwnersPackagePHIDType' => 'PhabricatorPHIDType',
|
||||||
'PhabricatorOwnersPackageQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'PhabricatorOwnersPackageQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
'PhabricatorOwnersPackageSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
'PhabricatorOwnersPackageSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||||
|
@ -6571,6 +6604,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPolicyManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
'PhabricatorPolicyManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||||
'PhabricatorPolicyPHIDTypePolicy' => 'PhabricatorPHIDType',
|
'PhabricatorPolicyPHIDTypePolicy' => 'PhabricatorPHIDType',
|
||||||
'PhabricatorPolicyQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'PhabricatorPolicyQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
|
'PhabricatorPolicyRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
|
||||||
'PhabricatorPolicyRule' => 'Phobject',
|
'PhabricatorPolicyRule' => 'Phobject',
|
||||||
'PhabricatorPolicyTestCase' => 'PhabricatorTestCase',
|
'PhabricatorPolicyTestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorPolicyTestObject' => array(
|
'PhabricatorPolicyTestObject' => array(
|
||||||
|
@ -6649,6 +6683,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectNoProjectsDatasource' => 'PhabricatorTypeaheadDatasource',
|
'PhabricatorProjectNoProjectsDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||||
'PhabricatorProjectObjectHasProjectEdgeType' => 'PhabricatorEdgeType',
|
'PhabricatorProjectObjectHasProjectEdgeType' => 'PhabricatorEdgeType',
|
||||||
'PhabricatorProjectOrUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
'PhabricatorProjectOrUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||||
|
'PhabricatorProjectOrUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||||
'PhabricatorProjectProfileController' => 'PhabricatorProjectController',
|
'PhabricatorProjectProfileController' => 'PhabricatorProjectController',
|
||||||
'PhabricatorProjectProjectHasMemberEdgeType' => 'PhabricatorEdgeType',
|
'PhabricatorProjectProjectHasMemberEdgeType' => 'PhabricatorEdgeType',
|
||||||
'PhabricatorProjectProjectHasObjectEdgeType' => 'PhabricatorEdgeType',
|
'PhabricatorProjectProjectHasObjectEdgeType' => 'PhabricatorEdgeType',
|
||||||
|
@ -6671,6 +6706,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
'PhabricatorProjectTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||||
'PhabricatorProjectUIEventListener' => 'PhabricatorEventListener',
|
'PhabricatorProjectUIEventListener' => 'PhabricatorEventListener',
|
||||||
'PhabricatorProjectUpdateController' => 'PhabricatorProjectController',
|
'PhabricatorProjectUpdateController' => 'PhabricatorProjectController',
|
||||||
|
'PhabricatorProjectUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||||
'PhabricatorProjectViewController' => 'PhabricatorProjectController',
|
'PhabricatorProjectViewController' => 'PhabricatorProjectController',
|
||||||
'PhabricatorProjectWatchController' => 'PhabricatorProjectController',
|
'PhabricatorProjectWatchController' => 'PhabricatorProjectController',
|
||||||
'PhabricatorProjectsPolicyRule' => 'PhabricatorPolicyRule',
|
'PhabricatorProjectsPolicyRule' => 'PhabricatorPolicyRule',
|
||||||
|
@ -6684,6 +6720,7 @@ phutil_register_library_map(array(
|
||||||
'Phobject',
|
'Phobject',
|
||||||
'Iterator',
|
'Iterator',
|
||||||
),
|
),
|
||||||
|
'PhabricatorRateLimitRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
|
||||||
'PhabricatorRecaptchaConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
'PhabricatorRecaptchaConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||||
'PhabricatorRecipientHasBadgeEdgeType' => 'PhabricatorEdgeType',
|
'PhabricatorRecipientHasBadgeEdgeType' => 'PhabricatorEdgeType',
|
||||||
'PhabricatorRedirectController' => 'PhabricatorController',
|
'PhabricatorRedirectController' => 'PhabricatorController',
|
||||||
|
@ -6810,6 +6847,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorRepositoryURITestCase' => 'PhabricatorTestCase',
|
'PhabricatorRepositoryURITestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorRepositoryVCSPassword' => 'PhabricatorRepositoryDAO',
|
'PhabricatorRepositoryVCSPassword' => 'PhabricatorRepositoryDAO',
|
||||||
'PhabricatorRepositoryVersion' => 'Phobject',
|
'PhabricatorRepositoryVersion' => 'Phobject',
|
||||||
|
'PhabricatorRequestExceptionHandler' => 'AphrontRequestExceptionHandler',
|
||||||
'PhabricatorResourceSite' => 'PhabricatorSite',
|
'PhabricatorResourceSite' => 'PhabricatorSite',
|
||||||
'PhabricatorRobotsController' => 'PhabricatorController',
|
'PhabricatorRobotsController' => 'PhabricatorController',
|
||||||
'PhabricatorS3FileStorageEngine' => 'PhabricatorFileStorageEngine',
|
'PhabricatorS3FileStorageEngine' => 'PhabricatorFileStorageEngine',
|
||||||
|
@ -7238,7 +7276,6 @@ phutil_register_library_map(array(
|
||||||
'PhameBlogListController' => 'PhameController',
|
'PhameBlogListController' => 'PhameController',
|
||||||
'PhameBlogLiveController' => 'PhameController',
|
'PhameBlogLiveController' => 'PhameController',
|
||||||
'PhameBlogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'PhameBlogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
'PhameBlogResourceSite' => 'PhameSite',
|
|
||||||
'PhameBlogSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
'PhameBlogSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||||
'PhameBlogSite' => 'PhameSite',
|
'PhameBlogSite' => 'PhameSite',
|
||||||
'PhameBlogSkin' => 'PhabricatorController',
|
'PhameBlogSkin' => 'PhabricatorController',
|
||||||
|
|
|
@ -24,10 +24,6 @@ abstract class AphrontController extends Phobject {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function didProcessRequest($response) {
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handleRequest(AphrontRequest $request) {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
if (method_exists($this, 'processRequest')) {
|
if (method_exists($this, 'processRequest')) {
|
||||||
return $this->processRequest();
|
return $this->processRequest();
|
||||||
|
|
|
@ -26,6 +26,8 @@ final class AphrontRequest extends Phobject {
|
||||||
private $requestData;
|
private $requestData;
|
||||||
private $user;
|
private $user;
|
||||||
private $applicationConfiguration;
|
private $applicationConfiguration;
|
||||||
|
private $site;
|
||||||
|
private $controller;
|
||||||
private $uriData;
|
private $uriData;
|
||||||
private $cookiePrefix;
|
private $cookiePrefix;
|
||||||
|
|
||||||
|
@ -77,6 +79,24 @@ final class AphrontRequest extends Phobject {
|
||||||
return $uri->getDomain();
|
return $uri->getDomain();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setSite(AphrontSite $site) {
|
||||||
|
$this->site = $site;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSite() {
|
||||||
|
return $this->site;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setController(AphrontController $controller) {
|
||||||
|
$this->controller = $controller;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getController() {
|
||||||
|
return $this->controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Accessing Request Data )--------------------------------------------- */
|
/* -( Accessing Request Data )--------------------------------------------- */
|
||||||
|
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class AphrontURIMapper extends Phobject {
|
|
||||||
|
|
||||||
private $map;
|
|
||||||
|
|
||||||
public function __construct(array $map) {
|
|
||||||
$this->map = $map;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function mapPath($path) {
|
|
||||||
$map = $this->map;
|
|
||||||
foreach ($map as $rule => $value) {
|
|
||||||
list($controller, $data) = $this->tryRule($rule, $value, $path);
|
|
||||||
if ($controller) {
|
|
||||||
foreach ($data as $k => $v) {
|
|
||||||
if (is_numeric($k)) {
|
|
||||||
unset($data[$k]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return array($controller, $data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return array(null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function tryRule($rule, $value, $path) {
|
|
||||||
$match = null;
|
|
||||||
$pattern = '#^'.$rule.(is_array($value) ? '' : '$').'#';
|
|
||||||
if (!preg_match($pattern, $path, $match)) {
|
|
||||||
return array(null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_array($value)) {
|
|
||||||
return array($value, $match);
|
|
||||||
}
|
|
||||||
|
|
||||||
$path = substr($path, strlen($match[0]));
|
|
||||||
foreach ($value as $srule => $sval) {
|
|
||||||
list($controller, $data) = $this->tryRule($srule, $sval, $path);
|
|
||||||
if ($controller) {
|
|
||||||
return array($controller, $data + $match);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return array(null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,7 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @task routing URI Routing
|
* @task routing URI Routing
|
||||||
|
* @task response Response Handling
|
||||||
|
* @task exception Exception Handling
|
||||||
*/
|
*/
|
||||||
abstract class AphrontApplicationConfiguration extends Phobject {
|
abstract class AphrontApplicationConfiguration extends Phobject {
|
||||||
|
|
||||||
|
@ -10,7 +12,6 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
||||||
private $path;
|
private $path;
|
||||||
private $console;
|
private $console;
|
||||||
|
|
||||||
abstract public function getApplicationName();
|
|
||||||
abstract public function buildRequest();
|
abstract public function buildRequest();
|
||||||
abstract public function build404Controller();
|
abstract public function build404Controller();
|
||||||
abstract public function buildRedirectController($uri, $external);
|
abstract public function buildRedirectController($uri, $external);
|
||||||
|
@ -210,7 +211,9 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
||||||
));
|
));
|
||||||
$multimeter->setEventContext('web.'.$controller_class);
|
$multimeter->setEventContext('web.'.$controller_class);
|
||||||
|
|
||||||
|
$request->setController($controller);
|
||||||
$request->setURIMap($uri_data);
|
$request->setURIMap($uri_data);
|
||||||
|
|
||||||
$controller->setRequest($request);
|
$controller->setRequest($request);
|
||||||
|
|
||||||
// If execution throws an exception and then trying to render that
|
// If execution throws an exception and then trying to render that
|
||||||
|
@ -232,6 +235,7 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
||||||
if (!$response) {
|
if (!$response) {
|
||||||
$controller->willProcessRequest($uri_data);
|
$controller->willProcessRequest($uri_data);
|
||||||
$response = $controller->handleRequest($request);
|
$response = $controller->handleRequest($request);
|
||||||
|
$this->validateControllerResponse($controller, $response);
|
||||||
}
|
}
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
$original_exception = $ex;
|
$original_exception = $ex;
|
||||||
|
@ -239,8 +243,8 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$response = $controller->didProcessRequest($response);
|
$response = $this->produceResponse($request, $response);
|
||||||
$response = $this->willSendResponse($response, $controller);
|
$response = $controller->willSendResponse($response);
|
||||||
$response->setRequest($request);
|
$response->setRequest($request);
|
||||||
|
|
||||||
$unexpected_output = PhabricatorStartup::endOutputCapture();
|
$unexpected_output = PhabricatorStartup::endOutputCapture();
|
||||||
|
@ -283,35 +287,13 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Using builtin and application routes, build the appropriate
|
* Build a controller to respond to the request.
|
||||||
* @{class:AphrontController} class for the request. To route a request, we
|
|
||||||
* first test if the HTTP_HOST is configured as a valid Phabricator URI. If
|
|
||||||
* it isn't, we do a special check to see if it's a custom domain for a blog
|
|
||||||
* in the Phame application and if that fails we error. Otherwise, we test
|
|
||||||
* against all application routes from installed
|
|
||||||
* @{class:PhabricatorApplication}s.
|
|
||||||
*
|
|
||||||
* If we match a route, we construct the controller it points at, build it,
|
|
||||||
* and return it.
|
|
||||||
*
|
|
||||||
* If we fail to match a route, but the current path is missing a trailing
|
|
||||||
* "/", we try routing the same path with a trailing "/" and do a redirect
|
|
||||||
* if that has a valid route. The idea is to canoncalize URIs for consistency,
|
|
||||||
* but avoid breaking noncanonical URIs that we can easily salvage.
|
|
||||||
*
|
|
||||||
* NOTE: We only redirect on GET. On POST, we'd drop parameters and most
|
|
||||||
* likely mutate the request implicitly, and a bad POST usually indicates a
|
|
||||||
* programming error rather than a sloppy typist.
|
|
||||||
*
|
|
||||||
* If the failing path already has a trailing "/", or we can't route the
|
|
||||||
* version with a "/", we call @{method:build404Controller}, which build a
|
|
||||||
* fallback @{class:AphrontController}.
|
|
||||||
*
|
*
|
||||||
* @return pair<AphrontController,dict> Controller and dictionary of request
|
* @return pair<AphrontController,dict> Controller and dictionary of request
|
||||||
* parameters.
|
* parameters.
|
||||||
* @task routing
|
* @task routing
|
||||||
*/
|
*/
|
||||||
final public function buildController() {
|
final private function buildController() {
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
|
|
||||||
// If we're configured to operate in cluster mode, reject requests which
|
// If we're configured to operate in cluster mode, reject requests which
|
||||||
|
@ -336,7 +318,7 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
||||||
// This is a command line script (probably something like a unit
|
// This is a command line script (probably something like a unit
|
||||||
// test) so it's fine that we don't have SERVER_ADDR defined.
|
// test) so it's fine that we don't have SERVER_ADDR defined.
|
||||||
} else {
|
} else {
|
||||||
throw new AphrontUsageException(
|
throw new AphrontMalformedRequestException(
|
||||||
pht('No %s', 'SERVER_ADDR'),
|
pht('No %s', 'SERVER_ADDR'),
|
||||||
pht(
|
pht(
|
||||||
'Phabricator is configured to operate in cluster mode, but '.
|
'Phabricator is configured to operate in cluster mode, but '.
|
||||||
|
@ -348,7 +330,7 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!PhabricatorEnv::isClusterAddress($server_addr)) {
|
if (!PhabricatorEnv::isClusterAddress($server_addr)) {
|
||||||
throw new AphrontUsageException(
|
throw new AphrontMalformedRequestException(
|
||||||
pht('External Interface'),
|
pht('External Interface'),
|
||||||
pht(
|
pht(
|
||||||
'Phabricator is configured in cluster mode and the address '.
|
'Phabricator is configured in cluster mode and the address '.
|
||||||
|
@ -373,78 +355,48 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Really, the Site should get more control here and be able to
|
$maps = $site->getRoutingMaps();
|
||||||
// do its own routing logic if it wants, but we don't need that for now.
|
$path = $request->getPath();
|
||||||
$path = $site->getPathForRouting($request);
|
|
||||||
|
|
||||||
list($controller, $uri_data) = $this->buildControllerForPath($path);
|
$result = $this->routePath($maps, $path);
|
||||||
if (!$controller) {
|
if ($result) {
|
||||||
if (!preg_match('@/$@', $path)) {
|
return $result;
|
||||||
// If we failed to match anything but don't have a trailing slash, try
|
|
||||||
// to add a trailing slash and issue a redirect if that resolves.
|
|
||||||
list($controller, $uri_data) = $this->buildControllerForPath($path.'/');
|
|
||||||
|
|
||||||
// NOTE: For POST, just 404 instead of redirecting, since the redirect
|
|
||||||
// will be a GET without parameters.
|
|
||||||
|
|
||||||
if ($controller && !$request->isHTTPPost()) {
|
|
||||||
$slash_uri = $request->getRequestURI()->setPath($path.'/');
|
|
||||||
|
|
||||||
$external = strlen($request->getRequestURI()->getDomain());
|
|
||||||
return $this->buildRedirectController($slash_uri, $external);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $this->build404Controller();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return array($controller, $uri_data);
|
// If we failed to match anything but don't have a trailing slash, try
|
||||||
}
|
// to add a trailing slash and issue a redirect if that resolves.
|
||||||
|
|
||||||
|
// NOTE: We only do this for GET, since redirects switch to GET and drop
|
||||||
|
// data like POST parameters.
|
||||||
|
if (!preg_match('@/$@', $path) && $request->isHTTPGet()) {
|
||||||
|
$result = $this->routePath($maps, $path.'/');
|
||||||
|
if ($result) {
|
||||||
|
$slash_uri = $request->getRequestURI()->setPath($path.'/');
|
||||||
|
$external = strlen($request->getRequestURI()->getDomain());
|
||||||
|
return $this->buildRedirectController($slash_uri, $external);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->build404Controller();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map a specific path to the corresponding controller. For a description
|
* Map a specific path to the corresponding controller. For a description
|
||||||
* of routing, see @{method:buildController}.
|
* of routing, see @{method:buildController}.
|
||||||
*
|
*
|
||||||
|
* @param list<AphrontRoutingMap> List of routing maps.
|
||||||
|
* @param string Path to route.
|
||||||
* @return pair<AphrontController,dict> Controller and dictionary of request
|
* @return pair<AphrontController,dict> Controller and dictionary of request
|
||||||
* parameters.
|
* parameters.
|
||||||
* @task routing
|
* @task routing
|
||||||
*/
|
*/
|
||||||
final public function buildControllerForPath($path) {
|
private function routePath(array $maps, $path) {
|
||||||
$maps = array();
|
foreach ($maps as $map) {
|
||||||
|
$result = $map->routePath($path);
|
||||||
$applications = PhabricatorApplication::getAllInstalledApplications();
|
if ($result) {
|
||||||
foreach ($applications as $application) {
|
return array($result->getController(), $result->getURIData());
|
||||||
$maps[] = array($application, $application->getRoutes());
|
|
||||||
}
|
|
||||||
|
|
||||||
$current_application = null;
|
|
||||||
$controller_class = null;
|
|
||||||
foreach ($maps as $map_info) {
|
|
||||||
list($application, $map) = $map_info;
|
|
||||||
|
|
||||||
$mapper = new AphrontURIMapper($map);
|
|
||||||
list($controller_class, $uri_data) = $mapper->mapPath($path);
|
|
||||||
|
|
||||||
if ($controller_class) {
|
|
||||||
if ($application) {
|
|
||||||
$current_application = $application;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$controller_class) {
|
|
||||||
return array(null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
$request = $this->getRequest();
|
|
||||||
|
|
||||||
$controller = newv($controller_class, array());
|
|
||||||
if ($current_application) {
|
|
||||||
$controller->setCurrentApplication($current_application);
|
|
||||||
}
|
|
||||||
|
|
||||||
return array($controller, $uri_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildSiteForRequest(AphrontRequest $request) {
|
private function buildSiteForRequest(AphrontRequest $request) {
|
||||||
|
@ -461,15 +413,223 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
||||||
if (!$site) {
|
if (!$site) {
|
||||||
$path = $request->getPath();
|
$path = $request->getPath();
|
||||||
$host = $request->getHost();
|
$host = $request->getHost();
|
||||||
throw new Exception(
|
throw new AphrontMalformedRequestException(
|
||||||
|
pht('Site Not Found'),
|
||||||
pht(
|
pht(
|
||||||
'This request asked for "%s" on host "%s", but no site is '.
|
'This request asked for "%s" on host "%s", but no site is '.
|
||||||
'configured which can serve this request.',
|
'configured which can serve this request.',
|
||||||
$path,
|
$path,
|
||||||
$host));
|
$host),
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$request->setSite($site);
|
||||||
|
|
||||||
return $site;
|
return $site;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( Response Handling )-------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if a response is of a valid type.
|
||||||
|
*
|
||||||
|
* @param wild Supposedly valid response.
|
||||||
|
* @return bool True if the object is of a valid type.
|
||||||
|
* @task response
|
||||||
|
*/
|
||||||
|
private function isValidResponseObject($response) {
|
||||||
|
if ($response instanceof AphrontResponse) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($response instanceof AphrontResponseProducerInterface) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that the return value from an @{class:AphrontController} is
|
||||||
|
* of an allowed type.
|
||||||
|
*
|
||||||
|
* @param AphrontController Controller which returned the response.
|
||||||
|
* @param wild Supposedly valid response.
|
||||||
|
* @return void
|
||||||
|
* @task response
|
||||||
|
*/
|
||||||
|
private function validateControllerResponse(
|
||||||
|
AphrontController $controller,
|
||||||
|
$response) {
|
||||||
|
|
||||||
|
if ($this->isValidResponseObject($response)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Controller "%s" returned an invalid response from call to "%s". '.
|
||||||
|
'This method must return an object of class "%s", or an object '.
|
||||||
|
'which implements the "%s" interface.',
|
||||||
|
get_class($controller),
|
||||||
|
'handleRequest()',
|
||||||
|
'AphrontResponse',
|
||||||
|
'AphrontResponseProducerInterface'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that the return value from an
|
||||||
|
* @{class:AphrontResponseProducerInterface} is of an allowed type.
|
||||||
|
*
|
||||||
|
* @param AphrontResponseProducerInterface Object which produced
|
||||||
|
* this response.
|
||||||
|
* @param wild Supposedly valid response.
|
||||||
|
* @return void
|
||||||
|
* @task response
|
||||||
|
*/
|
||||||
|
private function validateProducerResponse(
|
||||||
|
AphrontResponseProducerInterface $producer,
|
||||||
|
$response) {
|
||||||
|
|
||||||
|
if ($this->isValidResponseObject($response)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Producer "%s" returned an invalid response from call to "%s". '.
|
||||||
|
'This method must return an object of class "%s", or an object '.
|
||||||
|
'which implements the "%s" interface.',
|
||||||
|
get_class($producer),
|
||||||
|
'produceAphrontResponse()',
|
||||||
|
'AphrontResponse',
|
||||||
|
'AphrontResponseProducerInterface'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that the return value from an
|
||||||
|
* @{class:AphrontRequestExceptionHandler} is of an allowed type.
|
||||||
|
*
|
||||||
|
* @param AphrontRequestExceptionHandler Object which produced this
|
||||||
|
* response.
|
||||||
|
* @param wild Supposedly valid response.
|
||||||
|
* @return void
|
||||||
|
* @task response
|
||||||
|
*/
|
||||||
|
private function validateErrorHandlerResponse(
|
||||||
|
AphrontRequestExceptionHandler $handler,
|
||||||
|
$response) {
|
||||||
|
|
||||||
|
if ($this->isValidResponseObject($response)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Exception handler "%s" returned an invalid response from call to '.
|
||||||
|
'"%s". This method must return an object of class "%s", or an object '.
|
||||||
|
'which implements the "%s" interface.',
|
||||||
|
get_class($handler),
|
||||||
|
'handleRequestException()',
|
||||||
|
'AphrontResponse',
|
||||||
|
'AphrontResponseProducerInterface'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a response object into an @{class:AphrontResponse}.
|
||||||
|
*
|
||||||
|
* Controllers are permitted to return actual responses of class
|
||||||
|
* @{class:AphrontResponse}, or other objects which implement
|
||||||
|
* @{interface:AphrontResponseProducerInterface} and can produce a response.
|
||||||
|
*
|
||||||
|
* If a controller returns a response producer, invoke it now and produce
|
||||||
|
* the real response.
|
||||||
|
*
|
||||||
|
* @param AphrontRequest Request being handled.
|
||||||
|
* @param AphrontResponse|AphrontResponseProducerInterface Response, or
|
||||||
|
* response producer.
|
||||||
|
* @return AphrontResponse Response after any required production.
|
||||||
|
* @task response
|
||||||
|
*/
|
||||||
|
private function produceResponse(AphrontRequest $request, $response) {
|
||||||
|
$original = $response;
|
||||||
|
|
||||||
|
// Detect cycles on the exact same objects. It's still possible to produce
|
||||||
|
// infinite responses as long as they're all unique, but we can only
|
||||||
|
// reasonably detect cycles, not guarantee that response production halts.
|
||||||
|
|
||||||
|
$seen = array();
|
||||||
|
while (true) {
|
||||||
|
// NOTE: It is permissible for an object to be both a response and a
|
||||||
|
// response producer. If so, being a producer is "stronger". This is
|
||||||
|
// used by AphrontProxyResponse.
|
||||||
|
|
||||||
|
// If this response is a valid response, hand over the request first.
|
||||||
|
if ($response instanceof AphrontResponse) {
|
||||||
|
$response->setRequest($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this isn't a producer, we're all done.
|
||||||
|
if (!($response instanceof AphrontResponseProducerInterface)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$hash = spl_object_hash($response);
|
||||||
|
if (isset($seen[$hash])) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Failure while producing response for object of class "%s": '.
|
||||||
|
'encountered production cycle (identical object, of class "%s", '.
|
||||||
|
'was produced twice).',
|
||||||
|
get_class($original),
|
||||||
|
get_class($response)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$seen[$hash] = true;
|
||||||
|
|
||||||
|
$new_response = $response->produceAphrontResponse();
|
||||||
|
$this->validateProducerResponse($response, $new_response);
|
||||||
|
$response = $new_response;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( Error Handling )----------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert an exception which has escaped the controller into a response.
|
||||||
|
*
|
||||||
|
* This method delegates exception handling to available subclasses of
|
||||||
|
* @{class:AphrontRequestExceptionHandler}.
|
||||||
|
*
|
||||||
|
* @param Exception Exception which needs to be handled.
|
||||||
|
* @return wild Response or response producer, or null if no available
|
||||||
|
* handler can produce a response.
|
||||||
|
* @task exception
|
||||||
|
*/
|
||||||
|
private function handleException(Exception $ex) {
|
||||||
|
$handlers = AphrontRequestExceptionHandler::getAllHandlers();
|
||||||
|
|
||||||
|
$request = $this->getRequest();
|
||||||
|
foreach ($handlers as $handler) {
|
||||||
|
if ($handler->canHandleRequestException($request, $ex)) {
|
||||||
|
$response = $handler->handleRequestException($request, $ex);
|
||||||
|
$this->validateErrorHandlerResponse($handler, $response);
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,6 @@
|
||||||
class AphrontDefaultApplicationConfiguration
|
class AphrontDefaultApplicationConfiguration
|
||||||
extends AphrontApplicationConfiguration {
|
extends AphrontApplicationConfiguration {
|
||||||
|
|
||||||
public function __construct() {}
|
|
||||||
|
|
||||||
public function getApplicationName() {
|
|
||||||
return 'aphront-default';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @phutil-external-symbol class PhabricatorStartup
|
* @phutil-external-symbol class PhabricatorStartup
|
||||||
*/
|
*/
|
||||||
|
@ -50,233 +44,6 @@ class AphrontDefaultApplicationConfiguration
|
||||||
return $request;
|
return $request;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleException(Exception $ex) {
|
|
||||||
$request = $this->getRequest();
|
|
||||||
|
|
||||||
// For Conduit requests, return a Conduit response.
|
|
||||||
if ($request->isConduit()) {
|
|
||||||
$response = new ConduitAPIResponse();
|
|
||||||
$response->setErrorCode(get_class($ex));
|
|
||||||
$response->setErrorInfo($ex->getMessage());
|
|
||||||
|
|
||||||
return id(new AphrontJSONResponse())
|
|
||||||
->setAddJSONShield(false)
|
|
||||||
->setContent($response->toDictionary());
|
|
||||||
}
|
|
||||||
|
|
||||||
// For non-workflow requests, return a Ajax response.
|
|
||||||
if ($request->isAjax() && !$request->isWorkflow()) {
|
|
||||||
// Log these; they don't get shown on the client and can be difficult
|
|
||||||
// to debug.
|
|
||||||
phlog($ex);
|
|
||||||
|
|
||||||
$response = new AphrontAjaxResponse();
|
|
||||||
$response->setError(
|
|
||||||
array(
|
|
||||||
'code' => get_class($ex),
|
|
||||||
'info' => $ex->getMessage(),
|
|
||||||
));
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
$user = $request->getUser();
|
|
||||||
if (!$user) {
|
|
||||||
// If we hit an exception very early, we won't have a user.
|
|
||||||
$user = new PhabricatorUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($ex instanceof PhabricatorSystemActionRateLimitException) {
|
|
||||||
$dialog = id(new AphrontDialogView())
|
|
||||||
->setTitle(pht('Slow Down!'))
|
|
||||||
->setUser($user)
|
|
||||||
->setErrors(array(pht('You are being rate limited.')))
|
|
||||||
->appendParagraph($ex->getMessage())
|
|
||||||
->appendParagraph($ex->getRateExplanation())
|
|
||||||
->addCancelButton('/', pht('Okaaaaaaaaaaaaaay...'));
|
|
||||||
|
|
||||||
$response = new AphrontDialogResponse();
|
|
||||||
$response->setDialog($dialog);
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($ex instanceof PhabricatorAuthHighSecurityRequiredException) {
|
|
||||||
|
|
||||||
$form = id(new PhabricatorAuthSessionEngine())->renderHighSecurityForm(
|
|
||||||
$ex->getFactors(),
|
|
||||||
$ex->getFactorValidationResults(),
|
|
||||||
$user,
|
|
||||||
$request);
|
|
||||||
|
|
||||||
$dialog = id(new AphrontDialogView())
|
|
||||||
->setUser($user)
|
|
||||||
->setTitle(pht('Entering High Security'))
|
|
||||||
->setShortTitle(pht('Security Checkpoint'))
|
|
||||||
->setWidth(AphrontDialogView::WIDTH_FORM)
|
|
||||||
->addHiddenInput(AphrontRequest::TYPE_HISEC, true)
|
|
||||||
->setErrors(
|
|
||||||
array(
|
|
||||||
pht(
|
|
||||||
'You are taking an action which requires you to enter '.
|
|
||||||
'high security.'),
|
|
||||||
))
|
|
||||||
->appendParagraph(
|
|
||||||
pht(
|
|
||||||
'High security mode helps protect your account from security '.
|
|
||||||
'threats, like session theft or someone messing with your stuff '.
|
|
||||||
'while you\'re grabbing a coffee. To enter high security mode, '.
|
|
||||||
'confirm your credentials.'))
|
|
||||||
->appendChild($form->buildLayoutView())
|
|
||||||
->appendParagraph(
|
|
||||||
pht(
|
|
||||||
'Your account will remain in high security mode for a short '.
|
|
||||||
'period of time. When you are finished taking sensitive '.
|
|
||||||
'actions, you should leave high security.'))
|
|
||||||
->setSubmitURI($request->getPath())
|
|
||||||
->addCancelButton($ex->getCancelURI())
|
|
||||||
->addSubmitButton(pht('Enter High Security'));
|
|
||||||
|
|
||||||
$request_parameters = $request->getPassthroughRequestParameters(
|
|
||||||
$respect_quicksand = true);
|
|
||||||
foreach ($request_parameters as $key => $value) {
|
|
||||||
$dialog->addHiddenInput($key, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = new AphrontDialogResponse();
|
|
||||||
$response->setDialog($dialog);
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($ex instanceof PhabricatorPolicyException) {
|
|
||||||
if (!$user->isLoggedIn()) {
|
|
||||||
// If the user isn't logged in, just give them a login form. This is
|
|
||||||
// probably a generally more useful response than a policy dialog that
|
|
||||||
// they have to click through to get a login form.
|
|
||||||
//
|
|
||||||
// Possibly we should add a header here like "you need to login to see
|
|
||||||
// the thing you are trying to look at".
|
|
||||||
$login_controller = new PhabricatorAuthStartController();
|
|
||||||
$login_controller->setRequest($request);
|
|
||||||
|
|
||||||
$auth_app_class = 'PhabricatorAuthApplication';
|
|
||||||
$auth_app = PhabricatorApplication::getByClass($auth_app_class);
|
|
||||||
$login_controller->setCurrentApplication($auth_app);
|
|
||||||
|
|
||||||
return $login_controller->handleRequest($request);
|
|
||||||
}
|
|
||||||
|
|
||||||
$content = array(
|
|
||||||
phutil_tag(
|
|
||||||
'div',
|
|
||||||
array(
|
|
||||||
'class' => 'aphront-policy-rejection',
|
|
||||||
),
|
|
||||||
$ex->getRejection()),
|
|
||||||
);
|
|
||||||
|
|
||||||
$list = null;
|
|
||||||
if ($ex->getCapabilityName()) {
|
|
||||||
$list = $ex->getMoreInfo();
|
|
||||||
foreach ($list as $key => $item) {
|
|
||||||
$list[$key] = $item;
|
|
||||||
}
|
|
||||||
|
|
||||||
$content[] = phutil_tag(
|
|
||||||
'div',
|
|
||||||
array(
|
|
||||||
'class' => 'aphront-capability-details',
|
|
||||||
),
|
|
||||||
pht('Users with the "%s" capability:', $ex->getCapabilityName()));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
$dialog = id(new AphrontDialogView())
|
|
||||||
->setTitle($ex->getTitle())
|
|
||||||
->setClass('aphront-access-dialog')
|
|
||||||
->setUser($user)
|
|
||||||
->appendChild($content);
|
|
||||||
|
|
||||||
if ($list) {
|
|
||||||
$dialog->appendList($list);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->getRequest()->isAjax()) {
|
|
||||||
$dialog->addCancelButton('/', pht('Close'));
|
|
||||||
} else {
|
|
||||||
$dialog->addCancelButton('/', pht('OK'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = new AphrontDialogResponse();
|
|
||||||
$response->setDialog($dialog);
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($ex instanceof AphrontUsageException) {
|
|
||||||
$error = new PHUIInfoView();
|
|
||||||
$error->setTitle($ex->getTitle());
|
|
||||||
$error->appendChild($ex->getMessage());
|
|
||||||
|
|
||||||
$view = new PhabricatorStandardPageView();
|
|
||||||
$view->setRequest($this->getRequest());
|
|
||||||
$view->appendChild($error);
|
|
||||||
|
|
||||||
$response = new AphrontWebpageResponse();
|
|
||||||
$response->setContent($view->render());
|
|
||||||
$response->setHTTPResponseCode(500);
|
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always log the unhandled exception.
|
|
||||||
phlog($ex);
|
|
||||||
|
|
||||||
$class = get_class($ex);
|
|
||||||
$message = $ex->getMessage();
|
|
||||||
|
|
||||||
if ($ex instanceof AphrontSchemaQueryException) {
|
|
||||||
$message .= "\n\n".pht(
|
|
||||||
"NOTE: This usually indicates that the MySQL schema has not been ".
|
|
||||||
"properly upgraded. Run '%s' to ensure your schema is up to date.",
|
|
||||||
'bin/storage upgrade');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) {
|
|
||||||
$trace = id(new AphrontStackTraceView())
|
|
||||||
->setUser($user)
|
|
||||||
->setTrace($ex->getTrace());
|
|
||||||
} else {
|
|
||||||
$trace = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$content = phutil_tag(
|
|
||||||
'div',
|
|
||||||
array('class' => 'aphront-unhandled-exception'),
|
|
||||||
array(
|
|
||||||
phutil_tag('div', array('class' => 'exception-message'), $message),
|
|
||||||
$trace,
|
|
||||||
));
|
|
||||||
|
|
||||||
$dialog = new AphrontDialogView();
|
|
||||||
$dialog
|
|
||||||
->setTitle(pht('Unhandled Exception ("%s")', $class))
|
|
||||||
->setClass('aphront-exception-dialog')
|
|
||||||
->setUser($user)
|
|
||||||
->appendChild($content);
|
|
||||||
|
|
||||||
if ($this->getRequest()->isAjax()) {
|
|
||||||
$dialog->addCancelButton('/', pht('Close'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = new AphrontDialogResponse();
|
|
||||||
$response->setDialog($dialog);
|
|
||||||
$response->setHTTPResponseCode(500);
|
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function willSendResponse(AphrontResponse $response) {
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function build404Controller() {
|
public function build404Controller() {
|
||||||
return array(new Phabricator404Controller(), array());
|
return array(new Phabricator404Controller(), array());
|
||||||
}
|
}
|
||||||
|
|
34
src/aphront/exception/AphrontMalformedRequestException.php
Normal file
34
src/aphront/exception/AphrontMalformedRequestException.php
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These exceptions are raised when a client submits a malformed request.
|
||||||
|
*
|
||||||
|
* These errors are caught by Aphront itself and occur too early or too
|
||||||
|
* fundamentally in request handling to allow the request to route to a
|
||||||
|
* controller or survive to normal processing.
|
||||||
|
*
|
||||||
|
* These exceptions can be made "unlogged", which will prevent them from being
|
||||||
|
* logged. The intent is that errors which are purely the result of client
|
||||||
|
* failure and of no interest to the server can be raised silently to avoid
|
||||||
|
* cluttering the logs with client errors that are not actionable.
|
||||||
|
*/
|
||||||
|
final class AphrontMalformedRequestException extends AphrontException {
|
||||||
|
|
||||||
|
private $title;
|
||||||
|
private $isUnlogged;
|
||||||
|
|
||||||
|
public function __construct($title, $message, $unlogged = false) {
|
||||||
|
$this->title = $title;
|
||||||
|
$this->isUnlogged = $unlogged;
|
||||||
|
parent::__construct($message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle() {
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIsUnlogged() {
|
||||||
|
return $this->isUnlogged;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,21 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* These exceptions represent user error, and are not logged.
|
|
||||||
*
|
|
||||||
* @concrete-extensible
|
|
||||||
*/
|
|
||||||
class AphrontUsageException extends AphrontException {
|
|
||||||
|
|
||||||
private $title;
|
|
||||||
|
|
||||||
public function __construct($title, $message) {
|
|
||||||
$this->title = $title;
|
|
||||||
parent::__construct($message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTitle() {
|
|
||||||
return $this->title;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
36
src/aphront/handler/AphrontRequestExceptionHandler.php
Normal file
36
src/aphront/handler/AphrontRequestExceptionHandler.php
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React to an unhandled exception escaping request handling in a controller
|
||||||
|
* and convert it into a response.
|
||||||
|
*
|
||||||
|
* These handlers are generally used to render error pages, but they may
|
||||||
|
* also perform more specialized handling in situations where an error page
|
||||||
|
* is not appropriate.
|
||||||
|
*/
|
||||||
|
abstract class AphrontRequestExceptionHandler extends Phobject {
|
||||||
|
|
||||||
|
abstract public function getRequestExceptionHandlerPriority();
|
||||||
|
|
||||||
|
public function shouldLogException(
|
||||||
|
AphrontRequest $request,
|
||||||
|
Exception $ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function canHandleRequestException(
|
||||||
|
AphrontRequest $request,
|
||||||
|
Exception $ex);
|
||||||
|
|
||||||
|
abstract public function handleRequestException(
|
||||||
|
AphrontRequest $request,
|
||||||
|
Exception $ex);
|
||||||
|
|
||||||
|
final public static function getAllHandlers() {
|
||||||
|
return id(new PhutilClassMapQuery())
|
||||||
|
->setAncestorClass(__CLASS__)
|
||||||
|
->setSortMethod('getRequestExceptionHandlerPriority')
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorAjaxRequestExceptionHandler
|
||||||
|
extends PhabricatorRequestExceptionHandler {
|
||||||
|
|
||||||
|
public function getRequestExceptionHandlerPriority() {
|
||||||
|
return 110000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequestExceptionHandlerDescription() {
|
||||||
|
return pht('Responds to requests made by AJAX clients.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canHandleRequestException(
|
||||||
|
AphrontRequest $request,
|
||||||
|
Exception $ex) {
|
||||||
|
// For non-workflow requests, return a Ajax response.
|
||||||
|
return ($request->isAjax() && !$request->isWorkflow());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleRequestException(
|
||||||
|
AphrontRequest $request,
|
||||||
|
Exception $ex) {
|
||||||
|
|
||||||
|
// Log these; they don't get shown on the client and can be difficult
|
||||||
|
// to debug.
|
||||||
|
phlog($ex);
|
||||||
|
|
||||||
|
$response = new AphrontAjaxResponse();
|
||||||
|
$response->setError(
|
||||||
|
array(
|
||||||
|
'code' => get_class($ex),
|
||||||
|
'info' => $ex->getMessage(),
|
||||||
|
));
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorConduitRequestExceptionHandler
|
||||||
|
extends PhabricatorRequestExceptionHandler {
|
||||||
|
|
||||||
|
public function getRequestExceptionHandlerPriority() {
|
||||||
|
return 100000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequestExceptionHandlerDescription() {
|
||||||
|
return pht('Responds to requests made by Conduit clients.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canHandleRequestException(
|
||||||
|
AphrontRequest $request,
|
||||||
|
Exception $ex) {
|
||||||
|
return $request->isConduit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleRequestException(
|
||||||
|
AphrontRequest $request,
|
||||||
|
Exception $ex) {
|
||||||
|
|
||||||
|
$response = id(new ConduitAPIResponse())
|
||||||
|
->setErrorCode(get_class($ex))
|
||||||
|
->setErrorInfo($ex->getMessage());
|
||||||
|
|
||||||
|
return id(new AphrontJSONResponse())
|
||||||
|
->setAddJSONShield(false)
|
||||||
|
->setContent($response->toDictionary());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorDefaultRequestExceptionHandler
|
||||||
|
extends PhabricatorRequestExceptionHandler {
|
||||||
|
|
||||||
|
public function getRequestExceptionHandlerPriority() {
|
||||||
|
return 900000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequestExceptionHandlerDescription() {
|
||||||
|
return pht('Handles all other exceptions.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canHandleRequestException(
|
||||||
|
AphrontRequest $request,
|
||||||
|
Exception $ex) {
|
||||||
|
|
||||||
|
if (!$this->isPhabricatorSite($request)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleRequestException(
|
||||||
|
AphrontRequest $request,
|
||||||
|
Exception $ex) {
|
||||||
|
|
||||||
|
$viewer = $this->getViewer($request);
|
||||||
|
|
||||||
|
// Always log the unhandled exception.
|
||||||
|
phlog($ex);
|
||||||
|
|
||||||
|
$class = get_class($ex);
|
||||||
|
$message = $ex->getMessage();
|
||||||
|
|
||||||
|
if ($ex instanceof AphrontSchemaQueryException) {
|
||||||
|
$message .= "\n\n".pht(
|
||||||
|
"NOTE: This usually indicates that the MySQL schema has not been ".
|
||||||
|
"properly upgraded. Run '%s' to ensure your schema is up to date.",
|
||||||
|
'bin/storage upgrade');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) {
|
||||||
|
$trace = id(new AphrontStackTraceView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setTrace($ex->getTrace());
|
||||||
|
} else {
|
||||||
|
$trace = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = phutil_tag(
|
||||||
|
'div',
|
||||||
|
array('class' => 'aphront-unhandled-exception'),
|
||||||
|
array(
|
||||||
|
phutil_tag('div', array('class' => 'exception-message'), $message),
|
||||||
|
$trace,
|
||||||
|
));
|
||||||
|
|
||||||
|
$dialog = new AphrontDialogView();
|
||||||
|
$dialog
|
||||||
|
->setTitle(pht('Unhandled Exception ("%s")', $class))
|
||||||
|
->setClass('aphront-exception-dialog')
|
||||||
|
->setUser($viewer)
|
||||||
|
->appendChild($content);
|
||||||
|
|
||||||
|
if ($request->isAjax()) {
|
||||||
|
$dialog->addCancelButton('/', pht('Close'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return id(new AphrontDialogResponse())
|
||||||
|
->setDialog($dialog)
|
||||||
|
->setHTTPResponseCode(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorHighSecurityRequestExceptionHandler
|
||||||
|
extends PhabricatorRequestExceptionHandler {
|
||||||
|
|
||||||
|
public function getRequestExceptionHandlerPriority() {
|
||||||
|
return 310000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequestExceptionHandlerDescription() {
|
||||||
|
return pht(
|
||||||
|
'Handles high security exceptions which occur when a user needs '.
|
||||||
|
'to present MFA credentials to take an action.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canHandleRequestException(
|
||||||
|
AphrontRequest $request,
|
||||||
|
Exception $ex) {
|
||||||
|
|
||||||
|
if (!$this->isPhabricatorSite($request)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($ex instanceof PhabricatorAuthHighSecurityRequiredException);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleRequestException(
|
||||||
|
AphrontRequest $request,
|
||||||
|
Exception $ex) {
|
||||||
|
|
||||||
|
$viewer = $this->getViewer($request);
|
||||||
|
|
||||||
|
$form = id(new PhabricatorAuthSessionEngine())->renderHighSecurityForm(
|
||||||
|
$ex->getFactors(),
|
||||||
|
$ex->getFactorValidationResults(),
|
||||||
|
$viewer,
|
||||||
|
$request);
|
||||||
|
|
||||||
|
$dialog = id(new AphrontDialogView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setTitle(pht('Entering High Security'))
|
||||||
|
->setShortTitle(pht('Security Checkpoint'))
|
||||||
|
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||||
|
->addHiddenInput(AphrontRequest::TYPE_HISEC, true)
|
||||||
|
->setErrors(
|
||||||
|
array(
|
||||||
|
pht(
|
||||||
|
'You are taking an action which requires you to enter '.
|
||||||
|
'high security.'),
|
||||||
|
))
|
||||||
|
->appendParagraph(
|
||||||
|
pht(
|
||||||
|
'High security mode helps protect your account from security '.
|
||||||
|
'threats, like session theft or someone messing with your stuff '.
|
||||||
|
'while you\'re grabbing a coffee. To enter high security mode, '.
|
||||||
|
'confirm your credentials.'))
|
||||||
|
->appendChild($form->buildLayoutView())
|
||||||
|
->appendParagraph(
|
||||||
|
pht(
|
||||||
|
'Your account will remain in high security mode for a short '.
|
||||||
|
'period of time. When you are finished taking sensitive '.
|
||||||
|
'actions, you should leave high security.'))
|
||||||
|
->setSubmitURI($request->getPath())
|
||||||
|
->addCancelButton($ex->getCancelURI())
|
||||||
|
->addSubmitButton(pht('Enter High Security'));
|
||||||
|
|
||||||
|
$request_parameters = $request->getPassthroughRequestParameters(
|
||||||
|
$respect_quicksand = true);
|
||||||
|
foreach ($request_parameters as $key => $value) {
|
||||||
|
$dialog->addHiddenInput($key, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorPolicyRequestExceptionHandler
|
||||||
|
extends PhabricatorRequestExceptionHandler {
|
||||||
|
|
||||||
|
public function getRequestExceptionHandlerPriority() {
|
||||||
|
return 320000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequestExceptionHandlerDescription() {
|
||||||
|
return pht(
|
||||||
|
'Handles policy exceptions which occur when a user tries to '.
|
||||||
|
'do something they do not have permission to do.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canHandleRequestException(
|
||||||
|
AphrontRequest $request,
|
||||||
|
Exception $ex) {
|
||||||
|
|
||||||
|
if (!$this->isPhabricatorSite($request)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($ex instanceof PhabricatorPolicyException);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleRequestException(
|
||||||
|
AphrontRequest $request,
|
||||||
|
Exception $ex) {
|
||||||
|
|
||||||
|
$viewer = $this->getViewer($request);
|
||||||
|
|
||||||
|
if (!$viewer->isLoggedIn()) {
|
||||||
|
// If the user isn't logged in, just give them a login form. This is
|
||||||
|
// probably a generally more useful response than a policy dialog that
|
||||||
|
// they have to click through to get a login form.
|
||||||
|
//
|
||||||
|
// Possibly we should add a header here like "you need to login to see
|
||||||
|
// the thing you are trying to look at".
|
||||||
|
$auth_app_class = 'PhabricatorAuthApplication';
|
||||||
|
$auth_app = PhabricatorApplication::getByClass($auth_app_class);
|
||||||
|
|
||||||
|
return id(new PhabricatorAuthStartController())
|
||||||
|
->setRequest($request)
|
||||||
|
->setCurrentApplication($auth_app)
|
||||||
|
->handleRequest($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = array(
|
||||||
|
phutil_tag(
|
||||||
|
'div',
|
||||||
|
array(
|
||||||
|
'class' => 'aphront-policy-rejection',
|
||||||
|
),
|
||||||
|
$ex->getRejection()),
|
||||||
|
);
|
||||||
|
|
||||||
|
$list = null;
|
||||||
|
if ($ex->getCapabilityName()) {
|
||||||
|
$list = $ex->getMoreInfo();
|
||||||
|
foreach ($list as $key => $item) {
|
||||||
|
$list[$key] = $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
$content[] = phutil_tag(
|
||||||
|
'div',
|
||||||
|
array(
|
||||||
|
'class' => 'aphront-capability-details',
|
||||||
|
),
|
||||||
|
pht('Users with the "%s" capability:', $ex->getCapabilityName()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$dialog = id(new AphrontDialogView())
|
||||||
|
->setTitle($ex->getTitle())
|
||||||
|
->setClass('aphront-access-dialog')
|
||||||
|
->setUser($viewer)
|
||||||
|
->appendChild($content);
|
||||||
|
|
||||||
|
if ($list) {
|
||||||
|
$dialog->appendList($list);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->isAjax()) {
|
||||||
|
$dialog->addCancelButton('/', pht('Close'));
|
||||||
|
} else {
|
||||||
|
$dialog->addCancelButton('/', pht('OK'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorRateLimitRequestExceptionHandler
|
||||||
|
extends PhabricatorRequestExceptionHandler {
|
||||||
|
|
||||||
|
public function getRequestExceptionHandlerPriority() {
|
||||||
|
return 300000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequestExceptionHandlerDescription() {
|
||||||
|
return pht(
|
||||||
|
'Handles action rate limiting exceptions which occur when a user '.
|
||||||
|
'does something too frequently.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canHandleRequestException(
|
||||||
|
AphrontRequest $request,
|
||||||
|
Exception $ex) {
|
||||||
|
|
||||||
|
if (!$this->isPhabricatorSite($request)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($ex instanceof PhabricatorSystemActionRateLimitException);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleRequestException(
|
||||||
|
AphrontRequest $request,
|
||||||
|
Exception $ex) {
|
||||||
|
|
||||||
|
$viewer = $this->getViewer($request);
|
||||||
|
|
||||||
|
return id(new AphrontDialogView())
|
||||||
|
->setTitle(pht('Slow Down!'))
|
||||||
|
->setUser($viewer)
|
||||||
|
->setErrors(array(pht('You are being rate limited.')))
|
||||||
|
->appendParagraph($ex->getMessage())
|
||||||
|
->appendParagraph($ex->getRateExplanation())
|
||||||
|
->addCancelButton('/', pht('Okaaaaaaaaaaaaaay...'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
26
src/aphront/handler/PhabricatorRequestExceptionHandler.php
Normal file
26
src/aphront/handler/PhabricatorRequestExceptionHandler.php
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class PhabricatorRequestExceptionHandler
|
||||||
|
extends AphrontRequestExceptionHandler {
|
||||||
|
|
||||||
|
protected function isPhabricatorSite(AphrontRequest $request) {
|
||||||
|
$site = $request->getSite();
|
||||||
|
if (!$site) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($site instanceof PhabricatorSite);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getViewer(AphrontRequest $request) {
|
||||||
|
$viewer = $request->getUser();
|
||||||
|
|
||||||
|
if ($viewer) {
|
||||||
|
return $viewer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we hit an exception very early, we won't have a user yet.
|
||||||
|
return new PhabricatorUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
24
src/aphront/interface/AphrontResponseProducerInterface.php
Normal file
24
src/aphront/interface/AphrontResponseProducerInterface.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object can implement this interface to allow it to be returned directly
|
||||||
|
* from an @{class:AphrontController}.
|
||||||
|
*
|
||||||
|
* Normally, controllers must return an @{class:AphrontResponse}. Sometimes,
|
||||||
|
* this is not convenient or requires an awkward API. If it's preferable to
|
||||||
|
* return some other type of object which is equivalent to or describes a
|
||||||
|
* valid response, that object can implement this interface and produce a
|
||||||
|
* response later.
|
||||||
|
*/
|
||||||
|
interface AphrontResponseProducerInterface {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produce the equivalent @{class:AphrontResponse} for this object.
|
||||||
|
*
|
||||||
|
* @return AphrontResponse Equivalent response.
|
||||||
|
*/
|
||||||
|
public function produceAphrontResponse();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -8,7 +8,9 @@
|
||||||
* then constructing a real @{class:AphrontAjaxResponse} in
|
* then constructing a real @{class:AphrontAjaxResponse} in
|
||||||
* @{method:reduceProxyResponse}.
|
* @{method:reduceProxyResponse}.
|
||||||
*/
|
*/
|
||||||
abstract class AphrontProxyResponse extends AphrontResponse {
|
abstract class AphrontProxyResponse
|
||||||
|
extends AphrontResponse
|
||||||
|
implements AphrontResponseProducerInterface {
|
||||||
|
|
||||||
private $proxy;
|
private $proxy;
|
||||||
|
|
||||||
|
@ -71,4 +73,12 @@ abstract class AphrontProxyResponse extends AphrontResponse {
|
||||||
'reduceProxyResponse()'));
|
'reduceProxyResponse()'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( AphrontResponseProducerInterface )----------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
public function produceAphrontResponse() {
|
||||||
|
return $this->reduceProxyResponse();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,20 @@ final class AphrontUnhandledExceptionResponse
|
||||||
private $exception;
|
private $exception;
|
||||||
|
|
||||||
public function setException(Exception $exception) {
|
public function setException(Exception $exception) {
|
||||||
|
// Log the exception unless it's specifically a silent malformed request
|
||||||
|
// exception.
|
||||||
|
|
||||||
|
$should_log = true;
|
||||||
|
if ($exception instanceof AphrontMalformedRequestException) {
|
||||||
|
if ($exception->getIsUnlogged()) {
|
||||||
|
$should_log = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($should_log) {
|
||||||
|
phlog($exception);
|
||||||
|
}
|
||||||
|
|
||||||
$this->exception = $exception;
|
$this->exception = $exception;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -24,7 +38,7 @@ final class AphrontUnhandledExceptionResponse
|
||||||
protected function getResponseTitle() {
|
protected function getResponseTitle() {
|
||||||
$ex = $this->exception;
|
$ex = $this->exception;
|
||||||
|
|
||||||
if ($ex instanceof AphrontUsageException) {
|
if ($ex instanceof AphrontMalformedRequestException) {
|
||||||
return $ex->getTitle();
|
return $ex->getTitle();
|
||||||
} else {
|
} else {
|
||||||
return pht('Unhandled Exception');
|
return pht('Unhandled Exception');
|
||||||
|
@ -38,7 +52,7 @@ final class AphrontUnhandledExceptionResponse
|
||||||
protected function getResponseBody() {
|
protected function getResponseBody() {
|
||||||
$ex = $this->exception;
|
$ex = $this->exception;
|
||||||
|
|
||||||
if ($ex instanceof AphrontUsageException) {
|
if ($ex instanceof AphrontMalformedRequestException) {
|
||||||
$title = $ex->getTitle();
|
$title = $ex->getTitle();
|
||||||
} else {
|
} else {
|
||||||
$title = get_class($ex);
|
$title = get_class($ex);
|
||||||
|
|
159
src/aphront/site/AphrontRoutingMap.php
Normal file
159
src/aphront/site/AphrontRoutingMap.php
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection of routes on a site for an application.
|
||||||
|
*
|
||||||
|
* @task info Map Information
|
||||||
|
* @task routing Routing
|
||||||
|
*/
|
||||||
|
final class AphrontRoutingMap extends Phobject {
|
||||||
|
|
||||||
|
private $site;
|
||||||
|
private $application;
|
||||||
|
private $routes = array();
|
||||||
|
|
||||||
|
|
||||||
|
/* -( Map Info )----------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
public function setSite(AphrontSite $site) {
|
||||||
|
$this->site = $site;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSite() {
|
||||||
|
return $this->site;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setApplication(PhabricatorApplication $application) {
|
||||||
|
$this->application = $application;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getApplication() {
|
||||||
|
return $this->application;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRoutes(array $routes) {
|
||||||
|
$this->routes = $routes;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRoutes() {
|
||||||
|
return $this->routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( Routing )------------------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the route matching a path, if one exists.
|
||||||
|
*
|
||||||
|
* @param string Path to route.
|
||||||
|
* @return AphrontRoutingResult|null Routing result, if path matches map.
|
||||||
|
* @task routing
|
||||||
|
*/
|
||||||
|
public function routePath($path) {
|
||||||
|
$map = $this->getRoutes();
|
||||||
|
|
||||||
|
foreach ($map as $route => $value) {
|
||||||
|
$match = $this->tryRoute($route, $value, $path);
|
||||||
|
if (!$match) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->newRoutingResult();
|
||||||
|
$application = $result->getApplication();
|
||||||
|
|
||||||
|
$controller_class = $match['class'];
|
||||||
|
$controller = newv($controller_class, array());
|
||||||
|
$controller->setCurrentApplication($application);
|
||||||
|
|
||||||
|
$result
|
||||||
|
->setController($controller)
|
||||||
|
->setURIData($match['data']);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test a sub-map to see if any routes match a path.
|
||||||
|
*
|
||||||
|
* @param string Path to route.
|
||||||
|
* @param string Pattern from the map.
|
||||||
|
* @param string Value from the map.
|
||||||
|
* @return dict<string, wild>|null Match details, if path matches sub-map.
|
||||||
|
* @task routing
|
||||||
|
*/
|
||||||
|
private function tryRoute($route, $value, $path) {
|
||||||
|
$has_submap = is_array($value);
|
||||||
|
|
||||||
|
if (!$has_submap) {
|
||||||
|
// If the value is a controller rather than a sub-map, any matching
|
||||||
|
// route must completely consume the path.
|
||||||
|
$pattern = '(^'.$route.'\z)';
|
||||||
|
} else {
|
||||||
|
$pattern = '(^'.$route.')';
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = null;
|
||||||
|
$ok = preg_match($pattern, $path, $data);
|
||||||
|
if ($ok === false) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Routing fragment "%s" is not a valid regular expression.',
|
||||||
|
$route));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$ok) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$path_match = $data[0];
|
||||||
|
|
||||||
|
// Clean up the data. We only want to retain named capturing groups, not
|
||||||
|
// the duplicated numeric captures.
|
||||||
|
foreach ($data as $k => $v) {
|
||||||
|
if (is_numeric($k)) {
|
||||||
|
unset($data[$k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$has_submap) {
|
||||||
|
return array(
|
||||||
|
'class' => $value,
|
||||||
|
'data' => $data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sub_path = substr($path, strlen($path_match));
|
||||||
|
foreach ($value as $sub_route => $sub_value) {
|
||||||
|
$result = $this->tryRoute($sub_route, $sub_value, $sub_path);
|
||||||
|
if ($result) {
|
||||||
|
$result['data'] += $data;
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a new routing result for this map.
|
||||||
|
*
|
||||||
|
* @return AphrontRoutingResult New, empty routing result.
|
||||||
|
* @task routing
|
||||||
|
*/
|
||||||
|
private function newRoutingResult() {
|
||||||
|
return id(new AphrontRoutingResult())
|
||||||
|
->setSite($this->getSite())
|
||||||
|
->setApplication($this->getApplication());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
55
src/aphront/site/AphrontRoutingResult.php
Normal file
55
src/aphront/site/AphrontRoutingResult.php
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Details about a routing map match for a path.
|
||||||
|
*
|
||||||
|
* @param info Result Information
|
||||||
|
*/
|
||||||
|
final class AphrontRoutingResult extends Phobject {
|
||||||
|
|
||||||
|
private $site;
|
||||||
|
private $application;
|
||||||
|
private $controller;
|
||||||
|
private $uriData;
|
||||||
|
|
||||||
|
|
||||||
|
/* -( Result Information )------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
public function setSite(AphrontSite $site) {
|
||||||
|
$this->site = $site;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSite() {
|
||||||
|
return $this->site;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setApplication(PhabricatorApplication $application) {
|
||||||
|
$this->application = $application;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getApplication() {
|
||||||
|
return $this->application;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setController(AphrontController $controller) {
|
||||||
|
$this->controller = $controller;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getController() {
|
||||||
|
return $this->controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setURIData(array $uri_data) {
|
||||||
|
$this->uriData = $uri_data;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getURIData() {
|
||||||
|
return $this->uriData;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -7,14 +7,7 @@ abstract class AphrontSite extends Phobject {
|
||||||
|
|
||||||
abstract public function shouldRequireHTTPS();
|
abstract public function shouldRequireHTTPS();
|
||||||
abstract public function newSiteForRequest(AphrontRequest $request);
|
abstract public function newSiteForRequest(AphrontRequest $request);
|
||||||
|
abstract public function getRoutingMaps();
|
||||||
/**
|
|
||||||
* NOTE: This is temporary glue; eventually, sites will return an entire
|
|
||||||
* route map.
|
|
||||||
*/
|
|
||||||
public function getPathForRouting(AphrontRequest $request) {
|
|
||||||
return $request->getPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function isHostMatch($host, array $uris) {
|
protected function isHostMatch($host, array $uris) {
|
||||||
foreach ($uris as $uri) {
|
foreach ($uris as $uri) {
|
||||||
|
@ -32,14 +25,9 @@ abstract class AphrontSite extends Phobject {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function isPathPrefixMatch($path, array $paths) {
|
protected function newRoutingMap() {
|
||||||
foreach ($paths as $candidate) {
|
return id(new AphrontRoutingMap())
|
||||||
if (strncmp($path, $candidate, strlen($candidate)) === 0) {
|
->setSite($this);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final public static function getAllSites() {
|
final public static function getAllSites() {
|
||||||
|
|
|
@ -37,4 +37,17 @@ final class PhabricatorPlatformSite extends PhabricatorSite {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRoutingMaps() {
|
||||||
|
$applications = PhabricatorApplication::getAllInstalledApplications();
|
||||||
|
|
||||||
|
$maps = array();
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
$maps[] = $this->newRoutingMap()
|
||||||
|
->setApplication($application)
|
||||||
|
->setRoutes($application->getRoutes());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $maps;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,20 +22,20 @@ final class PhabricatorResourceSite extends PhabricatorSite {
|
||||||
return new PhabricatorResourceSite();
|
return new PhabricatorResourceSite();
|
||||||
}
|
}
|
||||||
|
|
||||||
// These are CDN routes, so we let them through even if the "Host" header
|
|
||||||
// doesn't match anything we recognize. The
|
|
||||||
$whitelist = array(
|
|
||||||
'/res/',
|
|
||||||
'/file/data/',
|
|
||||||
'/file/xform/',
|
|
||||||
);
|
|
||||||
|
|
||||||
$path = $request->getPath();
|
|
||||||
if ($this->isPathPrefixMatch($path, $whitelist)) {
|
|
||||||
return new PhabricatorResourceSite();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRoutingMaps() {
|
||||||
|
$applications = PhabricatorApplication::getAllInstalledApplications();
|
||||||
|
|
||||||
|
$maps = array();
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
$maps[] = $this->newRoutingMap()
|
||||||
|
->setApplication($application)
|
||||||
|
->setRoutes($application->getResourceRoutes());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $maps;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,9 +70,8 @@ final class PhabricatorAuditApplication extends PhabricatorApplication {
|
||||||
|
|
||||||
$query = id(new DiffusionCommitQuery())
|
$query = id(new DiffusionCommitQuery())
|
||||||
->setViewer($user)
|
->setViewer($user)
|
||||||
->withAuditorPHIDs($phids)
|
->withNeedsAuditByPHIDs($phids)
|
||||||
->withAuditStatus(DiffusionCommitQuery::AUDIT_STATUS_OPEN)
|
->withAuditStatus(DiffusionCommitQuery::AUDIT_STATUS_OPEN)
|
||||||
->withAuditAwaitingUser($user)
|
|
||||||
->setLimit(self::MAX_STATUS_ITEMS);
|
->setLimit(self::MAX_STATUS_ITEMS);
|
||||||
$commits = $query->execute();
|
$commits = $query->execute();
|
||||||
|
|
||||||
|
|
|
@ -11,103 +11,65 @@ final class PhabricatorCommitSearchEngine
|
||||||
return 'PhabricatorDiffusionApplication';
|
return 'PhabricatorDiffusionApplication';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildSavedQueryFromRequest(AphrontRequest $request) {
|
public function newQuery() {
|
||||||
$saved = new PhabricatorSavedQuery();
|
return id(new DiffusionCommitQuery())
|
||||||
|
|
||||||
$saved->setParameter(
|
|
||||||
'auditorPHIDs',
|
|
||||||
$this->readPHIDsFromRequest($request, 'auditorPHIDs'));
|
|
||||||
|
|
||||||
$saved->setParameter(
|
|
||||||
'commitAuthorPHIDs',
|
|
||||||
$this->readUsersFromRequest($request, 'authors'));
|
|
||||||
|
|
||||||
$saved->setParameter(
|
|
||||||
'auditStatus',
|
|
||||||
$request->getStr('auditStatus'));
|
|
||||||
|
|
||||||
$saved->setParameter(
|
|
||||||
'repositoryPHIDs',
|
|
||||||
$this->readPHIDsFromRequest($request, 'repositoryPHIDs'));
|
|
||||||
|
|
||||||
// -- TODO - T4173 - file location
|
|
||||||
|
|
||||||
return $saved;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
|
|
||||||
$query = id(new DiffusionCommitQuery())
|
|
||||||
->needAuditRequests(true)
|
->needAuditRequests(true)
|
||||||
->needCommitData(true);
|
->needCommitData(true);
|
||||||
|
}
|
||||||
|
|
||||||
$auditor_phids = $saved->getParameter('auditorPHIDs', array());
|
protected function buildQueryFromParameters(array $map) {
|
||||||
if ($auditor_phids) {
|
$query = $this->newQuery();
|
||||||
$query->withAuditorPHIDs($auditor_phids);
|
|
||||||
|
if ($map['needsAuditByPHIDs']) {
|
||||||
|
$query->withNeedsAuditByPHIDs($map['needsAuditByPHIDs']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$commit_author_phids = $saved->getParameter('commitAuthorPHIDs', array());
|
if ($map['auditorPHIDs']) {
|
||||||
if ($commit_author_phids) {
|
$query->withAuditorPHIDs($map['auditorPHIDs']);
|
||||||
$query->withAuthorPHIDs($commit_author_phids);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$audit_status = $saved->getParameter('auditStatus', null);
|
if ($map['commitAuthorPHIDs']) {
|
||||||
if ($audit_status) {
|
$query->withAuthorPHIDs($map['commitAuthorPHIDs']);
|
||||||
$query->withAuditStatus($audit_status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$awaiting_user_phid = $saved->getParameter('awaitingUserPHID', null);
|
if ($map['auditStatus']) {
|
||||||
if ($awaiting_user_phid) {
|
$query->withAuditStatus($map['auditStatus']);
|
||||||
// This is used only for the built-in "needs attention" filter,
|
|
||||||
// so cheat and just use the already-loaded viewer rather than reloading
|
|
||||||
// it.
|
|
||||||
$query->withAuditAwaitingUser($this->requireViewer());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$repository_phids = $saved->getParameter('repositoryPHIDs', array());
|
if ($map['repositoryPHIDs']) {
|
||||||
if ($repository_phids) {
|
$query->withRepositoryPHIDs($map['repositoryPHIDs']);
|
||||||
$query->withRepositoryPHIDs($repository_phids);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildSearchForm(
|
protected function buildCustomSearchFields() {
|
||||||
AphrontFormView $form,
|
return array(
|
||||||
PhabricatorSavedQuery $saved) {
|
id(new PhabricatorSearchDatasourceField())
|
||||||
|
->setLabel(pht('Needs Audit By'))
|
||||||
$auditor_phids = $saved->getParameter('auditorPHIDs', array());
|
->setKey('needsAuditByPHIDs')
|
||||||
$commit_author_phids = $saved->getParameter(
|
->setAliases(array('needs', 'need'))
|
||||||
'commitAuthorPHIDs',
|
->setDatasource(new DiffusionAuditorFunctionDatasource()),
|
||||||
array());
|
id(new PhabricatorSearchDatasourceField())
|
||||||
$audit_status = $saved->getParameter('auditStatus', null);
|
->setLabel(pht('Auditors'))
|
||||||
$repository_phids = $saved->getParameter('repositoryPHIDs', array());
|
->setKey('auditorPHIDs')
|
||||||
|
->setAliases(array('auditor', 'auditors'))
|
||||||
$form
|
->setDatasource(new DiffusionAuditorFunctionDatasource()),
|
||||||
->appendControl(
|
id(new PhabricatorUsersSearchField())
|
||||||
id(new AphrontFormTokenizerControl())
|
->setLabel(pht('Authors'))
|
||||||
->setDatasource(new DiffusionAuditorDatasource())
|
->setKey('commitAuthorPHIDs')
|
||||||
->setName('auditorPHIDs')
|
->setAliases(array('author', 'authors')),
|
||||||
->setLabel(pht('Auditors'))
|
id(new PhabricatorSearchSelectField())
|
||||||
->setValue($auditor_phids))
|
->setLabel(pht('Audit Status'))
|
||||||
->appendControl(
|
->setKey('auditStatus')
|
||||||
id(new AphrontFormTokenizerControl())
|
->setAliases(array('status'))
|
||||||
->setDatasource(new PhabricatorPeopleDatasource())
|
->setOptions($this->getAuditStatusOptions()),
|
||||||
->setName('authors')
|
id(new PhabricatorSearchDatasourceField())
|
||||||
->setLabel(pht('Commit Authors'))
|
->setLabel(pht('Repositories'))
|
||||||
->setValue($commit_author_phids))
|
->setKey('repositoryPHIDs')
|
||||||
->appendChild(
|
->setAliases(array('repository', 'repositories'))
|
||||||
id(new AphrontFormSelectControl())
|
->setDatasource(new DiffusionRepositoryDatasource()),
|
||||||
->setName('auditStatus')
|
);
|
||||||
->setLabel(pht('Audit Status'))
|
|
||||||
->setOptions($this->getAuditStatusOptions())
|
|
||||||
->setValue($audit_status))
|
|
||||||
->appendControl(
|
|
||||||
id(new AphrontFormTokenizerControl())
|
|
||||||
->setLabel(pht('Repositories'))
|
|
||||||
->setName('repositoryPHIDs')
|
|
||||||
->setDatasource(new DiffusionRepositoryDatasource())
|
|
||||||
->setValue($repository_phids));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getURI($path) {
|
protected function getURI($path) {
|
||||||
|
@ -118,7 +80,7 @@ final class PhabricatorCommitSearchEngine
|
||||||
$names = array();
|
$names = array();
|
||||||
|
|
||||||
if ($this->requireViewer()->isLoggedIn()) {
|
if ($this->requireViewer()->isLoggedIn()) {
|
||||||
$names['need'] = pht('Need Attention');
|
$names['need'] = pht('Needs Audit');
|
||||||
$names['problem'] = pht('Problem Commits');
|
$names['problem'] = pht('Problem Commits');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,22 +100,24 @@ final class PhabricatorCommitSearchEngine
|
||||||
$query->setQueryKey($query_key);
|
$query->setQueryKey($query_key);
|
||||||
$viewer = $this->requireViewer();
|
$viewer = $this->requireViewer();
|
||||||
|
|
||||||
|
$viewer_phid = $viewer->getPHID();
|
||||||
|
$status_open = DiffusionCommitQuery::AUDIT_STATUS_OPEN;
|
||||||
|
|
||||||
switch ($query_key) {
|
switch ($query_key) {
|
||||||
case 'all':
|
case 'all':
|
||||||
return $query;
|
return $query;
|
||||||
case 'open':
|
case 'open':
|
||||||
$query->setParameter(
|
$query->setParameter('auditStatus', $status_open);
|
||||||
'auditStatus',
|
|
||||||
DiffusionCommitQuery::AUDIT_STATUS_OPEN);
|
|
||||||
return $query;
|
return $query;
|
||||||
case 'need':
|
case 'need':
|
||||||
$query->setParameter('awaitingUserPHID', $viewer->getPHID());
|
$needs_tokens = array(
|
||||||
$query->setParameter(
|
$viewer_phid,
|
||||||
'auditStatus',
|
'projects('.$viewer_phid.')',
|
||||||
DiffusionCommitQuery::AUDIT_STATUS_OPEN);
|
'packages('.$viewer_phid.')',
|
||||||
$query->setParameter(
|
);
|
||||||
'auditorPHIDs',
|
|
||||||
PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($viewer));
|
$query->setParameter('needsAuditByPHIDs', $needs_tokens);
|
||||||
|
$query->setParameter('auditStatus', $status_open);
|
||||||
return $query;
|
return $query;
|
||||||
case 'authored':
|
case 'authored':
|
||||||
$query->setParameter('commitAuthorPHIDs', array($viewer->getPHID()));
|
$query->setParameter('commitAuthorPHIDs', array($viewer->getPHID()));
|
||||||
|
|
|
@ -209,7 +209,7 @@ abstract class PhabricatorAuthController extends PhabricatorController {
|
||||||
|
|
||||||
$actual = $account->getProperty('registrationKey');
|
$actual = $account->getProperty('registrationKey');
|
||||||
$expect = PhabricatorHash::digest($registration_key);
|
$expect = PhabricatorHash::digest($registration_key);
|
||||||
if ($actual !== $expect) {
|
if (!phutil_hashes_are_identical($actual, $expect)) {
|
||||||
$response = $this->renderError(
|
$response = $this->renderError(
|
||||||
pht(
|
pht(
|
||||||
'Your browser submitted a different registration key than the one '.
|
'Your browser submitted a different registration key than the one '.
|
||||||
|
|
|
@ -163,8 +163,22 @@ final class PhabricatorAuthStartController
|
||||||
$button_columns);
|
$button_columns);
|
||||||
}
|
}
|
||||||
|
|
||||||
$login_message = PhabricatorEnv::getEnvConfig('auth.login-message');
|
$handlers = PhabricatorAuthLoginHandler::getAllHandlers();
|
||||||
$login_message = phutil_safe_html($login_message);
|
|
||||||
|
$delegating_controller = $this->getDelegatingController();
|
||||||
|
|
||||||
|
$header = array();
|
||||||
|
foreach ($handlers as $handler) {
|
||||||
|
$handler = clone $handler;
|
||||||
|
|
||||||
|
$handler->setRequest($request);
|
||||||
|
|
||||||
|
if ($delegating_controller) {
|
||||||
|
$handler->setDelegatingController($delegating_controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
$header[] = $handler->getAuthLoginHeaderContent();
|
||||||
|
}
|
||||||
|
|
||||||
$invite_message = null;
|
$invite_message = null;
|
||||||
if ($invite) {
|
if ($invite) {
|
||||||
|
@ -178,7 +192,7 @@ final class PhabricatorAuthStartController
|
||||||
return $this->buildApplicationPage(
|
return $this->buildApplicationPage(
|
||||||
array(
|
array(
|
||||||
$crumbs,
|
$crumbs,
|
||||||
$login_message,
|
$header,
|
||||||
$invite_message,
|
$invite_message,
|
||||||
$out,
|
$out,
|
||||||
),
|
),
|
||||||
|
|
|
@ -21,7 +21,10 @@ final class PhabricatorAuthTerminateSessionController
|
||||||
|
|
||||||
$sessions = $query->execute();
|
$sessions = $query->execute();
|
||||||
foreach ($sessions as $key => $session) {
|
foreach ($sessions as $key => $session) {
|
||||||
if ($session->getSessionKey() == $current_key) {
|
$is_current = phutil_hashes_are_identical(
|
||||||
|
$session->getSessionKey(),
|
||||||
|
$current_key);
|
||||||
|
if ($is_current) {
|
||||||
// Don't terminate the current login session.
|
// Don't terminate the current login session.
|
||||||
unset($sessions[$key]);
|
unset($sessions[$key]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -296,7 +296,10 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||||
|
|
||||||
foreach ($sessions as $key => $session) {
|
foreach ($sessions as $key => $session) {
|
||||||
if ($except_session !== null) {
|
if ($except_session !== null) {
|
||||||
if ($except_session == $session->getSessionKey()) {
|
$is_except = phutil_hashes_are_identical(
|
||||||
|
$session->getSessionKey(),
|
||||||
|
$except_session);
|
||||||
|
if ($is_except) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,7 +201,7 @@ final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor {
|
||||||
// case the server or client has some clock skew.
|
// case the server or client has some clock skew.
|
||||||
for ($offset = -2; $offset <= 2; $offset++) {
|
for ($offset = -2; $offset <= 2; $offset++) {
|
||||||
$real = self::getTOTPCode($key, $now + $offset);
|
$real = self::getTOTPCode($key, $now + $offset);
|
||||||
if ($real === $code) {
|
if (phutil_hashes_are_identical($real, $code)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class PhabricatorAuthLoginHandler
|
||||||
|
extends Phobject {
|
||||||
|
|
||||||
|
private $request;
|
||||||
|
private $delegatingController;
|
||||||
|
|
||||||
|
public function getAuthLoginHeaderContent() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function setDelegatingController(AphrontController $controller) {
|
||||||
|
$this->delegatingController = $controller;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function getDelegatingController() {
|
||||||
|
return $this->delegatingController;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function setRequest(AphrontRequest $request) {
|
||||||
|
$this->request = $request;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function getRequest() {
|
||||||
|
return $this->request;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public static function getAllHandlers() {
|
||||||
|
return id(new PhutilClassMapQuery())
|
||||||
|
->setAncestorClass(__CLASS__)
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
}
|
|
@ -482,7 +482,7 @@ abstract class PhabricatorAuthProvider extends Phobject {
|
||||||
'problem persists, you may need to clear your cookies.'));
|
'problem persists, you may need to clear your cookies.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($actual !== $expect) {
|
if (!phutil_hashes_are_identical($actual, $expect)) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht(
|
pht(
|
||||||
'The authentication provider did not return the correct client '.
|
'The authentication provider did not return the correct client '.
|
||||||
|
|
|
@ -243,6 +243,10 @@ abstract class PhabricatorApplication
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getResourceRoutes() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Email Integration )-------------------------------------------------- */
|
/* -( Email Integration )-------------------------------------------------- */
|
||||||
|
|
||||||
|
|
|
@ -370,28 +370,8 @@ abstract class PhabricatorController extends AphrontController {
|
||||||
return $this->buildPageResponse($page);
|
return $this->buildPageResponse($page);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function didProcessRequest($response) {
|
public function willSendResponse(AphrontResponse $response) {
|
||||||
// If a bare DialogView is returned, wrap it in a DialogResponse.
|
|
||||||
if ($response instanceof AphrontDialogView) {
|
|
||||||
$response = id(new AphrontDialogResponse())->setDialog($response);
|
|
||||||
}
|
|
||||||
|
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
$response->setRequest($request);
|
|
||||||
|
|
||||||
$seen = array();
|
|
||||||
while ($response instanceof AphrontProxyResponse) {
|
|
||||||
$hash = spl_object_hash($response);
|
|
||||||
if (isset($seen[$hash])) {
|
|
||||||
$seen[] = get_class($response);
|
|
||||||
throw new Exception(
|
|
||||||
pht('Cycle while reducing proxy responses: %s',
|
|
||||||
implode(' -> ', $seen)));
|
|
||||||
}
|
|
||||||
$seen[$hash] = get_class($response);
|
|
||||||
|
|
||||||
$response = $response->reduceProxyResponse();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($response instanceof AphrontDialogResponse) {
|
if ($response instanceof AphrontDialogResponse) {
|
||||||
if (!$request->isAjax() && !$request->isQuicksand()) {
|
if (!$request->isAjax() && !$request->isQuicksand()) {
|
||||||
|
|
|
@ -15,6 +15,17 @@ final class PhabricatorCelerityApplication extends PhabricatorApplication {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRoutes() {
|
public function getRoutes() {
|
||||||
|
// We serve resources from both the platform site and the resource site.
|
||||||
|
// This is safe because the user doesn't have any direct control over
|
||||||
|
// resources.
|
||||||
|
|
||||||
|
// The advantage of serving resources from the resource site (if possible)
|
||||||
|
// is that we can use a CDN there if one is configured, but there is no
|
||||||
|
// particular security concern.
|
||||||
|
return $this->getResourceRoutes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResourceRoutes() {
|
||||||
$extensions = CelerityResourceController::getSupportedResourceTypes();
|
$extensions = CelerityResourceController::getSupportedResourceTypes();
|
||||||
$extensions = array_keys($extensions);
|
$extensions = array_keys($extensions);
|
||||||
$extensions = implode('|', $extensions);
|
$extensions = implode('|', $extensions);
|
||||||
|
|
|
@ -434,7 +434,8 @@ final class PhabricatorConduitAPIController
|
||||||
$token = idx($metadata, 'authToken');
|
$token = idx($metadata, 'authToken');
|
||||||
$signature = idx($metadata, 'authSignature');
|
$signature = idx($metadata, 'authSignature');
|
||||||
$certificate = $user->getConduitCertificate();
|
$certificate = $user->getConduitCertificate();
|
||||||
if (sha1($token.$certificate) !== $signature) {
|
$hash = sha1($token.$certificate);
|
||||||
|
if (!phutil_hashes_are_identical($hash, $signature)) {
|
||||||
return array(
|
return array(
|
||||||
'ERR-INVALID-AUTH',
|
'ERR-INVALID-AUTH',
|
||||||
pht('Authentication is invalid.'),
|
pht('Authentication is invalid.'),
|
||||||
|
|
|
@ -142,7 +142,7 @@ final class ConduitConnectConduitAPIMethod extends ConduitAPIMethod {
|
||||||
$threshold));
|
$threshold));
|
||||||
}
|
}
|
||||||
$valid = sha1($token.$user->getConduitCertificate());
|
$valid = sha1($token.$user->getConduitCertificate());
|
||||||
if ($valid != $signature) {
|
if (!phutil_hashes_are_identical($valid, $signature)) {
|
||||||
throw new ConduitException('ERR-INVALID-CERTIFICATE');
|
throw new ConduitException('ERR-INVALID-CERTIFICATE');
|
||||||
}
|
}
|
||||||
$session_key = id(new PhabricatorAuthSessionEngine())->establishSession(
|
$session_key = id(new PhabricatorAuthSessionEngine())->establishSession(
|
||||||
|
|
|
@ -276,6 +276,10 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
|
||||||
'Impersonating users over the API is no longer supported.'),
|
'Impersonating users over the API is no longer supported.'),
|
||||||
|
|
||||||
'feed.public' => pht('The framable public feed is no longer supported.'),
|
'feed.public' => pht('The framable public feed is no longer supported.'),
|
||||||
|
|
||||||
|
'auth.login-message' => pht(
|
||||||
|
'This configuration option has been replaced with a modular '.
|
||||||
|
'handler. See T9346.'),
|
||||||
);
|
);
|
||||||
|
|
||||||
return $ancient_config;
|
return $ancient_config;
|
||||||
|
|
|
@ -122,7 +122,7 @@ final class PhabricatorConfigEditController
|
||||||
->appendChild(phutil_tag('p', array(), $msg));
|
->appendChild(phutil_tag('p', array(), $msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($option->getHidden()) {
|
if ($option->getHidden() || $option->getLocked()) {
|
||||||
$control = null;
|
$control = null;
|
||||||
} else {
|
} else {
|
||||||
$control = $this->renderControl(
|
$control = $this->renderControl(
|
||||||
|
@ -164,14 +164,20 @@ final class PhabricatorConfigEditController
|
||||||
$form
|
$form
|
||||||
->appendChild($control);
|
->appendChild($control);
|
||||||
|
|
||||||
$submit_control = id(new AphrontFormSubmitControl())
|
|
||||||
->addCancelButton($done_uri);
|
|
||||||
|
|
||||||
if (!$option->getLocked()) {
|
if (!$option->getLocked()) {
|
||||||
$submit_control->setValue(pht('Save Config Entry'));
|
$form->appendChild(
|
||||||
|
id(new AphrontFormSubmitControl())
|
||||||
|
->addCancelButton($done_uri)
|
||||||
|
->setValue(pht('Save Config Entry')));
|
||||||
}
|
}
|
||||||
|
|
||||||
$form->appendChild($submit_control);
|
if (!$option->getHidden()) {
|
||||||
|
$form->appendChild(
|
||||||
|
id(new AphrontFormMarkupControl())
|
||||||
|
->setLabel(pht('Current Configuration'))
|
||||||
|
->setValue($this->renderDefaults($option, $config_entry)));
|
||||||
|
}
|
||||||
|
|
||||||
$examples = $this->renderExamples($option);
|
$examples = $this->renderExamples($option);
|
||||||
if ($examples) {
|
if ($examples) {
|
||||||
|
@ -181,13 +187,6 @@ final class PhabricatorConfigEditController
|
||||||
->setValue($examples));
|
->setValue($examples));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$option->getHidden()) {
|
|
||||||
$form->appendChild(
|
|
||||||
id(new AphrontFormMarkupControl())
|
|
||||||
->setLabel(pht('Default'))
|
|
||||||
->setValue($this->renderDefaults($option, $config_entry)));
|
|
||||||
}
|
|
||||||
|
|
||||||
$title = pht('Edit %s', $key);
|
$title = pht('Edit %s', $key);
|
||||||
$short = pht('Edit');
|
$short = pht('Edit');
|
||||||
|
|
||||||
|
@ -438,16 +437,12 @@ final class PhabricatorConfigEditController
|
||||||
}
|
}
|
||||||
|
|
||||||
$control
|
$control
|
||||||
->setLabel(pht('Value'))
|
->setLabel(pht('Database Value'))
|
||||||
->setError($e_value)
|
->setError($e_value)
|
||||||
->setValue($display_value)
|
->setValue($display_value)
|
||||||
->setName('value');
|
->setName('value');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($option->getLocked()) {
|
|
||||||
$control->setDisabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $control;
|
return $control;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,25 +496,41 @@ final class PhabricatorConfigEditController
|
||||||
phutil_tag('th', array(), pht('Source')),
|
phutil_tag('th', array(), pht('Source')),
|
||||||
phutil_tag('th', array(), pht('Value')),
|
phutil_tag('th', array(), pht('Value')),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
$is_effective_value = true;
|
||||||
foreach ($stack as $key => $source) {
|
foreach ($stack as $key => $source) {
|
||||||
|
$row_classes = array(
|
||||||
|
'column-labels',
|
||||||
|
);
|
||||||
|
|
||||||
$value = $source->getKeys(
|
$value = $source->getKeys(
|
||||||
array(
|
array(
|
||||||
$option->getKey(),
|
$option->getKey(),
|
||||||
));
|
));
|
||||||
|
|
||||||
if (!array_key_exists($option->getKey(), $value)) {
|
if (!array_key_exists($option->getKey(), $value)) {
|
||||||
$value = phutil_tag('em', array(), pht('(empty)'));
|
$value = phutil_tag('em', array(), pht('(No Value Configured)'));
|
||||||
} else {
|
} else {
|
||||||
$value = $this->getDisplayValue(
|
$value = $this->getDisplayValue(
|
||||||
$option,
|
$option,
|
||||||
$entry,
|
$entry,
|
||||||
$value[$option->getKey()]);
|
$value[$option->getKey()]);
|
||||||
|
|
||||||
|
if ($is_effective_value) {
|
||||||
|
$is_effective_value = false;
|
||||||
|
$row_classes[] = 'config-options-effective-value';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$table[] = phutil_tag('tr', array('class' => 'column-labels'), array(
|
$table[] = phutil_tag(
|
||||||
phutil_tag('th', array(), $source->getName()),
|
'tr',
|
||||||
phutil_tag('td', array(), $value),
|
array(
|
||||||
));
|
'class' => implode(' ', $row_classes),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
phutil_tag('th', array(), $source->getName()),
|
||||||
|
phutil_tag('td', array(), $value),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
require_celerity_resource('config-options-css');
|
require_celerity_resource('config-options-css');
|
||||||
|
|
|
@ -41,7 +41,7 @@ final class PhabricatorConfigEdgeModule extends PhabricatorConfigModule {
|
||||||
|
|
||||||
return id(new PHUIObjectBoxView())
|
return id(new PHUIObjectBoxView())
|
||||||
->setHeaderText(pht('Edge Types'))
|
->setHeaderText(pht('Edge Types'))
|
||||||
->appendChild($table);
|
->setTable($table);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ final class PhabricatorConfigPHIDModule extends PhabricatorConfigModule {
|
||||||
|
|
||||||
return id(new PHUIObjectBoxView())
|
return id(new PHUIObjectBoxView())
|
||||||
->setHeaderText(pht('PHID Types'))
|
->setHeaderText(pht('PHID Types'))
|
||||||
->appendChild($table);
|
->setTable($table);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorConfigRequestExceptionHandlerModule
|
||||||
|
extends PhabricatorConfigModule {
|
||||||
|
|
||||||
|
public function getModuleKey() {
|
||||||
|
return 'exception-handler';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getModuleName() {
|
||||||
|
return pht('Exception Handlers');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderModuleStatus(AphrontRequest $request) {
|
||||||
|
$viewer = $request->getViewer();
|
||||||
|
|
||||||
|
$handlers = AphrontRequestExceptionHandler::getAllHandlers();
|
||||||
|
|
||||||
|
$rows = array();
|
||||||
|
foreach ($handlers as $key => $handler) {
|
||||||
|
$rows[] = array(
|
||||||
|
$handler->getRequestExceptionHandlerPriority(),
|
||||||
|
$key,
|
||||||
|
$handler->getRequestExceptionHandlerDescription(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = id(new AphrontTableView($rows))
|
||||||
|
->setHeaders(
|
||||||
|
array(
|
||||||
|
pht('Priority'),
|
||||||
|
pht('Class'),
|
||||||
|
pht('Description'),
|
||||||
|
))
|
||||||
|
->setColumnClasses(
|
||||||
|
array(
|
||||||
|
null,
|
||||||
|
'pri',
|
||||||
|
'wide',
|
||||||
|
));
|
||||||
|
|
||||||
|
return id(new PHUIObjectBoxView())
|
||||||
|
->setHeaderText(pht('Exception Handlers'))
|
||||||
|
->setTable($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -40,7 +40,7 @@ final class PhabricatorConfigSiteModule extends PhabricatorConfigModule {
|
||||||
|
|
||||||
return id(new PHUIObjectBoxView())
|
return id(new PHUIObjectBoxView())
|
||||||
->setHeaderText(pht('Sites'))
|
->setHeaderText(pht('Sites'))
|
||||||
->appendChild($table);
|
->setTable($table);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,14 +73,6 @@ final class PhabricatorAuthenticationConfigOptions
|
||||||
->addExample(
|
->addExample(
|
||||||
"yourcompany.com\nmail.yourcompany.com",
|
"yourcompany.com\nmail.yourcompany.com",
|
||||||
pht('Valid Setting')),
|
pht('Valid Setting')),
|
||||||
$this->newOption('auth.login-message', 'string', null)
|
|
||||||
->setLocked(true)
|
|
||||||
->setSummary(pht('A block of HTML displayed on the login screen.'))
|
|
||||||
->setDescription(
|
|
||||||
pht(
|
|
||||||
"You can provide an arbitrary block of HTML here, which will ".
|
|
||||||
"appear on the login screen. Normally, you'd use this to provide ".
|
|
||||||
"login or registration instructions to users.")),
|
|
||||||
$this->newOption('account.editable', 'bool', true)
|
$this->newOption('account.editable', 'bool', true)
|
||||||
->setBoolOptions(
|
->setBoolOptions(
|
||||||
array(
|
array(
|
||||||
|
|
|
@ -76,9 +76,9 @@ final class PhabricatorConfigOption
|
||||||
}
|
}
|
||||||
return pht(
|
return pht(
|
||||||
'This configuration is locked and can not be edited from the web '.
|
'This configuration is locked and can not be edited from the web '.
|
||||||
'interface. Use `%s` in `%s` to edit it.',
|
'interface. Use %s in %s to edit it.',
|
||||||
'./bin/config',
|
phutil_tag('tt', array(), './bin/config'),
|
||||||
'phabricator/');
|
phutil_tag('tt', array(), 'phabricator/'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addExample($value, $description) {
|
public function addExample($value, $description) {
|
||||||
|
|
|
@ -90,6 +90,11 @@ final class ConpherenceLayoutView extends AphrontView {
|
||||||
'hasWidgets' => false,
|
'hasWidgets' => false,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
$class = null;
|
||||||
|
if (!$this->getUser()->isLoggedIn()) {
|
||||||
|
$class = 'conpherence-logged-out';
|
||||||
|
}
|
||||||
|
|
||||||
$this->initBehavior(
|
$this->initBehavior(
|
||||||
'conpherence-widget-pane',
|
'conpherence-widget-pane',
|
||||||
ConpherenceWidgetConfigConstants::getWidgetPaneBehaviorConfig());
|
ConpherenceWidgetConfigConstants::getWidgetPaneBehaviorConfig());
|
||||||
|
@ -99,7 +104,9 @@ final class ConpherenceLayoutView extends AphrontView {
|
||||||
array(
|
array(
|
||||||
'id' => $layout_id,
|
'id' => $layout_id,
|
||||||
'sigil' => 'conpherence-layout',
|
'sigil' => 'conpherence-layout',
|
||||||
'class' => 'conpherence-layout conpherence-role-'.$this->role,
|
'class' => 'conpherence-layout '.
|
||||||
|
$class.
|
||||||
|
' conpherence-role-'.$this->role,
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
javelin_tag(
|
javelin_tag(
|
||||||
|
|
|
@ -14,66 +14,121 @@ final class DarkConsoleRequestPlugin extends DarkConsolePlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generateData() {
|
public function generateData() {
|
||||||
|
$addr = idx($_SERVER, 'SERVER_ADDR');
|
||||||
|
if ($addr) {
|
||||||
|
$hostname = @gethostbyaddr($addr);
|
||||||
|
} else {
|
||||||
|
$hostname = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$controller = $this->getRequest()->getController();
|
||||||
|
if ($controller) {
|
||||||
|
$controller_class = get_class($controller);
|
||||||
|
} else {
|
||||||
|
$controller_class = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$site = $this->getRequest()->getSite();
|
||||||
|
if ($site) {
|
||||||
|
$site_class = get_class($site);
|
||||||
|
} else {
|
||||||
|
$site_class = null;
|
||||||
|
}
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'Request' => $_REQUEST,
|
'request' => $_REQUEST,
|
||||||
'Server' => $_SERVER,
|
'server' => $_SERVER,
|
||||||
|
'special' => array(
|
||||||
|
'site' => $site_class,
|
||||||
|
'controller' => $controller_class,
|
||||||
|
'machine' => php_uname('n'),
|
||||||
|
'host' => $addr,
|
||||||
|
'hostname' => $hostname,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderPanel() {
|
public function renderPanel() {
|
||||||
$data = $this->getData();
|
$data = $this->getData();
|
||||||
|
|
||||||
$sections = array(
|
$special_map = array(
|
||||||
'Basics' => array(
|
'site' => pht('Site'),
|
||||||
'Machine' => php_uname('n'),
|
'controller' => pht('Controller'),
|
||||||
),
|
'machine' => pht('Machine'),
|
||||||
|
'host' => pht('Host'),
|
||||||
|
'hostname' => pht('Hostname'),
|
||||||
);
|
);
|
||||||
|
|
||||||
// NOTE: This may not be present for some SAPIs, like php-fpm.
|
$special = idx($data, 'special', array());
|
||||||
if (!empty($data['Server']['SERVER_ADDR'])) {
|
|
||||||
$addr = $data['Server']['SERVER_ADDR'];
|
$rows = array();
|
||||||
$sections['Basics']['Host'] = $addr;
|
foreach ($special_map as $key => $label) {
|
||||||
$sections['Basics']['Hostname'] = @gethostbyaddr($addr);
|
$rows[] = array(
|
||||||
|
$label,
|
||||||
|
idx($special, $key),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$sections = array_merge($sections, $data);
|
$sections = array();
|
||||||
|
$sections[] = array(
|
||||||
|
'name' => pht('Basics'),
|
||||||
|
'rows' => $rows,
|
||||||
|
);
|
||||||
|
|
||||||
$mask = array(
|
$mask = array(
|
||||||
'HTTP_COOKIE' => true,
|
'HTTP_COOKIE' => true,
|
||||||
'HTTP_X_PHABRICATOR_CSRF' => true,
|
'HTTP_X_PHABRICATOR_CSRF' => true,
|
||||||
);
|
);
|
||||||
|
|
||||||
$out = array();
|
$maps = array(
|
||||||
foreach ($sections as $header => $map) {
|
array(
|
||||||
|
'name' => pht('Request'),
|
||||||
|
'data' => idx($data, 'request', array()),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => pht('Server'),
|
||||||
|
'data' => idx($data, 'server', array()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($maps as $map) {
|
||||||
|
$data = $map['data'];
|
||||||
$rows = array();
|
$rows = array();
|
||||||
foreach ($map as $key => $value) {
|
foreach ($data as $key => $value) {
|
||||||
if (isset($mask[$key])) {
|
if (isset($mask[$key])) {
|
||||||
$rows[] = array(
|
$value = phutil_tag('em', array(), pht('(Masked)'));
|
||||||
$key,
|
} else if (is_array($value)) {
|
||||||
phutil_tag('em', array(), pht('(Masked)')),
|
$value = @json_encode($value);
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
$rows[] = array(
|
$value = $value;
|
||||||
$key,
|
|
||||||
(is_array($value) ? json_encode($value) : $value),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$rows[] = array(
|
||||||
|
$key,
|
||||||
|
$value,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$table = new AphrontTableView($rows);
|
$sections[] = array(
|
||||||
$table->setHeaders(
|
'name' => $map['name'],
|
||||||
array(
|
'rows' => $rows,
|
||||||
$header,
|
);
|
||||||
null,
|
|
||||||
));
|
|
||||||
$table->setColumnClasses(
|
|
||||||
array(
|
|
||||||
'header',
|
|
||||||
'wide wrap',
|
|
||||||
));
|
|
||||||
$out[] = $table->render();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return phutil_implode_html("\n", $out);
|
$out = array();
|
||||||
|
foreach ($sections as $section) {
|
||||||
|
$out[] = id(new AphrontTableView($section['rows']))
|
||||||
|
->setHeaders(
|
||||||
|
array(
|
||||||
|
$section['name'],
|
||||||
|
null,
|
||||||
|
))
|
||||||
|
->setColumnClasses(
|
||||||
|
array(
|
||||||
|
'header',
|
||||||
|
'wide wrap',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@ final class DifferentialCreateDiffConduitAPIMethod
|
||||||
'okay',
|
'okay',
|
||||||
'warn',
|
'warn',
|
||||||
'fail',
|
'fail',
|
||||||
'postponed',
|
|
||||||
));
|
));
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
|
@ -95,9 +94,6 @@ final class DifferentialCreateDiffConduitAPIMethod
|
||||||
case 'fail':
|
case 'fail':
|
||||||
$lint_status = DifferentialLintStatus::LINT_FAIL;
|
$lint_status = DifferentialLintStatus::LINT_FAIL;
|
||||||
break;
|
break;
|
||||||
case 'postponed':
|
|
||||||
$lint_status = DifferentialLintStatus::LINT_POSTPONED;
|
|
||||||
break;
|
|
||||||
case 'none':
|
case 'none':
|
||||||
default:
|
default:
|
||||||
$lint_status = DifferentialLintStatus::LINT_NONE;
|
$lint_status = DifferentialLintStatus::LINT_NONE;
|
||||||
|
@ -117,9 +113,6 @@ final class DifferentialCreateDiffConduitAPIMethod
|
||||||
case 'fail':
|
case 'fail':
|
||||||
$unit_status = DifferentialUnitStatus::UNIT_FAIL;
|
$unit_status = DifferentialUnitStatus::UNIT_FAIL;
|
||||||
break;
|
break;
|
||||||
case 'postponed':
|
|
||||||
$unit_status = DifferentialUnitStatus::UNIT_POSTPONED;
|
|
||||||
break;
|
|
||||||
case 'none':
|
case 'none':
|
||||||
default:
|
default:
|
||||||
$unit_status = DifferentialUnitStatus::UNIT_NONE;
|
$unit_status = DifferentialUnitStatus::UNIT_NONE;
|
||||||
|
|
|
@ -2,12 +2,11 @@
|
||||||
|
|
||||||
final class DifferentialLintStatus extends Phobject {
|
final class DifferentialLintStatus extends Phobject {
|
||||||
|
|
||||||
const LINT_NONE = 0;
|
const LINT_NONE = 0;
|
||||||
const LINT_OKAY = 1;
|
const LINT_OKAY = 1;
|
||||||
const LINT_WARN = 2;
|
const LINT_WARN = 2;
|
||||||
const LINT_FAIL = 3;
|
const LINT_FAIL = 3;
|
||||||
const LINT_SKIP = 4;
|
const LINT_SKIP = 4;
|
||||||
const LINT_POSTPONED = 5;
|
const LINT_AUTO_SKIP = 6;
|
||||||
const LINT_AUTO_SKIP = 6;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,11 @@
|
||||||
|
|
||||||
final class DifferentialUnitStatus extends Phobject {
|
final class DifferentialUnitStatus extends Phobject {
|
||||||
|
|
||||||
const UNIT_NONE = 0;
|
const UNIT_NONE = 0;
|
||||||
const UNIT_OKAY = 1;
|
const UNIT_OKAY = 1;
|
||||||
const UNIT_WARN = 2;
|
const UNIT_WARN = 2;
|
||||||
const UNIT_FAIL = 3;
|
const UNIT_FAIL = 3;
|
||||||
const UNIT_SKIP = 4;
|
const UNIT_SKIP = 4;
|
||||||
const UNIT_POSTPONED = 5;
|
const UNIT_AUTO_SKIP = 6;
|
||||||
const UNIT_AUTO_SKIP = 6;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,10 @@
|
||||||
|
|
||||||
final class DifferentialUnitTestResult extends Phobject {
|
final class DifferentialUnitTestResult extends Phobject {
|
||||||
|
|
||||||
const RESULT_PASS = 'pass';
|
const RESULT_PASS = 'pass';
|
||||||
const RESULT_FAIL = 'fail';
|
const RESULT_FAIL = 'fail';
|
||||||
const RESULT_SKIP = 'skip';
|
const RESULT_SKIP = 'skip';
|
||||||
const RESULT_BROKEN = 'broken';
|
const RESULT_BROKEN = 'broken';
|
||||||
const RESULT_UNSOUND = 'unsound';
|
const RESULT_UNSOUND = 'unsound';
|
||||||
const RESULT_POSTPONED = 'postponed';
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,9 +73,6 @@ final class DifferentialLintField
|
||||||
if ($status == DifferentialLintStatus::LINT_SKIP) {
|
if ($status == DifferentialLintStatus::LINT_SKIP) {
|
||||||
$warnings[] = pht(
|
$warnings[] = pht(
|
||||||
'Lint was skipped when generating these changes.');
|
'Lint was skipped when generating these changes.');
|
||||||
} else if ($status == DifferentialLintStatus::LINT_POSTPONED) {
|
|
||||||
$warnings[] = pht(
|
|
||||||
'Background linting has not finished executing on these changes.');
|
|
||||||
} else {
|
} else {
|
||||||
$warnings[] = pht('These changes have lint problems.');
|
$warnings[] = pht('These changes have lint problems.');
|
||||||
}
|
}
|
||||||
|
@ -94,7 +91,6 @@ final class DifferentialLintField
|
||||||
DifferentialLintStatus::LINT_FAIL => 'red',
|
DifferentialLintStatus::LINT_FAIL => 'red',
|
||||||
DifferentialLintStatus::LINT_SKIP => 'blue',
|
DifferentialLintStatus::LINT_SKIP => 'blue',
|
||||||
DifferentialLintStatus::LINT_AUTO_SKIP => 'blue',
|
DifferentialLintStatus::LINT_AUTO_SKIP => 'blue',
|
||||||
DifferentialLintStatus::LINT_POSTPONED => 'blue',
|
|
||||||
);
|
);
|
||||||
$icon_color = idx($colors, $diff->getLintStatus(), 'grey');
|
$icon_color = idx($colors, $diff->getLintStatus(), 'grey');
|
||||||
|
|
||||||
|
|
|
@ -84,9 +84,6 @@ final class DifferentialUnitField
|
||||||
// Don't show any warnings.
|
// Don't show any warnings.
|
||||||
} else if ($status == DifferentialUnitStatus::UNIT_AUTO_SKIP) {
|
} else if ($status == DifferentialUnitStatus::UNIT_AUTO_SKIP) {
|
||||||
// Don't show any warnings.
|
// Don't show any warnings.
|
||||||
} else if ($status == DifferentialUnitStatus::UNIT_POSTPONED) {
|
|
||||||
$warnings[] = pht(
|
|
||||||
'Background tests have not finished executing on these changes.');
|
|
||||||
} else if ($status == DifferentialUnitStatus::UNIT_SKIP) {
|
} else if ($status == DifferentialUnitStatus::UNIT_SKIP) {
|
||||||
$warnings[] = pht(
|
$warnings[] = pht(
|
||||||
'Unit tests were skipped when generating these changes.');
|
'Unit tests were skipped when generating these changes.');
|
||||||
|
@ -108,7 +105,6 @@ final class DifferentialUnitField
|
||||||
DifferentialUnitStatus::UNIT_FAIL => 'red',
|
DifferentialUnitStatus::UNIT_FAIL => 'red',
|
||||||
DifferentialUnitStatus::UNIT_SKIP => 'blue',
|
DifferentialUnitStatus::UNIT_SKIP => 'blue',
|
||||||
DifferentialUnitStatus::UNIT_AUTO_SKIP => 'blue',
|
DifferentialUnitStatus::UNIT_AUTO_SKIP => 'blue',
|
||||||
DifferentialUnitStatus::UNIT_POSTPONED => 'blue',
|
|
||||||
);
|
);
|
||||||
$icon_color = idx($colors, $diff->getUnitStatus(), 'grey');
|
$icon_color = idx($colors, $diff->getUnitStatus(), 'grey');
|
||||||
|
|
||||||
|
|
|
@ -323,7 +323,6 @@ final class DifferentialRevisionUpdateHistoryView extends AphrontView {
|
||||||
DifferentialLintStatus::LINT_FAIL => self::STAR_FAIL,
|
DifferentialLintStatus::LINT_FAIL => self::STAR_FAIL,
|
||||||
DifferentialLintStatus::LINT_SKIP => self::STAR_SKIP,
|
DifferentialLintStatus::LINT_SKIP => self::STAR_SKIP,
|
||||||
DifferentialLintStatus::LINT_AUTO_SKIP => self::STAR_SKIP,
|
DifferentialLintStatus::LINT_AUTO_SKIP => self::STAR_SKIP,
|
||||||
DifferentialLintStatus::LINT_POSTPONED => self::STAR_SKIP,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
$star = idx($map, $diff->getLintStatus(), self::STAR_FAIL);
|
$star = idx($map, $diff->getLintStatus(), self::STAR_FAIL);
|
||||||
|
@ -339,7 +338,6 @@ final class DifferentialRevisionUpdateHistoryView extends AphrontView {
|
||||||
DifferentialUnitStatus::UNIT_FAIL => self::STAR_FAIL,
|
DifferentialUnitStatus::UNIT_FAIL => self::STAR_FAIL,
|
||||||
DifferentialUnitStatus::UNIT_SKIP => self::STAR_SKIP,
|
DifferentialUnitStatus::UNIT_SKIP => self::STAR_SKIP,
|
||||||
DifferentialUnitStatus::UNIT_AUTO_SKIP => self::STAR_SKIP,
|
DifferentialUnitStatus::UNIT_AUTO_SKIP => self::STAR_SKIP,
|
||||||
DifferentialUnitStatus::UNIT_POSTPONED => self::STAR_SKIP,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
$star = idx($map, $diff->getUnitStatus(), self::STAR_FAIL);
|
$star = idx($map, $diff->getUnitStatus(), self::STAR_FAIL);
|
||||||
|
@ -361,8 +359,6 @@ final class DifferentialRevisionUpdateHistoryView extends AphrontView {
|
||||||
return pht('Lint Skipped');
|
return pht('Lint Skipped');
|
||||||
case DifferentialLintStatus::LINT_AUTO_SKIP:
|
case DifferentialLintStatus::LINT_AUTO_SKIP:
|
||||||
return pht('Automatic diff as part of commit; lint not applicable.');
|
return pht('Automatic diff as part of commit; lint not applicable.');
|
||||||
case DifferentialLintStatus::LINT_POSTPONED:
|
|
||||||
return pht('Lint Postponed');
|
|
||||||
}
|
}
|
||||||
return pht('Unknown');
|
return pht('Unknown');
|
||||||
}
|
}
|
||||||
|
@ -382,8 +378,6 @@ final class DifferentialRevisionUpdateHistoryView extends AphrontView {
|
||||||
case DifferentialUnitStatus::UNIT_AUTO_SKIP:
|
case DifferentialUnitStatus::UNIT_AUTO_SKIP:
|
||||||
return pht(
|
return pht(
|
||||||
'Automatic diff as part of commit; unit tests not applicable.');
|
'Automatic diff as part of commit; unit tests not applicable.');
|
||||||
case DifferentialUnitStatus::UNIT_POSTPONED:
|
|
||||||
return pht('Unit Tests Postponed');
|
|
||||||
}
|
}
|
||||||
return pht('Unknown');
|
return pht('Unknown');
|
||||||
}
|
}
|
||||||
|
|
|
@ -302,7 +302,16 @@ final class DiffusionRepositoryController extends DiffusionController {
|
||||||
|
|
||||||
$info = null;
|
$info = null;
|
||||||
$drequest = $this->getDiffusionRequest();
|
$drequest = $this->getDiffusionRequest();
|
||||||
if ($drequest->getRefAlternatives()) {
|
|
||||||
|
// Try to load alternatives. This may fail for repositories which have not
|
||||||
|
// cloned yet. If it does, just ignore it and continue.
|
||||||
|
try {
|
||||||
|
$alternatives = $drequest->getRefAlternatives();
|
||||||
|
} catch (ConduitClientException $ex) {
|
||||||
|
$alternatives = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($alternatives) {
|
||||||
$message = array(
|
$message = array(
|
||||||
pht(
|
pht(
|
||||||
'The ref "%s" is ambiguous in this repository.',
|
'The ref "%s" is ambiguous in this repository.',
|
||||||
|
|
|
@ -1,9 +1,3 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class DiffusionSetupException extends AphrontUsageException {
|
final class DiffusionSetupException extends Exception {}
|
||||||
|
|
||||||
public function __construct($message) {
|
|
||||||
parent::__construct(pht('Diffusion Setup Exception'), $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -168,9 +168,7 @@ final class HeraldCommitAdapter
|
||||||
'commitPHID = %s AND auditStatus IN (%Ls)',
|
'commitPHID = %s AND auditStatus IN (%Ls)',
|
||||||
$this->commit->getPHID(),
|
$this->commit->getPHID(),
|
||||||
$status_arr);
|
$status_arr);
|
||||||
|
$this->auditNeededPackages = $requests;
|
||||||
$packages = mpull($requests, 'getAuditorPHID');
|
|
||||||
$this->auditNeededPackages = $packages;
|
|
||||||
}
|
}
|
||||||
return $this->auditNeededPackages;
|
return $this->auditNeededPackages;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ final class DiffusionCommitQuery
|
||||||
private $needAuditRequests;
|
private $needAuditRequests;
|
||||||
private $auditIDs;
|
private $auditIDs;
|
||||||
private $auditorPHIDs;
|
private $auditorPHIDs;
|
||||||
private $auditAwaitingUser;
|
private $needsAuditByPHIDs;
|
||||||
private $auditStatus;
|
private $auditStatus;
|
||||||
private $epochMin;
|
private $epochMin;
|
||||||
private $epochMax;
|
private $epochMax;
|
||||||
|
@ -103,27 +103,6 @@ final class DiffusionCommitQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if we should join the audit table, either because we're
|
|
||||||
* interested in the information if it's available or because matching rows
|
|
||||||
* must always have it.
|
|
||||||
*/
|
|
||||||
private function shouldJoinAudits() {
|
|
||||||
return $this->auditStatus ||
|
|
||||||
$this->rowsMustHaveAudits();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if we should `JOIN` (vs `LEFT JOIN`) the audit table, because
|
|
||||||
* matching commits will always have audit rows.
|
|
||||||
*/
|
|
||||||
private function rowsMustHaveAudits() {
|
|
||||||
return
|
|
||||||
$this->auditIDs ||
|
|
||||||
$this->auditorPHIDs ||
|
|
||||||
$this->auditAwaitingUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function withAuditIDs(array $ids) {
|
public function withAuditIDs(array $ids) {
|
||||||
$this->auditIDs = $ids;
|
$this->auditIDs = $ids;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -134,8 +113,8 @@ final class DiffusionCommitQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function withAuditAwaitingUser(PhabricatorUser $user) {
|
public function withNeedsAuditByPHIDs(array $needs_phids) {
|
||||||
$this->auditAwaitingUser = $user;
|
$this->needsAuditByPHIDs = $needs_phids;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,21 +154,12 @@ final class DiffusionCommitQuery
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function newResultObject() {
|
||||||
|
return new PhabricatorRepositoryCommit();
|
||||||
|
}
|
||||||
|
|
||||||
protected function loadPage() {
|
protected function loadPage() {
|
||||||
$table = new PhabricatorRepositoryCommit();
|
return $this->loadStandardPage($this->newResultObject());
|
||||||
$conn_r = $table->establishConnection('r');
|
|
||||||
|
|
||||||
$data = queryfx_all(
|
|
||||||
$conn_r,
|
|
||||||
'SELECT commit.* FROM %T commit %Q %Q %Q %Q %Q',
|
|
||||||
$table->getTableName(),
|
|
||||||
$this->buildJoinClause($conn_r),
|
|
||||||
$this->buildWhereClause($conn_r),
|
|
||||||
$this->buildGroupClause($conn_r),
|
|
||||||
$this->buildOrderClause($conn_r),
|
|
||||||
$this->buildLimitClause($conn_r));
|
|
||||||
|
|
||||||
return $table->loadAllFromArray($data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function willFilterPage(array $commits) {
|
protected function willFilterPage(array $commits) {
|
||||||
|
@ -296,8 +266,8 @@ final class DiffusionCommitQuery
|
||||||
return $commits;
|
return $commits;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||||
$where = array();
|
$where = parent::buildWhereClauseParts($conn);
|
||||||
|
|
||||||
if ($this->repositoryPHIDs !== null) {
|
if ($this->repositoryPHIDs !== null) {
|
||||||
$map_repositories = id(new PhabricatorRepositoryQuery())
|
$map_repositories = id(new PhabricatorRepositoryQuery())
|
||||||
|
@ -317,42 +287,42 @@ final class DiffusionCommitQuery
|
||||||
|
|
||||||
if ($this->ids !== null) {
|
if ($this->ids !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'commit.id IN (%Ld)',
|
'commit.id IN (%Ld)',
|
||||||
$this->ids);
|
$this->ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->phids !== null) {
|
if ($this->phids !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'commit.phid IN (%Ls)',
|
'commit.phid IN (%Ls)',
|
||||||
$this->phids);
|
$this->phids);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->repositoryIDs !== null) {
|
if ($this->repositoryIDs !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'commit.repositoryID IN (%Ld)',
|
'commit.repositoryID IN (%Ld)',
|
||||||
$this->repositoryIDs);
|
$this->repositoryIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->authorPHIDs !== null) {
|
if ($this->authorPHIDs !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'commit.authorPHID IN (%Ls)',
|
'commit.authorPHID IN (%Ls)',
|
||||||
$this->authorPHIDs);
|
$this->authorPHIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->epochMin !== null) {
|
if ($this->epochMin !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'commit.epoch >= %d',
|
'commit.epoch >= %d',
|
||||||
$this->epochMin);
|
$this->epochMin);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->epochMax !== null) {
|
if ($this->epochMax !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'commit.epoch <= %d',
|
'commit.epoch <= %d',
|
||||||
$this->epochMax);
|
$this->epochMax);
|
||||||
}
|
}
|
||||||
|
@ -360,13 +330,13 @@ final class DiffusionCommitQuery
|
||||||
if ($this->importing !== null) {
|
if ($this->importing !== null) {
|
||||||
if ($this->importing) {
|
if ($this->importing) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'(commit.importStatus & %d) != %d',
|
'(commit.importStatus & %d) != %d',
|
||||||
PhabricatorRepositoryCommit::IMPORTED_ALL,
|
PhabricatorRepositoryCommit::IMPORTED_ALL,
|
||||||
PhabricatorRepositoryCommit::IMPORTED_ALL);
|
PhabricatorRepositoryCommit::IMPORTED_ALL);
|
||||||
} else {
|
} else {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'(commit.importStatus & %d) = %d',
|
'(commit.importStatus & %d) = %d',
|
||||||
PhabricatorRepositoryCommit::IMPORTED_ALL,
|
PhabricatorRepositoryCommit::IMPORTED_ALL,
|
||||||
PhabricatorRepositoryCommit::IMPORTED_ALL);
|
PhabricatorRepositoryCommit::IMPORTED_ALL);
|
||||||
|
@ -409,7 +379,7 @@ final class DiffusionCommitQuery
|
||||||
|
|
||||||
foreach ($bare as $identifier) {
|
foreach ($bare as $identifier) {
|
||||||
$sql[] = qsprintf(
|
$sql[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'(commit.commitIdentifier LIKE %> AND '.
|
'(commit.commitIdentifier LIKE %> AND '.
|
||||||
'LENGTH(commit.commitIdentifier) = 40)',
|
'LENGTH(commit.commitIdentifier) = 40)',
|
||||||
$identifier);
|
$identifier);
|
||||||
|
@ -437,7 +407,7 @@ final class DiffusionCommitQuery
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$sql[] = qsprintf(
|
$sql[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'(commit.repositoryID = %d AND commit.commitIdentifier = %s)',
|
'(commit.repositoryID = %d AND commit.commitIdentifier = %s)',
|
||||||
$repo->getID(),
|
$repo->getID(),
|
||||||
// NOTE: Because the 'commitIdentifier' column is a string, MySQL
|
// NOTE: Because the 'commitIdentifier' column is a string, MySQL
|
||||||
|
@ -449,7 +419,7 @@ final class DiffusionCommitQuery
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$sql[] = qsprintf(
|
$sql[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'(commit.repositoryID = %d AND commit.commitIdentifier LIKE %>)',
|
'(commit.repositoryID = %d AND commit.commitIdentifier LIKE %>)',
|
||||||
$repo->getID(),
|
$repo->getID(),
|
||||||
$ref['identifier']);
|
$ref['identifier']);
|
||||||
|
@ -470,28 +440,23 @@ final class DiffusionCommitQuery
|
||||||
|
|
||||||
if ($this->auditIDs !== null) {
|
if ($this->auditIDs !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'audit.id IN (%Ld)',
|
'audit.id IN (%Ld)',
|
||||||
$this->auditIDs);
|
$this->auditIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->auditorPHIDs !== null) {
|
if ($this->auditorPHIDs !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'audit.auditorPHID IN (%Ls)',
|
'audit.auditorPHID IN (%Ls)',
|
||||||
$this->auditorPHIDs);
|
$this->auditorPHIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->auditAwaitingUser) {
|
if ($this->needsAuditByPHIDs !== null) {
|
||||||
$awaiting_user_phid = $this->auditAwaitingUser->getPHID();
|
|
||||||
// Exclude package and project audits associated with commits where
|
|
||||||
// the user is the author.
|
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'(commit.authorPHID IS NULL OR commit.authorPHID != %s)
|
'needs.auditorPHID IN (%Ls)',
|
||||||
OR (audit.auditorPHID = %s)',
|
$this->needsAuditByPHIDs);
|
||||||
$awaiting_user_phid,
|
|
||||||
$awaiting_user_phid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$status = $this->auditStatus;
|
$status = $this->auditStatus;
|
||||||
|
@ -499,33 +464,27 @@ final class DiffusionCommitQuery
|
||||||
switch ($status) {
|
switch ($status) {
|
||||||
case self::AUDIT_STATUS_PARTIAL:
|
case self::AUDIT_STATUS_PARTIAL:
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'commit.auditStatus = %d',
|
'commit.auditStatus = %d',
|
||||||
PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED);
|
PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED);
|
||||||
break;
|
break;
|
||||||
case self::AUDIT_STATUS_ACCEPTED:
|
case self::AUDIT_STATUS_ACCEPTED:
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'commit.auditStatus = %d',
|
'commit.auditStatus = %d',
|
||||||
PhabricatorAuditCommitStatusConstants::FULLY_AUDITED);
|
PhabricatorAuditCommitStatusConstants::FULLY_AUDITED);
|
||||||
break;
|
break;
|
||||||
case self::AUDIT_STATUS_CONCERN:
|
case self::AUDIT_STATUS_CONCERN:
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'audit.auditStatus = %s',
|
'status.auditStatus = %s',
|
||||||
PhabricatorAuditStatusConstants::CONCERNED);
|
PhabricatorAuditStatusConstants::CONCERNED);
|
||||||
break;
|
break;
|
||||||
case self::AUDIT_STATUS_OPEN:
|
case self::AUDIT_STATUS_OPEN:
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'audit.auditStatus in (%Ls)',
|
'status.auditStatus in (%Ls)',
|
||||||
PhabricatorAuditStatusConstants::getOpenStatusConstants());
|
PhabricatorAuditStatusConstants::getOpenStatusConstants());
|
||||||
if ($this->auditAwaitingUser) {
|
|
||||||
$where[] = qsprintf(
|
|
||||||
$conn_r,
|
|
||||||
'awaiting.auditStatus IS NULL OR awaiting.auditStatus != %s',
|
|
||||||
PhabricatorAuditStatusConstants::RESIGNED);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case self::AUDIT_STATUS_ANY:
|
case self::AUDIT_STATUS_ANY:
|
||||||
break;
|
break;
|
||||||
|
@ -545,9 +504,7 @@ final class DiffusionCommitQuery
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$where[] = $this->buildPagingClause($conn_r);
|
return $where;
|
||||||
|
|
||||||
return $this->formatWhereClause($where);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function didFilterResults(array $filtered) {
|
protected function didFilterResults(array $filtered) {
|
||||||
|
@ -560,56 +517,113 @@ final class DiffusionCommitQuery
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildJoinClause(AphrontDatabaseConnection $conn_r) {
|
private function shouldJoinStatus() {
|
||||||
$joins = array();
|
return $this->auditStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function shouldJoinAudits() {
|
||||||
|
return $this->auditIDs || $this->auditorPHIDs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function shouldJoinNeeds() {
|
||||||
|
return $this->needsAuditByPHIDs;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
|
||||||
|
$join = parent::buildJoinClauseParts($conn);
|
||||||
$audit_request = new PhabricatorRepositoryAuditRequest();
|
$audit_request = new PhabricatorRepositoryAuditRequest();
|
||||||
|
|
||||||
if ($this->shouldJoinAudits()) {
|
if ($this->shouldJoinStatus()) {
|
||||||
$joins[] = qsprintf(
|
$join[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'%Q %T audit ON commit.phid = audit.commitPHID',
|
'LEFT JOIN %T status ON commit.phid = status.commitPHID',
|
||||||
($this->rowsMustHaveAudits() ? 'JOIN' : 'LEFT JOIN'),
|
|
||||||
$audit_request->getTableName());
|
$audit_request->getTableName());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->auditAwaitingUser) {
|
if ($this->shouldJoinAudits()) {
|
||||||
// Join the request table on the awaiting user's requests, so we can
|
$join[] = qsprintf(
|
||||||
// filter out package and project requests which the user has resigned
|
$conn,
|
||||||
// from.
|
'JOIN %T audit ON commit.phid = audit.commitPHID',
|
||||||
$joins[] = qsprintf(
|
$audit_request->getTableName());
|
||||||
$conn_r,
|
|
||||||
'LEFT JOIN %T awaiting ON audit.commitPHID = awaiting.commitPHID AND
|
|
||||||
awaiting.auditorPHID = %s',
|
|
||||||
$audit_request->getTableName(),
|
|
||||||
$this->auditAwaitingUser->getPHID());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($joins) {
|
if ($this->shouldJoinNeeds()) {
|
||||||
return implode(' ', $joins);
|
$join[] = qsprintf(
|
||||||
} else {
|
$conn,
|
||||||
return '';
|
'JOIN %T needs ON commit.phid = needs.commitPHID
|
||||||
|
AND needs.auditStatus IN (%Ls)',
|
||||||
|
$audit_request->getTableName(),
|
||||||
|
array(
|
||||||
|
PhabricatorAuditStatusConstants::AUDIT_REQUESTED,
|
||||||
|
PhabricatorAuditStatusConstants::AUDIT_REQUIRED,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $join;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildGroupClause(AphrontDatabaseConnection $conn_r) {
|
protected function shouldGroupQueryResultRows() {
|
||||||
$should_group = $this->shouldJoinAudits();
|
if ($this->shouldJoinStatus()) {
|
||||||
|
return true;
|
||||||
// TODO: Currently, the audit table is missing a unique key, so we may
|
|
||||||
// require a GROUP BY if we perform this join. See T1768. This can be
|
|
||||||
// removed once the table has the key.
|
|
||||||
if ($this->auditAwaitingUser) {
|
|
||||||
$should_group = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($should_group) {
|
if ($this->shouldJoinAudits()) {
|
||||||
return 'GROUP BY commit.id';
|
return true;
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->shouldJoinNeeds()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::shouldGroupQueryResultRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getQueryApplicationClass() {
|
public function getQueryApplicationClass() {
|
||||||
return 'PhabricatorDiffusionApplication';
|
return 'PhabricatorDiffusionApplication';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getOrderableColumns() {
|
||||||
|
return parent::getOrderableColumns() + array(
|
||||||
|
'epoch' => array(
|
||||||
|
'table' => $this->getPrimaryTableAlias(),
|
||||||
|
'column' => 'epoch',
|
||||||
|
'type' => 'int',
|
||||||
|
'reverse' => false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getPagingValueMap($cursor, array $keys) {
|
||||||
|
$commit = $this->loadCursorObject($cursor);
|
||||||
|
return array(
|
||||||
|
'id' => $commit->getID(),
|
||||||
|
'epoch' => $commit->getEpoch(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBuiltinOrders() {
|
||||||
|
$parent = parent::getBuiltinOrders();
|
||||||
|
|
||||||
|
// Rename the default ID-based orders.
|
||||||
|
$parent['importnew'] = array(
|
||||||
|
'name' => pht('Import Date (Newest First)'),
|
||||||
|
) + $parent['newest'];
|
||||||
|
|
||||||
|
$parent['importold'] = array(
|
||||||
|
'name' => pht('Import Date (Oldest First)'),
|
||||||
|
) + $parent['oldest'];
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'newest' => array(
|
||||||
|
'vector' => array('epoch', 'id'),
|
||||||
|
'name' => pht('Commit Date (Newest First)'),
|
||||||
|
),
|
||||||
|
'oldest' => array(
|
||||||
|
'vector' => array('-epoch', '-id'),
|
||||||
|
'name' => pht('Commit Date (Oldest First)'),
|
||||||
|
),
|
||||||
|
) + $parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DiffusionAuditorFunctionDatasource
|
||||||
|
extends PhabricatorTypeaheadCompositeDatasource {
|
||||||
|
|
||||||
|
public function getBrowseTitle() {
|
||||||
|
return pht('Browse Auditors');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlaceholderText() {
|
||||||
|
return pht('Type a user, project, package name or function...');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatasourceApplicationClass() {
|
||||||
|
return 'PhabricatorDiffusionApplication';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getComponentDatasources() {
|
||||||
|
return array(
|
||||||
|
new PhabricatorProjectOrUserFunctionDatasource(),
|
||||||
|
new PhabricatorOwnersPackageFunctionDatasource(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -78,25 +78,36 @@ final class PhabricatorFilesApplication extends PhabricatorApplication {
|
||||||
'delete/(?P<id>[1-9]\d*)/' => 'PhabricatorFileDeleteController',
|
'delete/(?P<id>[1-9]\d*)/' => 'PhabricatorFileDeleteController',
|
||||||
'edit/(?P<id>[1-9]\d*)/' => 'PhabricatorFileEditController',
|
'edit/(?P<id>[1-9]\d*)/' => 'PhabricatorFileEditController',
|
||||||
'info/(?P<phid>[^/]+)/' => 'PhabricatorFileInfoController',
|
'info/(?P<phid>[^/]+)/' => 'PhabricatorFileInfoController',
|
||||||
'data/'.
|
|
||||||
'(?:@(?P<instance>[^/]+)/)?'.
|
|
||||||
'(?P<key>[^/]+)/'.
|
|
||||||
'(?P<phid>[^/]+)/'.
|
|
||||||
'(?:(?P<token>[^/]+)/)?'.
|
|
||||||
'.*'
|
|
||||||
=> 'PhabricatorFileDataController',
|
|
||||||
'proxy/' => 'PhabricatorFileProxyController',
|
'proxy/' => 'PhabricatorFileProxyController',
|
||||||
'xform/'.
|
|
||||||
'(?:@(?P<instance>[^/]+)/)?'.
|
|
||||||
'(?P<transform>[^/]+)/'.
|
|
||||||
'(?P<phid>[^/]+)/'.
|
|
||||||
'(?P<key>[^/]+)/'
|
|
||||||
=> 'PhabricatorFileTransformController',
|
|
||||||
'transforms/(?P<id>[1-9]\d*)/' =>
|
'transforms/(?P<id>[1-9]\d*)/' =>
|
||||||
'PhabricatorFileTransformListController',
|
'PhabricatorFileTransformListController',
|
||||||
'uploaddialog/' => 'PhabricatorFileUploadDialogController',
|
'uploaddialog/' => 'PhabricatorFileUploadDialogController',
|
||||||
'download/(?P<phid>[^/]+)/' => 'PhabricatorFileDialogController',
|
'download/(?P<phid>[^/]+)/' => 'PhabricatorFileDialogController',
|
||||||
),
|
) + $this->getResourceSubroutes(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResourceRoutes() {
|
||||||
|
return array(
|
||||||
|
'/file/' => $this->getResourceSubroutes(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getResourceSubroutes() {
|
||||||
|
return array(
|
||||||
|
'data/'.
|
||||||
|
'(?:@(?P<instance>[^/]+)/)?'.
|
||||||
|
'(?P<key>[^/]+)/'.
|
||||||
|
'(?P<phid>[^/]+)/'.
|
||||||
|
'(?:(?P<token>[^/]+)/)?'.
|
||||||
|
'.*'
|
||||||
|
=> 'PhabricatorFileDataController',
|
||||||
|
'xform/'.
|
||||||
|
'(?:@(?P<instance>[^/]+)/)?'.
|
||||||
|
'(?P<transform>[^/]+)/'.
|
||||||
|
'(?P<phid>[^/]+)/'.
|
||||||
|
'(?P<key>[^/]+)/'
|
||||||
|
=> 'PhabricatorFileTransformController',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ final class FileAllocateConduitAPIMethod
|
||||||
'contentLength' => 'int',
|
'contentLength' => 'int',
|
||||||
'contentHash' => 'optional string',
|
'contentHash' => 'optional string',
|
||||||
'viewPolicy' => 'optional string',
|
'viewPolicy' => 'optional string',
|
||||||
|
'deleteAfterEpoch' => 'optional int',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +40,11 @@ final class FileAllocateConduitAPIMethod
|
||||||
'isExplicitUpload' => true,
|
'isExplicitUpload' => true,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$ttl = $request->getValue('deleteAfterEpoch');
|
||||||
|
if ($ttl) {
|
||||||
|
$properties['ttl'] = $ttl;
|
||||||
|
}
|
||||||
|
|
||||||
$file = null;
|
$file = null;
|
||||||
if ($hash) {
|
if ($hash) {
|
||||||
$file = PhabricatorFile::newFileFromContentHash(
|
$file = PhabricatorFile::newFileFromContentHash(
|
||||||
|
|
|
@ -21,10 +21,15 @@ final class FundBackerProduct extends PhortuneProductImplementation {
|
||||||
|
|
||||||
public function getName(PhortuneProduct $product) {
|
public function getName(PhortuneProduct $product) {
|
||||||
$initiative = $this->getInitiative();
|
$initiative = $this->getInitiative();
|
||||||
return pht(
|
|
||||||
'Fund %s %s',
|
if (!$initiative) {
|
||||||
$initiative->getMonogram(),
|
return pht('Fund <Unknown Initiative>');
|
||||||
$initiative->getName());
|
} else {
|
||||||
|
return pht(
|
||||||
|
'Fund %s %s',
|
||||||
|
$initiative->getMonogram(),
|
||||||
|
$initiative->getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPriceAsCurrency(PhortuneProduct $product) {
|
public function getPriceAsCurrency(PhortuneProduct $product) {
|
||||||
|
|
|
@ -88,7 +88,6 @@ final class HarbormasterUnitPropertyView extends AphrontView {
|
||||||
ArcanistUnitTestResult::RESULT_FAIL => pht('Failed'),
|
ArcanistUnitTestResult::RESULT_FAIL => pht('Failed'),
|
||||||
ArcanistUnitTestResult::RESULT_UNSOUND => pht('Unsound'),
|
ArcanistUnitTestResult::RESULT_UNSOUND => pht('Unsound'),
|
||||||
ArcanistUnitTestResult::RESULT_SKIP => pht('Skipped'),
|
ArcanistUnitTestResult::RESULT_SKIP => pht('Skipped'),
|
||||||
ArcanistUnitTestResult::RESULT_POSTPONED => pht('Postponed'),
|
|
||||||
ArcanistUnitTestResult::RESULT_PASS => pht('Passed'),
|
ArcanistUnitTestResult::RESULT_PASS => pht('Passed'),
|
||||||
);
|
);
|
||||||
$result = idx($names, $result, $result);
|
$result = idx($names, $result, $result);
|
||||||
|
|
|
@ -349,9 +349,8 @@ final class PhabricatorHomeMainController extends PhabricatorHomeController {
|
||||||
|
|
||||||
$query = id(new DiffusionCommitQuery())
|
$query = id(new DiffusionCommitQuery())
|
||||||
->setViewer($user)
|
->setViewer($user)
|
||||||
->withAuditorPHIDs($phids)
|
->withNeedsAuditByPHIDs($phids)
|
||||||
->withAuditStatus(DiffusionCommitQuery::AUDIT_STATUS_OPEN)
|
->withAuditStatus(DiffusionCommitQuery::AUDIT_STATUS_OPEN)
|
||||||
->withAuditAwaitingUser($user)
|
|
||||||
->needAuditRequests(true)
|
->needAuditRequests(true)
|
||||||
->needCommitData(true)
|
->needCommitData(true)
|
||||||
->setLimit(10);
|
->setLimit(10);
|
||||||
|
|
|
@ -226,39 +226,36 @@ final class PhabricatorAppSearchEngine
|
||||||
),
|
),
|
||||||
'');
|
'');
|
||||||
|
|
||||||
$description = phutil_tag(
|
$description = $application->getShortDescription();
|
||||||
'div',
|
|
||||||
array(
|
$configure = id(new PHUIButtonView())
|
||||||
'style' => 'white-space: nowrap; '.
|
->setTag('a')
|
||||||
'overflow: hidden; '.
|
->setHref('/applications/view/'.get_class($application).'/')
|
||||||
'text-overflow: ellipsis;',
|
->setText(pht('Configure'))
|
||||||
),
|
->setColor(PHUIButtonView::GREY);
|
||||||
$application->getShortDescription());
|
|
||||||
|
$name = $application->getName();
|
||||||
|
if ($application->isPrototype()) {
|
||||||
|
$name = $name.' '.pht('(Prototype)');
|
||||||
|
}
|
||||||
|
|
||||||
$item = id(new PHUIObjectItemView())
|
$item = id(new PHUIObjectItemView())
|
||||||
->setHeader($application->getName())
|
->setHeader($name)
|
||||||
->setImageIcon($icon_view)
|
->setImageIcon($icon_view)
|
||||||
->addAttribute($description)
|
->setSubhead($description)
|
||||||
->addAction(
|
->setLaunchButton($configure);
|
||||||
id(new PHUIListItemView())
|
|
||||||
->setName(pht('Help/Options'))
|
|
||||||
->setIcon('fa-cog')
|
|
||||||
->setHref('/applications/view/'.get_class($application).'/'));
|
|
||||||
|
|
||||||
if ($application->getBaseURI() && $application->isInstalled()) {
|
if ($application->getBaseURI() && $application->isInstalled()) {
|
||||||
$item->setHref($application->getBaseURI());
|
$item->setHref($application->getBaseURI());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$application->isInstalled()) {
|
if (!$application->isInstalled()) {
|
||||||
$item->addIcon('fa-times', pht('Uninstalled'));
|
$item->addAttribute(pht('Uninstalled'));
|
||||||
}
|
$item->setDisabled(true);
|
||||||
|
|
||||||
if ($application->isPrototype()) {
|
|
||||||
$item->addIcon('fa-bomb grey', pht('Prototype'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$application->isFirstParty()) {
|
if (!$application->isFirstParty()) {
|
||||||
$item->addIcon('fa-puzzle-piece', pht('Extension'));
|
$item->addAttribute(pht('Extension'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$list->addItem($item);
|
$list->addItem($item);
|
||||||
|
|
|
@ -13,9 +13,11 @@ final class PhabricatorMetaMTAMailgunReceiveController
|
||||||
$timestamp = $request->getStr('timestamp');
|
$timestamp = $request->getStr('timestamp');
|
||||||
$token = $request->getStr('token');
|
$token = $request->getStr('token');
|
||||||
$sig = $request->getStr('signature');
|
$sig = $request->getStr('signature');
|
||||||
return hash_hmac('sha256', $timestamp.$token, $api_key) == $sig;
|
$hash = hash_hmac('sha256', $timestamp.$token, $api_key);
|
||||||
|
|
||||||
|
return phutil_hashes_are_identical($sig, $hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function processRequest() {
|
public function processRequest() {
|
||||||
|
|
||||||
// No CSRF for Mailgun.
|
// No CSRF for Mailgun.
|
||||||
|
|
|
@ -1,20 +1,15 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class PhabricatorOAuthServerAuthController
|
final class PhabricatorOAuthServerAuthController
|
||||||
extends PhabricatorAuthController {
|
extends PhabricatorOAuthServerController {
|
||||||
|
|
||||||
public function shouldRequireLogin() {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
return true;
|
$viewer = $this->getViewer();
|
||||||
}
|
|
||||||
|
|
||||||
public function processRequest() {
|
$server = new PhabricatorOAuthServer();
|
||||||
$request = $this->getRequest();
|
$client_phid = $request->getStr('client_id');
|
||||||
$viewer = $request->getUser();
|
$scope = $request->getStr('scope');
|
||||||
|
$redirect_uri = $request->getStr('redirect_uri');
|
||||||
$server = new PhabricatorOAuthServer();
|
|
||||||
$client_phid = $request->getStr('client_id');
|
|
||||||
$scope = $request->getStr('scope');
|
|
||||||
$redirect_uri = $request->getStr('redirect_uri');
|
|
||||||
$response_type = $request->getStr('response_type');
|
$response_type = $request->getStr('response_type');
|
||||||
|
|
||||||
// state is an opaque value the client sent us for their own purposes
|
// state is an opaque value the client sent us for their own purposes
|
||||||
|
@ -30,6 +25,19 @@ final class PhabricatorOAuthServerAuthController
|
||||||
phutil_tag('strong', array(), 'client_id')));
|
phutil_tag('strong', array(), 'client_id')));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We require that users must be able to see an OAuth application
|
||||||
|
// in order to authorize it. This allows an application's visibility
|
||||||
|
// policy to be used to restrict authorized users.
|
||||||
|
try {
|
||||||
|
$client = id(new PhabricatorOAuthServerClientQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs(array($client_phid))
|
||||||
|
->executeOne();
|
||||||
|
} catch (PhabricatorPolicyException $ex) {
|
||||||
|
$ex->setContext(self::CONTEXT_AUTHORIZE);
|
||||||
|
throw $ex;
|
||||||
|
}
|
||||||
|
|
||||||
$server->setUser($viewer);
|
$server->setUser($viewer);
|
||||||
$is_authorized = false;
|
$is_authorized = false;
|
||||||
$authorization = null;
|
$authorization = null;
|
||||||
|
@ -39,24 +47,6 @@ final class PhabricatorOAuthServerAuthController
|
||||||
// one giant try / catch around all the exciting database stuff so we
|
// one giant try / catch around all the exciting database stuff so we
|
||||||
// can return a 'server_error' response if something goes wrong!
|
// can return a 'server_error' response if something goes wrong!
|
||||||
try {
|
try {
|
||||||
try {
|
|
||||||
$client = id(new PhabricatorOAuthServerClientQuery())
|
|
||||||
->setViewer($viewer)
|
|
||||||
->withPHIDs(array($client_phid))
|
|
||||||
->executeOne();
|
|
||||||
} catch (PhabricatorPolicyException $ex) {
|
|
||||||
// We require that users must be able to see an OAuth application
|
|
||||||
// in order to authorize it. This allows an application's visibility
|
|
||||||
// policy to be used to restrict authorized users.
|
|
||||||
|
|
||||||
// None of the OAuth error responses are a perfect fit for this, but
|
|
||||||
// 'invalid_client' seems closest.
|
|
||||||
return $this->buildErrorResponse(
|
|
||||||
'invalid_client',
|
|
||||||
pht('Not Authorized'),
|
|
||||||
pht('You are not authorized to authenticate.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$client) {
|
if (!$client) {
|
||||||
return $this->buildErrorResponse(
|
return $this->buildErrorResponse(
|
||||||
'invalid_request',
|
'invalid_request',
|
||||||
|
@ -211,6 +201,7 @@ final class PhabricatorOAuthServerAuthController
|
||||||
} else {
|
} else {
|
||||||
$desired_scopes = $scope;
|
$desired_scopes = $scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PhabricatorOAuthServerScope::validateScopesDict($desired_scopes)) {
|
if (!PhabricatorOAuthServerScope::validateScopesDict($desired_scopes)) {
|
||||||
return $this->buildErrorResponse(
|
return $this->buildErrorResponse(
|
||||||
'invalid_scope',
|
'invalid_scope',
|
||||||
|
@ -236,8 +227,8 @@ final class PhabricatorOAuthServerAuthController
|
||||||
'error_description' => $cancel_msg,
|
'error_description' => $cancel_msg,
|
||||||
));
|
));
|
||||||
|
|
||||||
$dialog = id(new AphrontDialogView())
|
return $this->newDialog()
|
||||||
->setUser($viewer)
|
->setShortTitle(pht('Authorize Access'))
|
||||||
->setTitle(pht('Authorize "%s"?', $name))
|
->setTitle(pht('Authorize "%s"?', $name))
|
||||||
->setSubmitURI($request->getRequestURI()->getPath())
|
->setSubmitURI($request->getRequestURI()->getPath())
|
||||||
->setWidth(AphrontDialogView::WIDTH_FORM)
|
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||||
|
@ -250,23 +241,18 @@ final class PhabricatorOAuthServerAuthController
|
||||||
->appendChild($form->buildLayoutView())
|
->appendChild($form->buildLayoutView())
|
||||||
->addSubmitButton(pht('Authorize Access'))
|
->addSubmitButton(pht('Authorize Access'))
|
||||||
->addCancelButton((string)$cancel_uri, pht('Do Not Authorize'));
|
->addCancelButton((string)$cancel_uri, pht('Do Not Authorize'));
|
||||||
|
|
||||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private function buildErrorResponse($code, $title, $message) {
|
private function buildErrorResponse($code, $title, $message) {
|
||||||
$viewer = $this->getRequest()->getUser();
|
$viewer = $this->getRequest()->getUser();
|
||||||
|
|
||||||
$dialog = id(new AphrontDialogView())
|
return $this->newDialog()
|
||||||
->setUser($viewer)
|
|
||||||
->setTitle(pht('OAuth: %s', $title))
|
->setTitle(pht('OAuth: %s', $title))
|
||||||
->appendParagraph($message)
|
->appendParagraph($message)
|
||||||
->appendParagraph(
|
->appendParagraph(
|
||||||
pht('OAuth Error Code: %s', phutil_tag('tt', array(), $code)))
|
pht('OAuth Error Code: %s', phutil_tag('tt', array(), $code)))
|
||||||
->addCancelButton('/', pht('Alas!'));
|
->addCancelButton('/', pht('Alas!'));
|
||||||
|
|
||||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,58 +3,13 @@
|
||||||
abstract class PhabricatorOAuthServerController
|
abstract class PhabricatorOAuthServerController
|
||||||
extends PhabricatorController {
|
extends PhabricatorController {
|
||||||
|
|
||||||
public function buildStandardPageResponse($view, array $data) {
|
const CONTEXT_AUTHORIZE = 'oauthserver.authorize';
|
||||||
$user = $this->getRequest()->getUser();
|
|
||||||
$page = $this->buildStandardPageView();
|
|
||||||
$page->setApplicationName(pht('OAuth Server'));
|
|
||||||
$page->setBaseURI('/oauthserver/');
|
|
||||||
$page->setTitle(idx($data, 'title'));
|
|
||||||
|
|
||||||
$nav = new AphrontSideNavFilterView();
|
protected function buildApplicationCrumbs() {
|
||||||
$nav->setBaseURI(new PhutilURI('/oauthserver/'));
|
// We're specifically not putting an "OAuth Server" application crumb
|
||||||
$nav->addLabel(pht('Clients'));
|
// on these pages because it doesn't make sense to send users there on
|
||||||
$nav->addFilter('client/create', pht('Create Client'));
|
// the auth workflows.
|
||||||
foreach ($this->getExtraClientFilters() as $filter) {
|
return new PHUICrumbsView();
|
||||||
$nav->addFilter($filter['url'], $filter['label']);
|
|
||||||
}
|
|
||||||
$nav->addFilter('client', pht('My Clients'));
|
|
||||||
$nav->selectFilter($this->getFilter(), 'clientauthorization');
|
|
||||||
|
|
||||||
$nav->appendChild($view);
|
|
||||||
|
|
||||||
$page->appendChild($nav);
|
|
||||||
|
|
||||||
$response = new AphrontWebpageResponse();
|
|
||||||
return $response->setContent($page->render());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getFilter() {
|
|
||||||
return 'clientauthorization';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getExtraClientFilters() {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getHighlightPHIDs() {
|
|
||||||
$phids = array();
|
|
||||||
$request = $this->getRequest();
|
|
||||||
$edited = $request->getStr('edited');
|
|
||||||
$new = $request->getStr('new');
|
|
||||||
if ($edited) {
|
|
||||||
$phids[$edited] = $edited;
|
|
||||||
}
|
|
||||||
if ($new) {
|
|
||||||
$phids[$new] = $new;
|
|
||||||
}
|
|
||||||
return $phids;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function buildErrorView($error_message) {
|
|
||||||
$error = new PHUIInfoView();
|
|
||||||
$error->setSeverity(PHUIInfoView::SEVERITY_ERROR);
|
|
||||||
$error->setTitle($error_message);
|
|
||||||
|
|
||||||
return $error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,26 +3,13 @@
|
||||||
final class PhabricatorOAuthServerTestController
|
final class PhabricatorOAuthServerTestController
|
||||||
extends PhabricatorOAuthServerController {
|
extends PhabricatorOAuthServerController {
|
||||||
|
|
||||||
private $id;
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
public function shouldRequireLogin() {
|
$id = $request->getURIData('id');
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function willProcessRequest(array $data) {
|
|
||||||
$this->id = $data['id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function processRequest() {
|
|
||||||
$request = $this->getRequest();
|
|
||||||
$viewer = $request->getUser();
|
|
||||||
|
|
||||||
$panels = array();
|
|
||||||
$results = array();
|
|
||||||
|
|
||||||
$client = id(new PhabricatorOAuthServerClientQuery())
|
$client = id(new PhabricatorOAuthServerClientQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withIDs(array($this->id))
|
->withIDs(array($id))
|
||||||
->executeOne();
|
->executeOne();
|
||||||
if (!$client) {
|
if (!$client) {
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
|
@ -37,16 +24,13 @@ final class PhabricatorOAuthServerTestController
|
||||||
->withClientPHIDs(array($client->getPHID()))
|
->withClientPHIDs(array($client->getPHID()))
|
||||||
->executeOne();
|
->executeOne();
|
||||||
if ($authorization) {
|
if ($authorization) {
|
||||||
$dialog = id(new AphrontDialogView())
|
return $this->newDialog()
|
||||||
->setUser($viewer)
|
|
||||||
->setTitle(pht('Already Authorized'))
|
->setTitle(pht('Already Authorized'))
|
||||||
->appendParagraph(
|
->appendParagraph(
|
||||||
pht(
|
pht(
|
||||||
'You have already authorized this application to access your '.
|
'You have already authorized this application to access your '.
|
||||||
'account.'))
|
'account.'))
|
||||||
->addCancelButton($view_uri, pht('Close'));
|
->addCancelButton($view_uri, pht('Close'));
|
||||||
|
|
||||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($request->isFormPost()) {
|
if ($request->isFormPost()) {
|
||||||
|
@ -65,8 +49,7 @@ final class PhabricatorOAuthServerTestController
|
||||||
|
|
||||||
// TODO: It would be nice to put scope options in this dialog, maybe?
|
// TODO: It would be nice to put scope options in this dialog, maybe?
|
||||||
|
|
||||||
$dialog = id(new AphrontDialogView())
|
return $this->newDialog()
|
||||||
->setUser($viewer)
|
|
||||||
->setTitle(pht('Authorize Application?'))
|
->setTitle(pht('Authorize Application?'))
|
||||||
->appendParagraph(
|
->appendParagraph(
|
||||||
pht(
|
pht(
|
||||||
|
@ -75,7 +58,5 @@ final class PhabricatorOAuthServerTestController
|
||||||
phutil_tag('strong', array(), $client->getName())))
|
phutil_tag('strong', array(), $client->getName())))
|
||||||
->addCancelButton($view_uri)
|
->addCancelButton($view_uri)
|
||||||
->addSubmitButton(pht('Authorize Application'));
|
->addSubmitButton(pht('Authorize Application'));
|
||||||
|
|
||||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class PhabricatorOAuthServerTokenController
|
final class PhabricatorOAuthServerTokenController
|
||||||
extends PhabricatorAuthController {
|
extends PhabricatorOAuthServerController {
|
||||||
|
|
||||||
public function shouldRequireLogin() {
|
public function shouldRequireLogin() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -14,15 +14,15 @@ final class PhabricatorOAuthServerTokenController
|
||||||
return parent::shouldAllowRestrictedParameter($parameter_name);
|
return parent::shouldAllowRestrictedParameter($parameter_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function processRequest() {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
$request = $this->getRequest();
|
$grant_type = $request->getStr('grant_type');
|
||||||
$grant_type = $request->getStr('grant_type');
|
$code = $request->getStr('code');
|
||||||
$code = $request->getStr('code');
|
$redirect_uri = $request->getStr('redirect_uri');
|
||||||
$redirect_uri = $request->getStr('redirect_uri');
|
$client_phid = $request->getStr('client_id');
|
||||||
$client_phid = $request->getStr('client_id');
|
|
||||||
$client_secret = $request->getStr('client_secret');
|
$client_secret = $request->getStr('client_secret');
|
||||||
$response = new PhabricatorOAuthResponse();
|
$response = new PhabricatorOAuthResponse();
|
||||||
$server = new PhabricatorOAuthServer();
|
$server = new PhabricatorOAuthServer();
|
||||||
|
|
||||||
if ($grant_type != 'authorization_code') {
|
if ($grant_type != 'authorization_code') {
|
||||||
$response->setError('unsupported_grant_type');
|
$response->setError('unsupported_grant_type');
|
||||||
$response->setErrorDescription(
|
$response->setErrorDescription(
|
||||||
|
@ -32,11 +32,13 @@ final class PhabricatorOAuthServerTokenController
|
||||||
'authorization_code'));
|
'authorization_code'));
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$code) {
|
if (!$code) {
|
||||||
$response->setError('invalid_request');
|
$response->setError('invalid_request');
|
||||||
$response->setErrorDescription(pht('Required parameter code missing.'));
|
$response->setErrorDescription(pht('Required parameter code missing.'));
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$client_phid) {
|
if (!$client_phid) {
|
||||||
$response->setError('invalid_request');
|
$response->setError('invalid_request');
|
||||||
$response->setErrorDescription(
|
$response->setErrorDescription(
|
||||||
|
@ -45,6 +47,7 @@ final class PhabricatorOAuthServerTokenController
|
||||||
'client_id'));
|
'client_id'));
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$client_secret) {
|
if (!$client_secret) {
|
||||||
$response->setError('invalid_request');
|
$response->setError('invalid_request');
|
||||||
$response->setErrorDescription(
|
$response->setErrorDescription(
|
||||||
|
@ -53,6 +56,7 @@ final class PhabricatorOAuthServerTokenController
|
||||||
'client_secret'));
|
'client_secret'));
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
// one giant try / catch around all the exciting database stuff so we
|
// one giant try / catch around all the exciting database stuff so we
|
||||||
// can return a 'server_error' response if something goes wrong!
|
// can return a 'server_error' response if something goes wrong!
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -26,7 +26,7 @@ final class PhabricatorOwnersApplication extends PhabricatorApplication {
|
||||||
return array(
|
return array(
|
||||||
array(
|
array(
|
||||||
'name' => pht('Owners User Guide'),
|
'name' => pht('Owners User Guide'),
|
||||||
'href' => PhabricatorEnv::getDoclink('Owners Tool User Guide'),
|
'href' => PhabricatorEnv::getDoclink('Owners User Guide'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -372,6 +372,11 @@ final class PhabricatorOwnersPackageQuery
|
||||||
$include = false;
|
$include = false;
|
||||||
|
|
||||||
foreach ($package->getPaths() as $package_path) {
|
foreach ($package->getPaths() as $package_path) {
|
||||||
|
if ($package_path->getRepositoryPHID() != $repository_phid) {
|
||||||
|
// If this path is for some other repository, skip it.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$strength = $package_path->getPathMatchStrength($path);
|
$strength = $package_path->getPathMatchStrength($path);
|
||||||
if ($strength > $best_match) {
|
if ($strength > $best_match) {
|
||||||
$best_match = $strength;
|
$best_match = $strength;
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorOwnersPackageFunctionDatasource
|
||||||
|
extends PhabricatorTypeaheadCompositeDatasource {
|
||||||
|
|
||||||
|
public function getBrowseTitle() {
|
||||||
|
return pht('Browse Packages');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlaceholderText() {
|
||||||
|
return pht('Type a package name or function...');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatasourceApplicationClass() {
|
||||||
|
return 'PhabricatorOwnersApplication';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getComponentDatasources() {
|
||||||
|
return array(
|
||||||
|
new PhabricatorOwnersPackageDatasource(),
|
||||||
|
new PhabricatorOwnersPackageOwnerDatasource(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorOwnersPackageOwnerDatasource
|
||||||
|
extends PhabricatorTypeaheadCompositeDatasource {
|
||||||
|
|
||||||
|
public function getBrowseTitle() {
|
||||||
|
return pht('Browse Packages by Owner');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlaceholderText() {
|
||||||
|
return pht('Type packages(<user>) or packages(<project>)...');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatasourceApplicationClass() {
|
||||||
|
return 'PhabricatorOwnersApplication';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getComponentDatasources() {
|
||||||
|
return array(
|
||||||
|
new PhabricatorPeopleDatasource(),
|
||||||
|
new PhabricatorProjectDatasource(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatasourceFunctions() {
|
||||||
|
return array(
|
||||||
|
'packages' => array(
|
||||||
|
'name' => pht('Packages: ...'),
|
||||||
|
'arguments' => pht('owner'),
|
||||||
|
'summary' => pht("Find results in any of an owner's projects."),
|
||||||
|
'description' => pht(
|
||||||
|
"This function allows you to find results associated with any ".
|
||||||
|
"of the packages a specified user or project is an owner of. ".
|
||||||
|
"For example, this will find results associated with all of ".
|
||||||
|
"the projects `%s` owns:\n\n%s\n\n",
|
||||||
|
'alincoln',
|
||||||
|
'> packages(alincoln)'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function didLoadResults(array $results) {
|
||||||
|
foreach ($results as $result) {
|
||||||
|
$result
|
||||||
|
->setColor(null)
|
||||||
|
->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION)
|
||||||
|
->setIcon('fa-asterisk')
|
||||||
|
->setPHID('packages('.$result->getPHID().')')
|
||||||
|
->setDisplayName(pht('Packages: %s', $result->getDisplayName()))
|
||||||
|
->setName($result->getName().' packages');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function evaluateFunction($function, array $argv_list) {
|
||||||
|
$phids = array();
|
||||||
|
foreach ($argv_list as $argv) {
|
||||||
|
$phids[] = head($argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
$phids = $this->resolvePHIDs($phids);
|
||||||
|
|
||||||
|
$user_phids = array();
|
||||||
|
foreach ($phids as $key => $phid) {
|
||||||
|
if (phid_get_type($phid) == PhabricatorPeopleUserPHIDType::TYPECONST) {
|
||||||
|
$user_phids[] = $phid;
|
||||||
|
unset($phids[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user_phids) {
|
||||||
|
$packages = id(new PhabricatorOwnersPackageQuery())
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->withOwnerPHIDs($user_phids)
|
||||||
|
->execute();
|
||||||
|
foreach ($packages as $package) {
|
||||||
|
$phids[] = $package->getPHID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $phids;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderFunctionTokens($function, array $argv_list) {
|
||||||
|
$phids = array();
|
||||||
|
foreach ($argv_list as $argv) {
|
||||||
|
$phids[] = head($argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
$phids = $this->resolvePHIDs($phids);
|
||||||
|
|
||||||
|
$tokens = $this->renderTokens($phids);
|
||||||
|
foreach ($tokens as $token) {
|
||||||
|
$token->setColor(null);
|
||||||
|
if ($token->isInvalid()) {
|
||||||
|
$token
|
||||||
|
->setValue(pht('Packages: Invalid Owner'));
|
||||||
|
} else {
|
||||||
|
$token
|
||||||
|
->setIcon('fa-asterisk')
|
||||||
|
->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION)
|
||||||
|
->setKey('packages('.$token->getKey().')')
|
||||||
|
->setValue(pht('Packages: %s', $token->getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolvePHIDs(array $phids) {
|
||||||
|
|
||||||
|
// TODO: It would be nice for this to handle `packages(#project)` from a
|
||||||
|
// query string or eventually via Conduit. This could also share code with
|
||||||
|
// PhabricatorProjectLogicalUserDatasource.
|
||||||
|
|
||||||
|
$usernames = array();
|
||||||
|
foreach ($phids as $key => $phid) {
|
||||||
|
if (phid_get_type($phid) != PhabricatorPeopleUserPHIDType::TYPECONST) {
|
||||||
|
$usernames[$key] = $phid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($usernames) {
|
||||||
|
$users = id(new PhabricatorPeopleQuery())
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->withUsernames($usernames)
|
||||||
|
->execute();
|
||||||
|
$users = mpull($users, null, 'getUsername');
|
||||||
|
foreach ($usernames as $key => $username) {
|
||||||
|
$user = idx($users, $username);
|
||||||
|
if ($user) {
|
||||||
|
$phids[$key] = $user->getPHID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $phids;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -365,19 +365,16 @@ final class PhabricatorUser
|
||||||
}
|
}
|
||||||
|
|
||||||
public function validateCSRFToken($token) {
|
public function validateCSRFToken($token) {
|
||||||
$salt = null;
|
// We expect a BREACH-mitigating token. See T3684.
|
||||||
$version = 'plain';
|
|
||||||
|
|
||||||
// This is a BREACH-mitigating token. See T3684.
|
|
||||||
$breach_prefix = self::CSRF_BREACH_PREFIX;
|
$breach_prefix = self::CSRF_BREACH_PREFIX;
|
||||||
$breach_prelen = strlen($breach_prefix);
|
$breach_prelen = strlen($breach_prefix);
|
||||||
|
if (strncmp($token, $breach_prefix, $breach_prelen) !== 0) {
|
||||||
if (!strncmp($token, $breach_prefix, $breach_prelen)) {
|
return false;
|
||||||
$version = 'breach';
|
|
||||||
$salt = substr($token, $breach_prelen, self::CSRF_SALT_LENGTH);
|
|
||||||
$token = substr($token, $breach_prelen + self::CSRF_SALT_LENGTH);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$salt = substr($token, $breach_prelen, self::CSRF_SALT_LENGTH);
|
||||||
|
$token = substr($token, $breach_prelen + self::CSRF_SALT_LENGTH);
|
||||||
|
|
||||||
// When the user posts a form, we check that it contains a valid CSRF token.
|
// When the user posts a form, we check that it contains a valid CSRF token.
|
||||||
// Tokens cycle each hour (every CSRF_CYLCE_FREQUENCY seconds) and we accept
|
// Tokens cycle each hour (every CSRF_CYLCE_FREQUENCY seconds) and we accept
|
||||||
// either the current token, the next token (users can submit a "future"
|
// either the current token, the next token (users can submit a "future"
|
||||||
|
@ -407,22 +404,11 @@ final class PhabricatorUser
|
||||||
|
|
||||||
for ($ii = -$csrf_window; $ii <= 1; $ii++) {
|
for ($ii = -$csrf_window; $ii <= 1; $ii++) {
|
||||||
$valid = $this->getRawCSRFToken($ii);
|
$valid = $this->getRawCSRFToken($ii);
|
||||||
switch ($version) {
|
|
||||||
// TODO: We can remove this after the BREACH version has been in the
|
$digest = PhabricatorHash::digest($valid, $salt);
|
||||||
// wild for a while.
|
$digest = substr($digest, 0, self::CSRF_TOKEN_LENGTH);
|
||||||
case 'plain':
|
if (phutil_hashes_are_identical($digest, $token)) {
|
||||||
if ($token == $valid) {
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'breach':
|
|
||||||
$digest = PhabricatorHash::digest($valid, $salt);
|
|
||||||
if (substr($digest, 0, self::CSRF_TOKEN_LENGTH) == $token) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception(pht('Unknown CSRF token format!'));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,9 +39,6 @@ final class PhabricatorPhameApplication extends PhabricatorApplication {
|
||||||
return array(
|
return array(
|
||||||
'/phame/' => array(
|
'/phame/' => array(
|
||||||
'' => 'PhamePostListController',
|
'' => 'PhamePostListController',
|
||||||
'r/(?P<id>\d+)/(?P<hash>[^/]+)/(?P<name>.*)'
|
|
||||||
=> 'PhameResourceController',
|
|
||||||
|
|
||||||
'live/(?P<id>[^/]+)/(?P<more>.*)' => 'PhameBlogLiveController',
|
'live/(?P<id>[^/]+)/(?P<more>.*)' => 'PhameBlogLiveController',
|
||||||
'post/' => array(
|
'post/' => array(
|
||||||
'(?:(?P<filter>draft|all)/)?' => 'PhamePostListController',
|
'(?:(?P<filter>draft|all)/)?' => 'PhamePostListController',
|
||||||
|
@ -65,6 +62,34 @@ final class PhabricatorPhameApplication extends PhabricatorApplication {
|
||||||
'feed/(?P<id>[^/]+)/' => 'PhameBlogFeedController',
|
'feed/(?P<id>[^/]+)/' => 'PhameBlogFeedController',
|
||||||
'new/' => 'PhameBlogEditController',
|
'new/' => 'PhameBlogEditController',
|
||||||
),
|
),
|
||||||
|
) + $this->getResourceSubroutes(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResourceRoutes() {
|
||||||
|
return array(
|
||||||
|
'/phame/' => $this->getResourceSubroutes(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getResourceSubroutes() {
|
||||||
|
return array(
|
||||||
|
'r/(?P<id>\d+)/(?P<hash>[^/]+)/(?P<name>.*)' =>
|
||||||
|
'PhameResourceController',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBlogRoutes() {
|
||||||
|
return array(
|
||||||
|
'/(?P<more>.*)' => 'PhameBlogLiveController',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBlogCDNRoutes() {
|
||||||
|
return array(
|
||||||
|
'/phame/' => array(
|
||||||
|
'r/(?P<id>\d+)/(?P<hash>[^/]+)/(?P<name>.*)' =>
|
||||||
|
'PhameResourceController',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,14 +8,20 @@ final class PhameBlogLiveController extends PhameController {
|
||||||
|
|
||||||
public function handleRequest(AphrontRequest $request) {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
$user = $request->getUser();
|
$user = $request->getUser();
|
||||||
$id = $request->getURIData('id');
|
|
||||||
|
|
||||||
$blog = id(new PhameBlogQuery())
|
$site = $request->getSite();
|
||||||
->setViewer($user)
|
if ($site instanceof PhameBlogSite) {
|
||||||
->withIDs(array($id))
|
$blog = $site->getBlog();
|
||||||
->executeOne();
|
} else {
|
||||||
if (!$blog) {
|
$id = $request->getURIData('id');
|
||||||
return new Aphront404Response();
|
|
||||||
|
$blog = id(new PhameBlogQuery())
|
||||||
|
->setViewer($user)
|
||||||
|
->withIDs(array($id))
|
||||||
|
->executeOne();
|
||||||
|
if (!$blog) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($blog->getDomain() && ($request->getHost() != $blog->getDomain())) {
|
if ($blog->getDomain() && ($request->getHost() != $blog->getDomain())) {
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class PhameBlogResourceSite extends PhameSite {
|
|
||||||
|
|
||||||
public function getDescription() {
|
|
||||||
return pht('Serves static resources for blogs.');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPriority() {
|
|
||||||
return 3000;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function newSiteForRequest(AphrontRequest $request) {
|
|
||||||
if (!$this->isPhameActive()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$whitelist = array(
|
|
||||||
'/phame/r/',
|
|
||||||
);
|
|
||||||
|
|
||||||
$path = $request->getPath();
|
|
||||||
if (!$this->isPathPrefixMatch($path, $whitelist)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PhameBlogResourceSite();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -24,7 +24,7 @@ final class PhameBlogSite extends PhameSite {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPriority() {
|
public function getPriority() {
|
||||||
return 4000;
|
return 3000;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function newSiteForRequest(AphrontRequest $request) {
|
public function newSiteForRequest(AphrontRequest $request) {
|
||||||
|
@ -53,11 +53,14 @@ final class PhameBlogSite extends PhameSite {
|
||||||
return id(new PhameBlogSite())->setBlog($blog);
|
return id(new PhameBlogSite())->setBlog($blog);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPathForRouting(AphrontRequest $request) {
|
public function getRoutingMaps() {
|
||||||
$path = $request->getPath();
|
$app = PhabricatorApplication::getByClass('PhabricatorPhameApplication');
|
||||||
$id = $this->getBlog()->getID();
|
|
||||||
|
|
||||||
return "/phame/live/{$id}/{$path}";
|
$maps = array();
|
||||||
|
$maps[] = $this->newRoutingMap()
|
||||||
|
->setApplication($app)
|
||||||
|
->setRoutes($app->getBlogRoutes());
|
||||||
|
return $maps;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -268,6 +268,7 @@ final class PhortuneCartViewController
|
||||||
$refund_uri = $this->getApplicationURI("{$prefix}cart/{$id}/refund/");
|
$refund_uri = $this->getApplicationURI("{$prefix}cart/{$id}/refund/");
|
||||||
$update_uri = $this->getApplicationURI("{$prefix}cart/{$id}/update/");
|
$update_uri = $this->getApplicationURI("{$prefix}cart/{$id}/update/");
|
||||||
$accept_uri = $this->getApplicationURI("{$prefix}cart/{$id}/accept/");
|
$accept_uri = $this->getApplicationURI("{$prefix}cart/{$id}/accept/");
|
||||||
|
$print_uri = $this->getApplicationURI("{$prefix}cart/{$id}/?__print__=1");
|
||||||
|
|
||||||
$view->addAction(
|
$view->addAction(
|
||||||
id(new PhabricatorActionView())
|
id(new PhabricatorActionView())
|
||||||
|
@ -309,6 +310,13 @@ final class PhortuneCartViewController
|
||||||
->setHref($resume_uri));
|
->setHref($resume_uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$view->addAction(
|
||||||
|
id(new PhabricatorActionView())
|
||||||
|
->setName(pht('Printable Version'))
|
||||||
|
->setHref($print_uri)
|
||||||
|
->setOpenInNewWindow(true)
|
||||||
|
->setIcon('fa-print'));
|
||||||
|
|
||||||
return $view;
|
return $view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,8 @@ final class PhrictionEditController
|
||||||
$notes = null;
|
$notes = null;
|
||||||
$title = $content->getTitle();
|
$title = $content->getTitle();
|
||||||
$overwrite = false;
|
$overwrite = false;
|
||||||
|
$v_cc = PhabricatorSubscribersQuery::loadSubscribersForPHID(
|
||||||
|
$document->getPHID());
|
||||||
|
|
||||||
if ($request->isFormPost()) {
|
if ($request->isFormPost()) {
|
||||||
|
|
||||||
|
@ -118,6 +120,7 @@ final class PhrictionEditController
|
||||||
$current_version = $request->getInt('contentVersion');
|
$current_version = $request->getInt('contentVersion');
|
||||||
$v_view = $request->getStr('viewPolicy');
|
$v_view = $request->getStr('viewPolicy');
|
||||||
$v_edit = $request->getStr('editPolicy');
|
$v_edit = $request->getStr('editPolicy');
|
||||||
|
$v_cc = $request->getArr('cc');
|
||||||
|
|
||||||
$xactions = array();
|
$xactions = array();
|
||||||
$xactions[] = id(new PhrictionTransaction())
|
$xactions[] = id(new PhrictionTransaction())
|
||||||
|
@ -132,6 +135,9 @@ final class PhrictionEditController
|
||||||
$xactions[] = id(new PhrictionTransaction())
|
$xactions[] = id(new PhrictionTransaction())
|
||||||
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
|
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
|
||||||
->setNewValue($v_edit);
|
->setNewValue($v_edit);
|
||||||
|
$xactions[] = id(new PhrictionTransaction())
|
||||||
|
->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)
|
||||||
|
->setNewValue(array('=' => $v_cc));
|
||||||
|
|
||||||
$editor = id(new PhrictionTransactionEditor())
|
$editor = id(new PhrictionTransactionEditor())
|
||||||
->setActor($viewer)
|
->setActor($viewer)
|
||||||
|
@ -222,6 +228,13 @@ final class PhrictionEditController
|
||||||
->setName('content')
|
->setName('content')
|
||||||
->setID('document-textarea')
|
->setID('document-textarea')
|
||||||
->setUser($viewer))
|
->setUser($viewer))
|
||||||
|
->appendControl(
|
||||||
|
id(new AphrontFormTokenizerControl())
|
||||||
|
->setLabel(pht('Subscribers'))
|
||||||
|
->setName('cc')
|
||||||
|
->setValue($v_cc)
|
||||||
|
->setUser($viewer)
|
||||||
|
->setDatasource(new PhabricatorMetaMTAMailableDatasource()))
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormPolicyControl())
|
id(new AphrontFormPolicyControl())
|
||||||
->setName('viewPolicy')
|
->setName('viewPolicy')
|
||||||
|
|
|
@ -6,6 +6,9 @@ final class PhabricatorPolicyException extends Exception {
|
||||||
private $rejection;
|
private $rejection;
|
||||||
private $capabilityName;
|
private $capabilityName;
|
||||||
private $moreInfo = array();
|
private $moreInfo = array();
|
||||||
|
private $objectPHID;
|
||||||
|
private $context;
|
||||||
|
private $capability;
|
||||||
|
|
||||||
public function setTitle($title) {
|
public function setTitle($title) {
|
||||||
$this->title = $title;
|
$this->title = $title;
|
||||||
|
@ -43,4 +46,31 @@ final class PhabricatorPolicyException extends Exception {
|
||||||
return $this->moreInfo;
|
return $this->moreInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setObjectPHID($object_phid) {
|
||||||
|
$this->objectPHID = $object_phid;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getObjectPHID() {
|
||||||
|
return $this->objectPHID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setContext($context) {
|
||||||
|
$this->context = $context;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContext() {
|
||||||
|
return $this->context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCapability($capability) {
|
||||||
|
$this->capability = $capability;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCapability() {
|
||||||
|
return $this->capability;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -589,7 +589,9 @@ final class PhabricatorPolicyFilter extends Phobject {
|
||||||
|
|
||||||
$exception = id(new PhabricatorPolicyException($full_message))
|
$exception = id(new PhabricatorPolicyException($full_message))
|
||||||
->setTitle($access_denied)
|
->setTitle($access_denied)
|
||||||
|
->setObjectPHID($object->getPHID())
|
||||||
->setRejection($rejection)
|
->setRejection($rejection)
|
||||||
|
->setCapability($capability)
|
||||||
->setCapabilityName($capability_name)
|
->setCapabilityName($capability_name)
|
||||||
->setMoreInfo($details);
|
->setMoreInfo($details);
|
||||||
|
|
||||||
|
@ -710,6 +712,11 @@ final class PhabricatorPolicyFilter extends Phobject {
|
||||||
$objects = $policy->getRuleObjects();
|
$objects = $policy->getRuleObjects();
|
||||||
$action = null;
|
$action = null;
|
||||||
foreach ($policy->getRules() as $rule) {
|
foreach ($policy->getRules() as $rule) {
|
||||||
|
if (!is_array($rule)) {
|
||||||
|
// Reject, this policy rule is invalid.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$rule_object = idx($objects, idx($rule, 'rule'));
|
$rule_object = idx($objects, idx($rule, 'rule'));
|
||||||
if (!$rule_object) {
|
if (!$rule_object) {
|
||||||
// Reject, this policy has a bogus rule.
|
// Reject, this policy has a bogus rule.
|
||||||
|
@ -831,7 +838,9 @@ final class PhabricatorPolicyFilter extends Phobject {
|
||||||
|
|
||||||
$exception = id(new PhabricatorPolicyException($full_message))
|
$exception = id(new PhabricatorPolicyException($full_message))
|
||||||
->setTitle($access_denied)
|
->setTitle($access_denied)
|
||||||
->setRejection($rejection);
|
->setObjectPHID($object->getPHID())
|
||||||
|
->setRejection($rejection)
|
||||||
|
->setCapability(PhabricatorPolicyCapability::CAN_VIEW);
|
||||||
|
|
||||||
throw $exception;
|
throw $exception;
|
||||||
}
|
}
|
||||||
|
|
|
@ -321,6 +321,12 @@ final class PhabricatorPolicy
|
||||||
$classes = array();
|
$classes = array();
|
||||||
|
|
||||||
foreach ($this->getRules() as $rule) {
|
foreach ($this->getRules() as $rule) {
|
||||||
|
if (!is_array($rule)) {
|
||||||
|
// This rule is invalid. We'll reject it later, but don't need to
|
||||||
|
// extract anything from it for now.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$class = idx($rule, 'rule');
|
$class = idx($rule, 'rule');
|
||||||
try {
|
try {
|
||||||
if (class_exists($class)) {
|
if (class_exists($class)) {
|
||||||
|
|
|
@ -5,14 +5,14 @@ final class PonderQuestionStatus extends PonderConstants {
|
||||||
const STATUS_OPEN = 'open';
|
const STATUS_OPEN = 'open';
|
||||||
const STATUS_CLOSED_RESOLVED = 'resolved';
|
const STATUS_CLOSED_RESOLVED = 'resolved';
|
||||||
const STATUS_CLOSED_OBSOLETE = 'obsolete';
|
const STATUS_CLOSED_OBSOLETE = 'obsolete';
|
||||||
const STATUS_CLOSED_DUPLICATE = 'duplicate';
|
const STATUS_CLOSED_INVALID = 'invalid';
|
||||||
|
|
||||||
public static function getQuestionStatusMap() {
|
public static function getQuestionStatusMap() {
|
||||||
return array(
|
return array(
|
||||||
self::STATUS_OPEN => pht('Open'),
|
self::STATUS_OPEN => pht('Open'),
|
||||||
self::STATUS_CLOSED_RESOLVED => pht('Closed, Resolved'),
|
self::STATUS_CLOSED_RESOLVED => pht('Closed, Resolved'),
|
||||||
self::STATUS_CLOSED_OBSOLETE => pht('Closed, Obsolete'),
|
self::STATUS_CLOSED_OBSOLETE => pht('Closed, Obsolete'),
|
||||||
self::STATUS_CLOSED_DUPLICATE => pht('Closed, Duplicate'),
|
self::STATUS_CLOSED_INVALID => pht('Closed, Invalid'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ final class PonderQuestionStatus extends PonderConstants {
|
||||||
self::STATUS_OPEN => pht('Open'),
|
self::STATUS_OPEN => pht('Open'),
|
||||||
self::STATUS_CLOSED_RESOLVED => pht('Closed, Resolved'),
|
self::STATUS_CLOSED_RESOLVED => pht('Closed, Resolved'),
|
||||||
self::STATUS_CLOSED_OBSOLETE => pht('Closed, Obsolete'),
|
self::STATUS_CLOSED_OBSOLETE => pht('Closed, Obsolete'),
|
||||||
self::STATUS_CLOSED_DUPLICATE => pht('Closed, Duplicate'),
|
self::STATUS_CLOSED_INVALID => pht('Closed, Invalid'),
|
||||||
);
|
);
|
||||||
return idx($map, $status, pht('Unknown'));
|
return idx($map, $status, pht('Unknown'));
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ final class PonderQuestionStatus extends PonderConstants {
|
||||||
self::STATUS_OPEN => pht('Open'),
|
self::STATUS_OPEN => pht('Open'),
|
||||||
self::STATUS_CLOSED_RESOLVED => pht('Resolved'),
|
self::STATUS_CLOSED_RESOLVED => pht('Resolved'),
|
||||||
self::STATUS_CLOSED_OBSOLETE => pht('Obsolete'),
|
self::STATUS_CLOSED_OBSOLETE => pht('Obsolete'),
|
||||||
self::STATUS_CLOSED_DUPLICATE => pht('Duplicate'),
|
self::STATUS_CLOSED_INVALID => pht('Invalid'),
|
||||||
);
|
);
|
||||||
return idx($map, $status, pht('Unknown'));
|
return idx($map, $status, pht('Unknown'));
|
||||||
}
|
}
|
||||||
|
@ -43,9 +43,9 @@ final class PonderQuestionStatus extends PonderConstants {
|
||||||
self::STATUS_CLOSED_RESOLVED =>
|
self::STATUS_CLOSED_RESOLVED =>
|
||||||
pht('This question has been answered or resolved.'),
|
pht('This question has been answered or resolved.'),
|
||||||
self::STATUS_CLOSED_OBSOLETE =>
|
self::STATUS_CLOSED_OBSOLETE =>
|
||||||
pht('This question is no longer valid or out of date.'),
|
pht('This question is out of date.'),
|
||||||
self::STATUS_CLOSED_DUPLICATE =>
|
self::STATUS_CLOSED_INVALID =>
|
||||||
pht('This question is a duplicate of another question.'),
|
pht('This question is invalid.'),
|
||||||
);
|
);
|
||||||
return idx($map, $status, pht('Unknown'));
|
return idx($map, $status, pht('Unknown'));
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ final class PonderQuestionStatus extends PonderConstants {
|
||||||
self::STATUS_OPEN => PHUITagView::COLOR_BLUE,
|
self::STATUS_OPEN => PHUITagView::COLOR_BLUE,
|
||||||
self::STATUS_CLOSED_RESOLVED => PHUITagView::COLOR_BLACK,
|
self::STATUS_CLOSED_RESOLVED => PHUITagView::COLOR_BLACK,
|
||||||
self::STATUS_CLOSED_OBSOLETE => PHUITagView::COLOR_BLACK,
|
self::STATUS_CLOSED_OBSOLETE => PHUITagView::COLOR_BLACK,
|
||||||
self::STATUS_CLOSED_DUPLICATE => PHUITagView::COLOR_BLACK,
|
self::STATUS_CLOSED_INVALID => PHUITagView::COLOR_BLACK,
|
||||||
);
|
);
|
||||||
|
|
||||||
return idx($map, $status);
|
return idx($map, $status);
|
||||||
|
@ -66,7 +66,7 @@ final class PonderQuestionStatus extends PonderConstants {
|
||||||
self::STATUS_OPEN => 'fa-question-circle',
|
self::STATUS_OPEN => 'fa-question-circle',
|
||||||
self::STATUS_CLOSED_RESOLVED => 'fa-check',
|
self::STATUS_CLOSED_RESOLVED => 'fa-check',
|
||||||
self::STATUS_CLOSED_OBSOLETE => 'fa-ban',
|
self::STATUS_CLOSED_OBSOLETE => 'fa-ban',
|
||||||
self::STATUS_CLOSED_DUPLICATE => 'fa-clone',
|
self::STATUS_CLOSED_INVALID => 'fa-ban',
|
||||||
);
|
);
|
||||||
|
|
||||||
return idx($map, $status);
|
return idx($map, $status);
|
||||||
|
@ -82,7 +82,7 @@ final class PonderQuestionStatus extends PonderConstants {
|
||||||
return array(
|
return array(
|
||||||
self::STATUS_CLOSED_RESOLVED,
|
self::STATUS_CLOSED_RESOLVED,
|
||||||
self::STATUS_CLOSED_OBSOLETE,
|
self::STATUS_CLOSED_OBSOLETE,
|
||||||
self::STATUS_CLOSED_DUPLICATE,
|
self::STATUS_CLOSED_INVALID,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ final class PonderQuestionEditController extends PonderController {
|
||||||
|
|
||||||
$v_title = $question->getTitle();
|
$v_title = $question->getTitle();
|
||||||
$v_content = $question->getContent();
|
$v_content = $question->getContent();
|
||||||
|
$v_wiki = $question->getAnswerWiki();
|
||||||
$v_view = $question->getViewPolicy();
|
$v_view = $question->getViewPolicy();
|
||||||
$v_space = $question->getSpacePHID();
|
$v_space = $question->getSpacePHID();
|
||||||
$v_status = $question->getStatus();
|
$v_status = $question->getStatus();
|
||||||
|
@ -42,6 +43,7 @@ final class PonderQuestionEditController extends PonderController {
|
||||||
if ($request->isFormPost()) {
|
if ($request->isFormPost()) {
|
||||||
$v_title = $request->getStr('title');
|
$v_title = $request->getStr('title');
|
||||||
$v_content = $request->getStr('content');
|
$v_content = $request->getStr('content');
|
||||||
|
$v_wiki = $request->getStr('answerWiki');
|
||||||
$v_projects = $request->getArr('projects');
|
$v_projects = $request->getArr('projects');
|
||||||
$v_view = $request->getStr('viewPolicy');
|
$v_view = $request->getStr('viewPolicy');
|
||||||
$v_space = $request->getStr('spacePHID');
|
$v_space = $request->getStr('spacePHID');
|
||||||
|
@ -68,6 +70,10 @@ final class PonderQuestionEditController extends PonderController {
|
||||||
->setTransactionType(PonderQuestionTransaction::TYPE_CONTENT)
|
->setTransactionType(PonderQuestionTransaction::TYPE_CONTENT)
|
||||||
->setNewValue($v_content);
|
->setNewValue($v_content);
|
||||||
|
|
||||||
|
$xactions[] = id(clone $template)
|
||||||
|
->setTransactionType(PonderQuestionTransaction::TYPE_ANSWERWIKI)
|
||||||
|
->setNewValue($v_wiki);
|
||||||
|
|
||||||
if (!$is_new) {
|
if (!$is_new) {
|
||||||
$xactions[] = id(clone $template)
|
$xactions[] = id(clone $template)
|
||||||
->setTransactionType(PonderQuestionTransaction::TYPE_STATUS)
|
->setTransactionType(PonderQuestionTransaction::TYPE_STATUS)
|
||||||
|
@ -119,7 +125,15 @@ final class PonderQuestionEditController extends PonderController {
|
||||||
->setName('content')
|
->setName('content')
|
||||||
->setID('content')
|
->setID('content')
|
||||||
->setValue($v_content)
|
->setValue($v_content)
|
||||||
->setLabel(pht('Description'))
|
->setLabel(pht('Question Details'))
|
||||||
|
->setUser($viewer))
|
||||||
|
->appendChild(
|
||||||
|
id(new PhabricatorRemarkupControl())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setName('answerWiki')
|
||||||
|
->setID('answerWiki')
|
||||||
|
->setValue($v_wiki)
|
||||||
|
->setLabel(pht('Answer Summary'))
|
||||||
->setUser($viewer))
|
->setUser($viewer))
|
||||||
->appendControl(
|
->appendControl(
|
||||||
id(new AphrontFormPolicyControl())
|
id(new AphrontFormPolicyControl())
|
||||||
|
@ -157,6 +171,11 @@ final class PonderQuestionEditController extends PonderController {
|
||||||
->setControlID('content')
|
->setControlID('content')
|
||||||
->setPreviewURI($this->getApplicationURI('preview/'));
|
->setPreviewURI($this->getApplicationURI('preview/'));
|
||||||
|
|
||||||
|
$answer_preview = id(new PHUIRemarkupPreviewPanel())
|
||||||
|
->setHeader(pht('Answer Summary Preview'))
|
||||||
|
->setControlID('answerWiki')
|
||||||
|
->setPreviewURI($this->getApplicationURI('preview/'));
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
|
|
||||||
$id = $question->getID();
|
$id = $question->getID();
|
||||||
|
@ -179,6 +198,7 @@ final class PonderQuestionEditController extends PonderController {
|
||||||
$crumbs,
|
$crumbs,
|
||||||
$form_box,
|
$form_box,
|
||||||
$preview,
|
$preview,
|
||||||
|
$answer_preview,
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
|
|
|
@ -20,7 +20,7 @@ final class PonderQuestionViewController extends PonderController {
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
$answers = $this->buildAnswers($question->getAnswers());
|
$answers = $this->buildAnswers($question);
|
||||||
|
|
||||||
$answer_add_panel = id(new PonderAddAnswerView())
|
$answer_add_panel = id(new PonderAddAnswerView())
|
||||||
->setQuestion($question)
|
->setQuestion($question)
|
||||||
|
@ -81,13 +81,37 @@ final class PonderQuestionViewController extends PonderController {
|
||||||
->addPropertyList($properties)
|
->addPropertyList($properties)
|
||||||
->appendChild($footer);
|
->appendChild($footer);
|
||||||
|
|
||||||
|
if ($viewer->getPHID() == $question->getAuthorPHID()) {
|
||||||
|
$status = $question->getStatus();
|
||||||
|
$answers_list = $question->getAnswers();
|
||||||
|
if ($answers_list && ($status == PonderQuestionStatus::STATUS_OPEN)) {
|
||||||
|
$info_view = id(new PHUIInfoView())
|
||||||
|
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
|
||||||
|
->appendChild(
|
||||||
|
pht(
|
||||||
|
'If this question has been resolved, please consider closing
|
||||||
|
the question and marking the answer as helpful.'));
|
||||||
|
$object_box->setInfoView($info_view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs($this->buildSideNavView());
|
$crumbs = $this->buildApplicationCrumbs($this->buildSideNavView());
|
||||||
$crumbs->addTextCrumb('Q'.$id, '/Q'.$id);
|
$crumbs->addTextCrumb('Q'.$id, '/Q'.$id);
|
||||||
|
|
||||||
|
$answer_wiki = null;
|
||||||
|
if ($question->getAnswerWiki()) {
|
||||||
|
$answer = phutil_tag_div('mlt mlb msr msl', $question->getAnswerWiki());
|
||||||
|
$answer_wiki = id(new PHUIObjectBoxView())
|
||||||
|
->setHeaderText(pht('Answer Summary'))
|
||||||
|
->setColor(PHUIObjectBoxView::COLOR_BLUE)
|
||||||
|
->appendChild($answer);
|
||||||
|
}
|
||||||
|
|
||||||
$ponder_view = id(new PHUITwoColumnView())
|
$ponder_view = id(new PHUITwoColumnView())
|
||||||
->setMainColumn(array(
|
->setMainColumn(array(
|
||||||
$object_box,
|
$object_box,
|
||||||
$comment_view,
|
$comment_view,
|
||||||
|
$answer_wiki,
|
||||||
$answers,
|
$answers,
|
||||||
$answer_add_panel,
|
$answer_add_panel,
|
||||||
))
|
))
|
||||||
|
@ -206,8 +230,9 @@ final class PonderQuestionViewController extends PonderController {
|
||||||
* TODO - re-factor this to ajax in one answer panel at a time in a more
|
* TODO - re-factor this to ajax in one answer panel at a time in a more
|
||||||
* standard fashion. This is necessary to scale this application.
|
* standard fashion. This is necessary to scale this application.
|
||||||
*/
|
*/
|
||||||
private function buildAnswers(array $answers) {
|
private function buildAnswers(PonderQuestion $question) {
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
|
$answers = $question->getAnswers();
|
||||||
|
|
||||||
$author_phids = mpull($answers, 'getAuthorPHID');
|
$author_phids = mpull($answers, 'getAuthorPHID');
|
||||||
$handles = $this->loadViewerHandles($author_phids);
|
$handles = $this->loadViewerHandles($author_phids);
|
||||||
|
|
|
@ -74,6 +74,7 @@ final class PonderQuestionEditor
|
||||||
$types[] = PonderQuestionTransaction::TYPE_CONTENT;
|
$types[] = PonderQuestionTransaction::TYPE_CONTENT;
|
||||||
$types[] = PonderQuestionTransaction::TYPE_ANSWERS;
|
$types[] = PonderQuestionTransaction::TYPE_ANSWERS;
|
||||||
$types[] = PonderQuestionTransaction::TYPE_STATUS;
|
$types[] = PonderQuestionTransaction::TYPE_STATUS;
|
||||||
|
$types[] = PonderQuestionTransaction::TYPE_ANSWERWIKI;
|
||||||
|
|
||||||
return $types;
|
return $types;
|
||||||
}
|
}
|
||||||
|
@ -91,6 +92,8 @@ final class PonderQuestionEditor
|
||||||
return mpull($object->getAnswers(), 'getPHID');
|
return mpull($object->getAnswers(), 'getPHID');
|
||||||
case PonderQuestionTransaction::TYPE_STATUS:
|
case PonderQuestionTransaction::TYPE_STATUS:
|
||||||
return $object->getStatus();
|
return $object->getStatus();
|
||||||
|
case PonderQuestionTransaction::TYPE_ANSWERWIKI:
|
||||||
|
return $object->getAnswerWiki();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,6 +105,7 @@ final class PonderQuestionEditor
|
||||||
case PonderQuestionTransaction::TYPE_TITLE:
|
case PonderQuestionTransaction::TYPE_TITLE:
|
||||||
case PonderQuestionTransaction::TYPE_CONTENT:
|
case PonderQuestionTransaction::TYPE_CONTENT:
|
||||||
case PonderQuestionTransaction::TYPE_STATUS:
|
case PonderQuestionTransaction::TYPE_STATUS:
|
||||||
|
case PonderQuestionTransaction::TYPE_ANSWERWIKI:
|
||||||
return $xaction->getNewValue();
|
return $xaction->getNewValue();
|
||||||
case PonderQuestionTransaction::TYPE_ANSWERS:
|
case PonderQuestionTransaction::TYPE_ANSWERS:
|
||||||
$raw_new_value = $xaction->getNewValue();
|
$raw_new_value = $xaction->getNewValue();
|
||||||
|
@ -136,6 +140,9 @@ final class PonderQuestionEditor
|
||||||
case PonderQuestionTransaction::TYPE_STATUS:
|
case PonderQuestionTransaction::TYPE_STATUS:
|
||||||
$object->setStatus($xaction->getNewValue());
|
$object->setStatus($xaction->getNewValue());
|
||||||
break;
|
break;
|
||||||
|
case PonderQuestionTransaction::TYPE_ANSWERWIKI:
|
||||||
|
$object->setAnswerWiki($xaction->getNewValue());
|
||||||
|
break;
|
||||||
case PonderQuestionTransaction::TYPE_ANSWERS:
|
case PonderQuestionTransaction::TYPE_ANSWERS:
|
||||||
$old = $xaction->getOldValue();
|
$old = $xaction->getOldValue();
|
||||||
$new = $xaction->getNewValue();
|
$new = $xaction->getNewValue();
|
||||||
|
@ -167,6 +174,7 @@ final class PonderQuestionEditor
|
||||||
case PonderQuestionTransaction::TYPE_TITLE:
|
case PonderQuestionTransaction::TYPE_TITLE:
|
||||||
case PonderQuestionTransaction::TYPE_CONTENT:
|
case PonderQuestionTransaction::TYPE_CONTENT:
|
||||||
case PonderQuestionTransaction::TYPE_STATUS:
|
case PonderQuestionTransaction::TYPE_STATUS:
|
||||||
|
case PonderQuestionTransaction::TYPE_ANSWERWIKI:
|
||||||
return $v;
|
return $v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ final class PonderQuestion extends PonderDAO
|
||||||
protected $authorPHID;
|
protected $authorPHID;
|
||||||
protected $status;
|
protected $status;
|
||||||
protected $content;
|
protected $content;
|
||||||
|
protected $answerWiki;
|
||||||
protected $contentSource;
|
protected $contentSource;
|
||||||
protected $viewPolicy;
|
protected $viewPolicy;
|
||||||
protected $spacePHID;
|
protected $spacePHID;
|
||||||
|
@ -56,6 +57,7 @@ final class PonderQuestion extends PonderDAO
|
||||||
'title' => 'text255',
|
'title' => 'text255',
|
||||||
'status' => 'text32',
|
'status' => 'text32',
|
||||||
'content' => 'text',
|
'content' => 'text',
|
||||||
|
'answerWiki' => 'text',
|
||||||
'answerCount' => 'uint32',
|
'answerCount' => 'uint32',
|
||||||
'mailKey' => 'bytes20',
|
'mailKey' => 'bytes20',
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ final class PonderQuestionTransaction
|
||||||
const TYPE_CONTENT = 'ponder.question:content';
|
const TYPE_CONTENT = 'ponder.question:content';
|
||||||
const TYPE_ANSWERS = 'ponder.question:answer';
|
const TYPE_ANSWERS = 'ponder.question:answer';
|
||||||
const TYPE_STATUS = 'ponder.question:status';
|
const TYPE_STATUS = 'ponder.question:status';
|
||||||
|
const TYPE_ANSWERWIKI = 'ponder.question:wiki';
|
||||||
|
|
||||||
const MAILTAG_DETAILS = 'question:details';
|
const MAILTAG_DETAILS = 'question:details';
|
||||||
const MAILTAG_COMMENT = 'question:comment';
|
const MAILTAG_COMMENT = 'question:comment';
|
||||||
|
@ -78,6 +79,10 @@ final class PonderQuestionTransaction
|
||||||
return pht(
|
return pht(
|
||||||
'%s edited the question description.',
|
'%s edited the question description.',
|
||||||
$this->renderHandleLink($author_phid));
|
$this->renderHandleLink($author_phid));
|
||||||
|
case self::TYPE_ANSWERWIKI:
|
||||||
|
return pht(
|
||||||
|
'%s edited the question answer wiki.',
|
||||||
|
$this->renderHandleLink($author_phid));
|
||||||
case self::TYPE_ANSWERS:
|
case self::TYPE_ANSWERS:
|
||||||
$answer_handle = $this->getHandle($this->getNewAnswerPHID());
|
$answer_handle = $this->getHandle($this->getNewAnswerPHID());
|
||||||
$question_handle = $this->getHandle($object_phid);
|
$question_handle = $this->getHandle($object_phid);
|
||||||
|
@ -100,9 +105,9 @@ final class PonderQuestionTransaction
|
||||||
return pht(
|
return pht(
|
||||||
'%s closed this question as obsolete.',
|
'%s closed this question as obsolete.',
|
||||||
$this->renderHandleLink($author_phid));
|
$this->renderHandleLink($author_phid));
|
||||||
case PonderQuestionStatus::STATUS_CLOSED_DUPLICATE:
|
case PonderQuestionStatus::STATUS_CLOSED_INVALID:
|
||||||
return pht(
|
return pht(
|
||||||
'%s closed this question as a duplicate.',
|
'%s closed this question as invalid.',
|
||||||
$this->renderHandleLink($author_phid));
|
$this->renderHandleLink($author_phid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,6 +125,7 @@ final class PonderQuestionTransaction
|
||||||
case self::TYPE_TITLE:
|
case self::TYPE_TITLE:
|
||||||
case self::TYPE_CONTENT:
|
case self::TYPE_CONTENT:
|
||||||
case self::TYPE_STATUS:
|
case self::TYPE_STATUS:
|
||||||
|
case self::TYPE_ANSWERWIKI:
|
||||||
$tags[] = self::MAILTAG_DETAILS;
|
$tags[] = self::MAILTAG_DETAILS;
|
||||||
break;
|
break;
|
||||||
case self::TYPE_ANSWERS:
|
case self::TYPE_ANSWERS:
|
||||||
|
@ -139,6 +145,7 @@ final class PonderQuestionTransaction
|
||||||
switch ($this->getTransactionType()) {
|
switch ($this->getTransactionType()) {
|
||||||
case self::TYPE_TITLE:
|
case self::TYPE_TITLE:
|
||||||
case self::TYPE_CONTENT:
|
case self::TYPE_CONTENT:
|
||||||
|
case self::TYPE_ANSWERWIKI:
|
||||||
return 'fa-pencil';
|
return 'fa-pencil';
|
||||||
case self::TYPE_STATUS:
|
case self::TYPE_STATUS:
|
||||||
return PonderQuestionStatus::getQuestionStatusIcon($new);
|
return PonderQuestionStatus::getQuestionStatusIcon($new);
|
||||||
|
@ -156,6 +163,7 @@ final class PonderQuestionTransaction
|
||||||
switch ($this->getTransactionType()) {
|
switch ($this->getTransactionType()) {
|
||||||
case self::TYPE_TITLE:
|
case self::TYPE_TITLE:
|
||||||
case self::TYPE_CONTENT:
|
case self::TYPE_CONTENT:
|
||||||
|
case self::TYPE_ANSWERWIKI:
|
||||||
return PhabricatorTransactions::COLOR_BLUE;
|
return PhabricatorTransactions::COLOR_BLUE;
|
||||||
case self::TYPE_ANSWERS:
|
case self::TYPE_ANSWERS:
|
||||||
return PhabricatorTransactions::COLOR_GREEN;
|
return PhabricatorTransactions::COLOR_GREEN;
|
||||||
|
@ -167,6 +175,7 @@ final class PonderQuestionTransaction
|
||||||
public function hasChangeDetails() {
|
public function hasChangeDetails() {
|
||||||
switch ($this->getTransactionType()) {
|
switch ($this->getTransactionType()) {
|
||||||
case self::TYPE_CONTENT:
|
case self::TYPE_CONTENT:
|
||||||
|
case self::TYPE_ANSWERWIKI:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return parent::hasChangeDetails();
|
return parent::hasChangeDetails();
|
||||||
|
@ -253,6 +262,11 @@ final class PonderQuestionTransaction
|
||||||
'%s edited the description of %s',
|
'%s edited the description of %s',
|
||||||
$this->renderHandleLink($author_phid),
|
$this->renderHandleLink($author_phid),
|
||||||
$this->renderHandleLink($object_phid));
|
$this->renderHandleLink($object_phid));
|
||||||
|
case self::TYPE_ANSWERWIKI:
|
||||||
|
return pht(
|
||||||
|
'%s edited the answer wiki for %s',
|
||||||
|
$this->renderHandleLink($author_phid),
|
||||||
|
$this->renderHandleLink($object_phid));
|
||||||
case self::TYPE_ANSWERS:
|
case self::TYPE_ANSWERS:
|
||||||
$answer_handle = $this->getHandle($this->getNewAnswerPHID());
|
$answer_handle = $this->getHandle($this->getNewAnswerPHID());
|
||||||
$question_handle = $this->getHandle($object_phid);
|
$question_handle = $this->getHandle($object_phid);
|
||||||
|
@ -272,9 +286,9 @@ final class PonderQuestionTransaction
|
||||||
'%s closed %s as resolved.',
|
'%s closed %s as resolved.',
|
||||||
$this->renderHandleLink($author_phid),
|
$this->renderHandleLink($author_phid),
|
||||||
$this->renderHandleLink($object_phid));
|
$this->renderHandleLink($object_phid));
|
||||||
case PonderQuestionStatus::STATUS_CLOSED_DUPLICATE:
|
case PonderQuestionStatus::STATUS_CLOSED_INVALID:
|
||||||
return pht(
|
return pht(
|
||||||
'%s closed %s as duplicate.',
|
'%s closed %s as invalid.',
|
||||||
$this->renderHandleLink($author_phid),
|
$this->renderHandleLink($author_phid),
|
||||||
$this->renderHandleLink($object_phid));
|
$this->renderHandleLink($object_phid));
|
||||||
case PonderQuestionStatus::STATUS_CLOSED_OBSOLETE:
|
case PonderQuestionStatus::STATUS_CLOSED_OBSOLETE:
|
||||||
|
|
|
@ -90,6 +90,18 @@ final class PonderAddAnswerView extends AphrontView {
|
||||||
id(new AphrontFormSubmitControl())
|
id(new AphrontFormSubmitControl())
|
||||||
->setValue(pht('Add Answer')));
|
->setValue(pht('Add Answer')));
|
||||||
|
|
||||||
|
if (!$viewer->isLoggedIn()) {
|
||||||
|
$login_href = id(new PhutilURI('/auth/start/'))
|
||||||
|
->setQueryParam('next', '/Q'.$question->getID());
|
||||||
|
$form = id(new PHUIFormLayoutView())
|
||||||
|
->addClass('login-to-participate')
|
||||||
|
->appendChild(
|
||||||
|
id(new PHUIButtonView())
|
||||||
|
->setTag('a')
|
||||||
|
->setText(pht('Login to Answer'))
|
||||||
|
->setHref((string)$login_href));
|
||||||
|
}
|
||||||
|
|
||||||
$box = id(new PHUIObjectBoxView())
|
$box = id(new PHUIObjectBoxView())
|
||||||
->setHeader($header)
|
->setHeader($header)
|
||||||
->appendChild($form);
|
->appendChild($form);
|
||||||
|
|
|
@ -37,7 +37,7 @@ final class PonderFooterView extends AphrontTagView {
|
||||||
|
|
||||||
if ($this->count == 0) {
|
if ($this->count == 0) {
|
||||||
$icon = id(new PHUIIconView())
|
$icon = id(new PHUIIconView())
|
||||||
->setIconFont('fa-plus-circle msr');
|
->setIconFont('fa-comments msr');
|
||||||
$text = pht('Add a Comment');
|
$text = pht('Add a Comment');
|
||||||
} else {
|
} else {
|
||||||
$icon = id(new PHUIIconView())
|
$icon = id(new PHUIIconView())
|
||||||
|
@ -78,7 +78,7 @@ final class PonderFooterView extends AphrontTagView {
|
||||||
$actions[] = $hide_action;
|
$actions[] = $hide_action;
|
||||||
$actions[] = $show_action;
|
$actions[] = $show_action;
|
||||||
|
|
||||||
return array($this->actions, $actions);
|
return array($actions, $this->actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -624,8 +624,7 @@ final class PhabricatorProjectBoardViewController
|
||||||
->setMetadata(
|
->setMetadata(
|
||||||
array(
|
array(
|
||||||
'columnPHID' => $column->getPHID(),
|
'columnPHID' => $column->getPHID(),
|
||||||
))
|
));
|
||||||
->setDisabled(!$can_edit);
|
|
||||||
|
|
||||||
$batch_edit_uri = $request->getRequestURI();
|
$batch_edit_uri = $request->getRequestURI();
|
||||||
$batch_edit_uri->setQueryParam('batch', $column->getID());
|
$batch_edit_uri->setQueryParam('batch', $column->getID());
|
||||||
|
@ -640,15 +639,13 @@ final class PhabricatorProjectBoardViewController
|
||||||
->setHref($batch_edit_uri)
|
->setHref($batch_edit_uri)
|
||||||
->setDisabled(!$can_batch_edit);
|
->setDisabled(!$can_batch_edit);
|
||||||
|
|
||||||
$edit_uri = $this->getApplicationURI(
|
$detail_uri = $this->getApplicationURI(
|
||||||
'board/'.$this->id.'/column/'.$column->getID().'/');
|
'board/'.$this->id.'/column/'.$column->getID().'/');
|
||||||
|
|
||||||
$column_items[] = id(new PhabricatorActionView())
|
$column_items[] = id(new PhabricatorActionView())
|
||||||
->setIcon('fa-pencil')
|
->setIcon('fa-columns')
|
||||||
->setName(pht('Edit Column'))
|
->setName(pht('Column Details'))
|
||||||
->setHref($edit_uri)
|
->setHref($detail_uri);
|
||||||
->setDisabled(!$can_edit)
|
|
||||||
->setWorkflow(!$can_edit);
|
|
||||||
|
|
||||||
$can_hide = ($can_edit && !$column->isDefaultColumn());
|
$can_hide = ($can_edit && !$column->isDefaultColumn());
|
||||||
$hide_uri = 'board/'.$this->id.'/hide/'.$column->getID().'/';
|
$hide_uri = 'board/'.$this->id.'/hide/'.$column->getID().'/';
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue