Merge branch 'master' into blender-tweaks
This commit is contained in:
@@ -1 +0,0 @@
|
||||
../scripts/user/account_admin.php
|
@@ -1 +0,0 @@
|
||||
../scripts/people/manage_people.php
|
@@ -9,10 +9,10 @@ return array(
|
||||
'names' => array(
|
||||
'conpherence.pkg.css' => '3c8a0668',
|
||||
'conpherence.pkg.js' => '020aebcf',
|
||||
'core.pkg.css' => 'efa1b78b',
|
||||
'core.pkg.js' => '8225dc58',
|
||||
'core.pkg.css' => 'eef4903d',
|
||||
'core.pkg.js' => '73a06a9f',
|
||||
'differential.pkg.css' => '8d8360fb',
|
||||
'differential.pkg.js' => '67e02996',
|
||||
'differential.pkg.js' => '0b037a4f',
|
||||
'diffusion.pkg.css' => '42c75c37',
|
||||
'diffusion.pkg.js' => 'a98c0bf7',
|
||||
'maniphest.pkg.css' => '35995d6d',
|
||||
@@ -24,7 +24,7 @@ return array(
|
||||
'rsrc/audio/basic/ting.mp3' => 'a6b6540e',
|
||||
'rsrc/css/aphront/aphront-bars.css' => '4a327b4a',
|
||||
'rsrc/css/aphront/dark-console.css' => '7f06cda2',
|
||||
'rsrc/css/aphront/dialog-view.css' => 'b70c70df',
|
||||
'rsrc/css/aphront/dialog-view.css' => '874f5c06',
|
||||
'rsrc/css/aphront/list-filter-view.css' => 'feb64255',
|
||||
'rsrc/css/aphront/multi-column.css' => 'fbc00ba3',
|
||||
'rsrc/css/aphront/notification.css' => '30240bd2',
|
||||
@@ -92,7 +92,7 @@ return array(
|
||||
'rsrc/css/application/pholio/pholio.css' => '88ef5ef1',
|
||||
'rsrc/css/application/phortune/phortune-credit-card-form.css' => '3b9868a8',
|
||||
'rsrc/css/application/phortune/phortune-invoice.css' => '4436b241',
|
||||
'rsrc/css/application/phortune/phortune.css' => '12e8251a',
|
||||
'rsrc/css/application/phortune/phortune.css' => '508a1a5e',
|
||||
'rsrc/css/application/phrequent/phrequent.css' => 'bd79cc67',
|
||||
'rsrc/css/application/phriction/phriction-document-css.css' => '03380da0',
|
||||
'rsrc/css/application/policy/policy-edit.css' => '8794e2ed',
|
||||
@@ -255,7 +255,7 @@ return array(
|
||||
'rsrc/externals/javelin/lib/URI.js' => '2e255291',
|
||||
'rsrc/externals/javelin/lib/Vector.js' => 'e9c80beb',
|
||||
'rsrc/externals/javelin/lib/WebSocket.js' => 'fdc13e4e',
|
||||
'rsrc/externals/javelin/lib/Workflow.js' => '851f642d',
|
||||
'rsrc/externals/javelin/lib/Workflow.js' => '945ff654',
|
||||
'rsrc/externals/javelin/lib/__tests__/Cookie.js' => 'ca686f71',
|
||||
'rsrc/externals/javelin/lib/__tests__/DOM.js' => '4566e249',
|
||||
'rsrc/externals/javelin/lib/__tests__/JSON.js' => '710377ae',
|
||||
@@ -414,16 +414,16 @@ return array(
|
||||
'rsrc/js/application/phortune/phortune-credit-card-form.js' => 'd12d214f',
|
||||
'rsrc/js/application/policy/behavior-policy-control.js' => '0eaa33a9',
|
||||
'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '9347f172',
|
||||
'rsrc/js/application/projects/WorkboardBoard.js' => 'c02a5497',
|
||||
'rsrc/js/application/projects/WorkboardBoard.js' => 'b46d88c5',
|
||||
'rsrc/js/application/projects/WorkboardCard.js' => '0392a5d8',
|
||||
'rsrc/js/application/projects/WorkboardCardTemplate.js' => '2a61f8d4',
|
||||
'rsrc/js/application/projects/WorkboardCardTemplate.js' => '84f82dad',
|
||||
'rsrc/js/application/projects/WorkboardColumn.js' => 'c3d24e63',
|
||||
'rsrc/js/application/projects/WorkboardController.js' => '42c7a5a7',
|
||||
'rsrc/js/application/projects/WorkboardController.js' => 'b9d0c2f3',
|
||||
'rsrc/js/application/projects/WorkboardDropEffect.js' => '8e0aa661',
|
||||
'rsrc/js/application/projects/WorkboardHeader.js' => '111bfd2d',
|
||||
'rsrc/js/application/projects/WorkboardHeaderTemplate.js' => 'ebe83a6b',
|
||||
'rsrc/js/application/projects/WorkboardOrderTemplate.js' => '03e8891f',
|
||||
'rsrc/js/application/projects/behavior-project-boards.js' => 'aad45445',
|
||||
'rsrc/js/application/projects/behavior-project-boards.js' => '58cb6a88',
|
||||
'rsrc/js/application/projects/behavior-project-create.js' => '34c53422',
|
||||
'rsrc/js/application/projects/behavior-reorder-columns.js' => '8ac32fd9',
|
||||
'rsrc/js/application/releeph/releeph-preview-branch.js' => '75184d68',
|
||||
@@ -486,7 +486,7 @@ return array(
|
||||
'rsrc/js/core/behavior-line-linker.js' => 'e15c8b1f',
|
||||
'rsrc/js/core/behavior-linked-container.js' => '74446546',
|
||||
'rsrc/js/core/behavior-more.js' => '506aa3f4',
|
||||
'rsrc/js/core/behavior-object-selector.js' => 'a4af0b4a',
|
||||
'rsrc/js/core/behavior-object-selector.js' => '98ef467f',
|
||||
'rsrc/js/core/behavior-oncopy.js' => 'ff7b3f22',
|
||||
'rsrc/js/core/behavior-phabricator-nav.js' => 'f166c949',
|
||||
'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '2f80333f',
|
||||
@@ -532,7 +532,7 @@ return array(
|
||||
'almanac-css' => '2e050f4f',
|
||||
'aphront-bars' => '4a327b4a',
|
||||
'aphront-dark-console-css' => '7f06cda2',
|
||||
'aphront-dialog-view-css' => 'b70c70df',
|
||||
'aphront-dialog-view-css' => '874f5c06',
|
||||
'aphront-list-filter-view-css' => 'feb64255',
|
||||
'aphront-multi-column-view-css' => 'fbc00ba3',
|
||||
'aphront-panel-view-css' => '46923d46',
|
||||
@@ -647,7 +647,7 @@ return array(
|
||||
'javelin-behavior-phabricator-line-linker' => 'e15c8b1f',
|
||||
'javelin-behavior-phabricator-nav' => 'f166c949',
|
||||
'javelin-behavior-phabricator-notification-example' => '29819b75',
|
||||
'javelin-behavior-phabricator-object-selector' => 'a4af0b4a',
|
||||
'javelin-behavior-phabricator-object-selector' => '98ef467f',
|
||||
'javelin-behavior-phabricator-oncopy' => 'ff7b3f22',
|
||||
'javelin-behavior-phabricator-remarkup-assist' => '2f80333f',
|
||||
'javelin-behavior-phabricator-reveal-content' => 'b105a3a6',
|
||||
@@ -669,7 +669,7 @@ return array(
|
||||
'javelin-behavior-phuix-example' => 'c2c500a7',
|
||||
'javelin-behavior-policy-control' => '0eaa33a9',
|
||||
'javelin-behavior-policy-rule-editor' => '9347f172',
|
||||
'javelin-behavior-project-boards' => 'aad45445',
|
||||
'javelin-behavior-project-boards' => '58cb6a88',
|
||||
'javelin-behavior-project-create' => '34c53422',
|
||||
'javelin-behavior-quicksand-blacklist' => '5a6f6a06',
|
||||
'javelin-behavior-read-only-warning' => 'b9109f8f',
|
||||
@@ -745,16 +745,16 @@ return array(
|
||||
'javelin-view-renderer' => '9aae2b66',
|
||||
'javelin-view-visitor' => '308f9fe4',
|
||||
'javelin-websocket' => 'fdc13e4e',
|
||||
'javelin-workboard-board' => 'c02a5497',
|
||||
'javelin-workboard-board' => 'b46d88c5',
|
||||
'javelin-workboard-card' => '0392a5d8',
|
||||
'javelin-workboard-card-template' => '2a61f8d4',
|
||||
'javelin-workboard-card-template' => '84f82dad',
|
||||
'javelin-workboard-column' => 'c3d24e63',
|
||||
'javelin-workboard-controller' => '42c7a5a7',
|
||||
'javelin-workboard-controller' => 'b9d0c2f3',
|
||||
'javelin-workboard-drop-effect' => '8e0aa661',
|
||||
'javelin-workboard-header' => '111bfd2d',
|
||||
'javelin-workboard-header-template' => 'ebe83a6b',
|
||||
'javelin-workboard-order-template' => '03e8891f',
|
||||
'javelin-workflow' => '851f642d',
|
||||
'javelin-workflow' => '945ff654',
|
||||
'maniphest-report-css' => '3d53188b',
|
||||
'maniphest-task-edit-css' => '272daa84',
|
||||
'maniphest-task-summary-css' => '61d1667e',
|
||||
@@ -813,7 +813,7 @@ return array(
|
||||
'pholio-inline-comments-css' => '722b48c2',
|
||||
'phortune-credit-card-form' => 'd12d214f',
|
||||
'phortune-credit-card-form-css' => '3b9868a8',
|
||||
'phortune-css' => '12e8251a',
|
||||
'phortune-css' => '508a1a5e',
|
||||
'phortune-invoice-css' => '4436b241',
|
||||
'phrequent-css' => 'bd79cc67',
|
||||
'phriction-document-css' => '03380da0',
|
||||
@@ -1133,9 +1133,6 @@ return array(
|
||||
'javelin-stratcom',
|
||||
'javelin-behavior',
|
||||
),
|
||||
'2a61f8d4' => array(
|
||||
'javelin-install',
|
||||
),
|
||||
'2a8b62d9' => array(
|
||||
'multirow-row-manager',
|
||||
'javelin-install',
|
||||
@@ -1264,16 +1261,6 @@ return array(
|
||||
'4234f572' => array(
|
||||
'syntax-default-css',
|
||||
),
|
||||
'42c7a5a7' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-vector',
|
||||
'javelin-stratcom',
|
||||
'javelin-workflow',
|
||||
'phabricator-drag-and-drop-file-upload',
|
||||
'javelin-workboard-board',
|
||||
),
|
||||
'4370900d' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
@@ -1412,6 +1399,16 @@ return array(
|
||||
'javelin-vector',
|
||||
'javelin-typeahead-static-source',
|
||||
),
|
||||
'58cb6a88' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-vector',
|
||||
'javelin-stratcom',
|
||||
'javelin-workflow',
|
||||
'javelin-workboard-controller',
|
||||
'javelin-workboard-drop-effect',
|
||||
),
|
||||
'5902260c' => array(
|
||||
'javelin-util',
|
||||
'javelin-magical-init',
|
||||
@@ -1607,16 +1604,8 @@ return array(
|
||||
'javelin-resource',
|
||||
'javelin-routable',
|
||||
),
|
||||
'851f642d' => array(
|
||||
'javelin-stratcom',
|
||||
'javelin-request',
|
||||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
'84f82dad' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-mask',
|
||||
'javelin-uri',
|
||||
'javelin-routable',
|
||||
),
|
||||
'87428eb2' => array(
|
||||
'javelin-behavior',
|
||||
@@ -1709,6 +1698,17 @@ return array(
|
||||
'javelin-typeahead-preloaded-source',
|
||||
'javelin-util',
|
||||
),
|
||||
'945ff654' => array(
|
||||
'javelin-stratcom',
|
||||
'javelin-request',
|
||||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-mask',
|
||||
'javelin-uri',
|
||||
'javelin-routable',
|
||||
),
|
||||
'94681e22' => array(
|
||||
'javelin-magical-init',
|
||||
'javelin-install',
|
||||
@@ -1730,6 +1730,12 @@ return array(
|
||||
'javelin-dom',
|
||||
'javelin-router',
|
||||
),
|
||||
'98ef467f' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-request',
|
||||
'javelin-util',
|
||||
),
|
||||
'9aae2b66' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
@@ -1790,12 +1796,6 @@ return array(
|
||||
'phui-button-css',
|
||||
'phui-button-simple-css',
|
||||
),
|
||||
'a4af0b4a' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-request',
|
||||
'javelin-util',
|
||||
),
|
||||
'a5257c4e' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
@@ -1843,16 +1843,6 @@ return array(
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
),
|
||||
'aad45445' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-vector',
|
||||
'javelin-stratcom',
|
||||
'javelin-workflow',
|
||||
'javelin-workboard-controller',
|
||||
'javelin-workboard-drop-effect',
|
||||
),
|
||||
'ab85e184' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
@@ -1901,6 +1891,18 @@ return array(
|
||||
'b347a301' => array(
|
||||
'javelin-behavior',
|
||||
),
|
||||
'b46d88c5' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-stratcom',
|
||||
'javelin-workflow',
|
||||
'phabricator-draggable-list',
|
||||
'javelin-workboard-column',
|
||||
'javelin-workboard-header-template',
|
||||
'javelin-workboard-card-template',
|
||||
'javelin-workboard-order-template',
|
||||
),
|
||||
'b49fd60c' => array(
|
||||
'multirow-row-manager',
|
||||
'trigger-rule',
|
||||
@@ -1943,20 +1945,18 @@ return array(
|
||||
'javelin-uri',
|
||||
'phabricator-notification',
|
||||
),
|
||||
'bde53589' => array(
|
||||
'phui-inline-comment-view-css',
|
||||
),
|
||||
'c02a5497' => array(
|
||||
'b9d0c2f3' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-vector',
|
||||
'javelin-stratcom',
|
||||
'javelin-workflow',
|
||||
'phabricator-draggable-list',
|
||||
'javelin-workboard-column',
|
||||
'javelin-workboard-header-template',
|
||||
'javelin-workboard-card-template',
|
||||
'javelin-workboard-order-template',
|
||||
'phabricator-drag-and-drop-file-upload',
|
||||
'javelin-workboard-board',
|
||||
),
|
||||
'bde53589' => array(
|
||||
'phui-inline-comment-view-css',
|
||||
),
|
||||
'c03f2fb4' => array(
|
||||
'javelin-install',
|
||||
|
2
resources/sql/autopatches/20190718.paste.01.edge.sql
Normal file
2
resources/sql/autopatches/20190718.paste.01.edge.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
RENAME TABLE {$NAMESPACE}_pastebin.edge
|
||||
TO {$NAMESPACE}_paste.edge;
|
2
resources/sql/autopatches/20190718.paste.02.edgedata.sql
Normal file
2
resources/sql/autopatches/20190718.paste.02.edgedata.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
RENAME TABLE {$NAMESPACE}_pastebin.edgedata
|
||||
TO {$NAMESPACE}_paste.edgedata;
|
2
resources/sql/autopatches/20190718.paste.03.paste.sql
Normal file
2
resources/sql/autopatches/20190718.paste.03.paste.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
RENAME TABLE {$NAMESPACE}_pastebin.pastebin_paste
|
||||
TO {$NAMESPACE}_paste.paste;
|
2
resources/sql/autopatches/20190718.paste.04.xaction.sql
Normal file
2
resources/sql/autopatches/20190718.paste.04.xaction.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
RENAME TABLE {$NAMESPACE}_pastebin.pastebin_pastetransaction
|
||||
TO {$NAMESPACE}_paste.paste_transaction;
|
2
resources/sql/autopatches/20190718.paste.05.comment.sql
Normal file
2
resources/sql/autopatches/20190718.paste.05.comment.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
RENAME TABLE {$NAMESPACE}_pastebin.pastebin_pastetransaction_comment
|
||||
TO {$NAMESPACE}_paste.paste_transaction_comment;
|
12
resources/sql/autopatches/20190802.email.01.storage.sql
Normal file
12
resources/sql/autopatches/20190802.email.01.storage.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
CREATE TABLE {$NAMESPACE}_phortune.phortune_accountemail (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
accountPHID VARBINARY(64) NOT NULL,
|
||||
authorPHID VARBINARY(64) NOT NULL,
|
||||
address VARCHAR(128) NOT NULL COLLATE {$COLLATE_SORT},
|
||||
status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
addressKey VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
accessKey VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE {$COLLATE_TEXT};
|
19
resources/sql/autopatches/20190802.email.02.xaction.sql
Normal file
19
resources/sql/autopatches/20190802.email.02.xaction.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
CREATE TABLE {$NAMESPACE}_phortune.phortune_accountemailtransaction (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
authorPHID VARBINARY(64) NOT NULL,
|
||||
objectPHID VARBINARY(64) NOT NULL,
|
||||
viewPolicy VARBINARY(64) NOT NULL,
|
||||
editPolicy VARBINARY(64) NOT NULL,
|
||||
commentPHID VARBINARY(64) DEFAULT NULL,
|
||||
commentVersion INT UNSIGNED NOT NULL,
|
||||
transactionType VARCHAR(32) NOT NULL,
|
||||
oldValue LONGTEXT NOT NULL,
|
||||
newValue LONGTEXT NOT NULL,
|
||||
contentSource LONGTEXT NOT NULL,
|
||||
metadata LONGTEXT NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_phid` (`phid`),
|
||||
KEY `key_object` (`objectPHID`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE {$COLLATE_TEXT};
|
10
resources/sql/autopatches/20190815.account.01.carts.php
Normal file
10
resources/sql/autopatches/20190815.account.01.carts.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
$edge_type = PhortuneAccountHasMerchantEdgeType::EDGECONST;
|
||||
|
||||
$table = new PhortuneCart();
|
||||
foreach (new LiskMigrationIterator($table) as $cart) {
|
||||
id(new PhabricatorEdgeEditor())
|
||||
->addEdge($cart->getAccountPHID(), $edge_type, $cart->getMerchantPHID())
|
||||
->save();
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
$edge_type = PhortuneAccountHasMerchantEdgeType::EDGECONST;
|
||||
|
||||
$table = new PhortuneSubscription();
|
||||
foreach (new LiskMigrationIterator($table) as $sub) {
|
||||
id(new PhabricatorEdgeEditor())
|
||||
->addEdge($sub->getAccountPHID(), $edge_type, $sub->getMerchantPHID())
|
||||
->save();
|
||||
}
|
19
resources/sql/autopatches/20190816.payment.01.xaction.sql
Normal file
19
resources/sql/autopatches/20190816.payment.01.xaction.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
CREATE TABLE {$NAMESPACE}_phortune.phortune_paymentmethodtransaction (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
authorPHID VARBINARY(64) NOT NULL,
|
||||
objectPHID VARBINARY(64) NOT NULL,
|
||||
viewPolicy VARBINARY(64) NOT NULL,
|
||||
editPolicy VARBINARY(64) NOT NULL,
|
||||
commentPHID VARBINARY(64) DEFAULT NULL,
|
||||
commentVersion INT UNSIGNED NOT NULL,
|
||||
transactionType VARCHAR(32) NOT NULL,
|
||||
oldValue LONGTEXT NOT NULL,
|
||||
newValue LONGTEXT NOT NULL,
|
||||
contentSource LONGTEXT NOT NULL,
|
||||
metadata LONGTEXT NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_phid` (`phid`),
|
||||
KEY `key_object` (`objectPHID`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE {$COLLATE_TEXT};
|
@@ -0,0 +1,19 @@
|
||||
CREATE TABLE {$NAMESPACE}_phortune.phortune_subscriptiontransaction (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
authorPHID VARBINARY(64) NOT NULL,
|
||||
objectPHID VARBINARY(64) NOT NULL,
|
||||
viewPolicy VARBINARY(64) NOT NULL,
|
||||
editPolicy VARBINARY(64) NOT NULL,
|
||||
commentPHID VARBINARY(64) DEFAULT NULL,
|
||||
commentVersion INT UNSIGNED NOT NULL,
|
||||
transactionType VARCHAR(32) NOT NULL,
|
||||
oldValue LONGTEXT NOT NULL,
|
||||
newValue LONGTEXT NOT NULL,
|
||||
contentSource LONGTEXT NOT NULL,
|
||||
metadata LONGTEXT NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_phid` (`phid`),
|
||||
KEY `key_object` (`objectPHID`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE {$COLLATE_TEXT};
|
2
resources/sql/autopatches/20190822.merchant.01.view.sql
Normal file
2
resources/sql/autopatches/20190822.merchant.01.view.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_phortune.phortune_merchant
|
||||
DROP viewPolicy;
|
@@ -6,8 +6,8 @@ require_once $root.'/scripts/__init_script__.php';
|
||||
|
||||
$args = new PhutilArgumentParser($argv);
|
||||
$args->setSynopsis(<<<EOSYNOPSIS
|
||||
**people** __command__ [__options__]
|
||||
Manage user profiles and accounts.
|
||||
**user** __command__ [__options__]
|
||||
Modify user accounts to regain access to an install.
|
||||
|
||||
EOSYNOPSIS
|
||||
);
|
@@ -1,228 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
$root = dirname(dirname(dirname(__FILE__)));
|
||||
require_once $root.'/scripts/__init_script__.php';
|
||||
|
||||
$table = new PhabricatorUser();
|
||||
$any_user = queryfx_one(
|
||||
$table->establishConnection('r'),
|
||||
'SELECT * FROM %T LIMIT 1',
|
||||
$table->getTableName());
|
||||
$is_first_user = (!$any_user);
|
||||
|
||||
if ($is_first_user) {
|
||||
echo pht(
|
||||
"WARNING\n\n".
|
||||
"You're about to create the first account on this install. Normally, ".
|
||||
"you should use the web interface to create the first account, not ".
|
||||
"this script.\n\n".
|
||||
"If you use the web interface, it will drop you into a nice UI workflow ".
|
||||
"which gives you more help setting up your install. If you create an ".
|
||||
"account with this script instead, you will skip the setup help and you ".
|
||||
"will not be able to access it later.");
|
||||
if (!phutil_console_confirm(pht('Skip easy setup and create account?'))) {
|
||||
echo pht('Cancelled.')."\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
echo pht(
|
||||
'Enter a username to create a new account or edit an existing account.');
|
||||
|
||||
$username = phutil_console_prompt(pht('Enter a username:'));
|
||||
if (!strlen($username)) {
|
||||
echo pht('Cancelled.')."\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!PhabricatorUser::validateUsername($username)) {
|
||||
$valid = PhabricatorUser::describeValidUsername();
|
||||
echo pht("The username '%s' is invalid. %s", $username, $valid)."\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
$user = id(new PhabricatorUser())->loadOneWhere(
|
||||
'username = %s',
|
||||
$username);
|
||||
|
||||
if (!$user) {
|
||||
$original = new PhabricatorUser();
|
||||
|
||||
echo pht("There is no existing user account '%s'.", $username)."\n";
|
||||
$ok = phutil_console_confirm(
|
||||
pht("Do you want to create a new '%s' account?", $username),
|
||||
$default_no = false);
|
||||
if (!$ok) {
|
||||
echo pht('Cancelled.')."\n";
|
||||
exit(1);
|
||||
}
|
||||
$user = new PhabricatorUser();
|
||||
$user->setUsername($username);
|
||||
|
||||
$is_new = true;
|
||||
} else {
|
||||
$original = clone $user;
|
||||
|
||||
echo pht("There is an existing user account '%s'.", $username)."\n";
|
||||
$ok = phutil_console_confirm(
|
||||
pht("Do you want to edit the existing '%s' account?", $username),
|
||||
$default_no = false);
|
||||
if (!$ok) {
|
||||
echo pht('Cancelled.')."\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$is_new = false;
|
||||
}
|
||||
|
||||
$user_realname = $user->getRealName();
|
||||
if (strlen($user_realname)) {
|
||||
$realname_prompt = ' ['.$user_realname.']:';
|
||||
} else {
|
||||
$realname_prompt = ':';
|
||||
}
|
||||
$realname = nonempty(
|
||||
phutil_console_prompt(pht('Enter user real name').$realname_prompt),
|
||||
$user_realname);
|
||||
$user->setRealName($realname);
|
||||
|
||||
// When creating a new user we prompt for an email address; when editing an
|
||||
// existing user we just skip this because it would be quite involved to provide
|
||||
// a reasonable CLI interface for editing multiple addresses and managing email
|
||||
// verification and primary addresses.
|
||||
|
||||
$create_email = null;
|
||||
if ($is_new) {
|
||||
do {
|
||||
$email = phutil_console_prompt(pht('Enter user email address:'));
|
||||
$duplicate = id(new PhabricatorUserEmail())->loadOneWhere(
|
||||
'address = %s',
|
||||
$email);
|
||||
if ($duplicate) {
|
||||
echo pht(
|
||||
"ERROR: There is already a user with that email address. ".
|
||||
"Each user must have a unique email address.\n");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
$create_email = $email;
|
||||
}
|
||||
|
||||
$is_system_agent = $user->getIsSystemAgent();
|
||||
$set_system_agent = phutil_console_confirm(
|
||||
pht('Is this user a bot?'),
|
||||
$default_no = !$is_system_agent);
|
||||
|
||||
$verify_email = null;
|
||||
$set_verified = false;
|
||||
// Allow administrators to verify primary email addresses at this time in edit
|
||||
// scenarios. (Create will work just fine from here as we auto-verify email
|
||||
// on create.)
|
||||
if (!$is_new) {
|
||||
$verify_email = $user->loadPrimaryEmail();
|
||||
if (!$verify_email->getIsVerified()) {
|
||||
$set_verified = phutil_console_confirm(
|
||||
pht('Should the primary email address be verified?'),
|
||||
$default_no = true);
|
||||
} else {
|
||||
// Already verified so let's not make a fuss.
|
||||
$verify_email = null;
|
||||
}
|
||||
}
|
||||
|
||||
$is_admin = $user->getIsAdmin();
|
||||
$set_admin = phutil_console_confirm(
|
||||
pht('Should this user be an administrator?'),
|
||||
$default_no = !$is_admin);
|
||||
|
||||
echo "\n\n".pht('ACCOUNT SUMMARY')."\n\n";
|
||||
$tpl = "%12s %-30s %-30s\n";
|
||||
printf($tpl, null, pht('OLD VALUE'), pht('NEW VALUE'));
|
||||
printf($tpl, pht('Username'), $original->getUsername(), $user->getUsername());
|
||||
printf($tpl, pht('Real Name'), $original->getRealName(), $user->getRealName());
|
||||
if ($is_new) {
|
||||
printf($tpl, pht('Email'), '', $create_email);
|
||||
}
|
||||
|
||||
printf(
|
||||
$tpl,
|
||||
pht('Bot'),
|
||||
$original->getIsSystemAgent() ? 'Y' : 'N',
|
||||
$set_system_agent ? 'Y' : 'N');
|
||||
|
||||
if ($verify_email) {
|
||||
printf(
|
||||
$tpl,
|
||||
pht('Verify Email'),
|
||||
$verify_email->getIsVerified() ? 'Y' : 'N',
|
||||
$set_verified ? 'Y' : 'N');
|
||||
}
|
||||
|
||||
printf(
|
||||
$tpl,
|
||||
pht('Admin'),
|
||||
$original->getIsAdmin() ? 'Y' : 'N',
|
||||
$set_admin ? 'Y' : 'N');
|
||||
|
||||
echo "\n";
|
||||
|
||||
if (!phutil_console_confirm(pht('Save these changes?'), $default_no = false)) {
|
||||
echo pht('Cancelled.')."\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$user->openTransaction();
|
||||
|
||||
$editor = new PhabricatorUserEditor();
|
||||
|
||||
// TODO: This is wrong, but we have a chicken-and-egg problem when you use
|
||||
// this script to create the first user.
|
||||
$editor->setActor($user);
|
||||
|
||||
if ($is_new) {
|
||||
$email = id(new PhabricatorUserEmail())
|
||||
->setAddress($create_email)
|
||||
->setIsVerified(1);
|
||||
|
||||
// Unconditionally approve new accounts created from the CLI.
|
||||
$user->setIsApproved(1);
|
||||
|
||||
$editor->createNewUser($user, $email);
|
||||
} else {
|
||||
if ($verify_email) {
|
||||
$user->setIsEmailVerified(1);
|
||||
$verify_email->setIsVerified($set_verified ? 1 : 0);
|
||||
}
|
||||
$editor->updateUser($user, $verify_email);
|
||||
}
|
||||
|
||||
$editor->makeSystemAgentUser($user, $set_system_agent);
|
||||
|
||||
$xactions = array();
|
||||
$xactions[] = id(new PhabricatorUserTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorUserEmpowerTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue($set_admin);
|
||||
|
||||
$actor = PhabricatorUser::getOmnipotentUser();
|
||||
$content_source = PhabricatorContentSource::newForSource(
|
||||
PhabricatorConsoleContentSource::SOURCECONST);
|
||||
|
||||
$people_application_phid = id(new PhabricatorPeopleApplication())->getPHID();
|
||||
|
||||
$transaction_editor = id(new PhabricatorUserTransactionEditor())
|
||||
->setActor($actor)
|
||||
->setActingAsPHID($people_application_phid)
|
||||
->setContentSource($content_source)
|
||||
->setContinueOnNoEffect(true)
|
||||
->setContinueOnMissingFields(true);
|
||||
|
||||
$transaction_editor->applyTransactions($user, $xactions);
|
||||
|
||||
$user->saveTransaction();
|
||||
|
||||
echo pht('Saved changes.')."\n";
|
@@ -1,73 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
$root = dirname(dirname(dirname(__FILE__)));
|
||||
require_once $root.'/scripts/__init_script__.php';
|
||||
|
||||
if ($argc !== 5) {
|
||||
echo pht(
|
||||
"Usage: %s\n",
|
||||
'add_user.php <username> <email> <realname> <admin_user>');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$username = $argv[1];
|
||||
$email = $argv[2];
|
||||
$realname = $argv[3];
|
||||
$admin = $argv[4];
|
||||
|
||||
$admin = id(new PhabricatorUser())->loadOneWhere(
|
||||
'username = %s',
|
||||
$argv[4]);
|
||||
if (!$admin) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Admin user must be the username of a valid Phabricator account, used '.
|
||||
'to send the new user a welcome email.'));
|
||||
}
|
||||
|
||||
$existing_user = id(new PhabricatorUser())->loadOneWhere(
|
||||
'username = %s',
|
||||
$username);
|
||||
if ($existing_user) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
"There is already a user with the username '%s'!",
|
||||
$username));
|
||||
}
|
||||
|
||||
$existing_email = id(new PhabricatorUserEmail())->loadOneWhere(
|
||||
'address = %s',
|
||||
$email);
|
||||
if ($existing_email) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
"There is already a user with the email '%s'!",
|
||||
$email));
|
||||
}
|
||||
|
||||
$user = new PhabricatorUser();
|
||||
$user->setUsername($username);
|
||||
$user->setRealname($realname);
|
||||
$user->setIsApproved(1);
|
||||
|
||||
$email_object = id(new PhabricatorUserEmail())
|
||||
->setAddress($email)
|
||||
->setIsVerified(1);
|
||||
|
||||
id(new PhabricatorUserEditor())
|
||||
->setActor($admin)
|
||||
->createNewUser($user, $email_object);
|
||||
|
||||
$welcome_engine = id(new PhabricatorPeopleWelcomeMailEngine())
|
||||
->setSender($admin)
|
||||
->setRecipient($user);
|
||||
if ($welcome_engine->canSendMail()) {
|
||||
$welcome_engine->sendMail();
|
||||
}
|
||||
|
||||
echo pht(
|
||||
"Created user '%s' (realname='%s', email='%s').\n",
|
||||
$username,
|
||||
$realname,
|
||||
$email);
|
File diff suppressed because it is too large
Load Diff
@@ -312,11 +312,17 @@ final class AphrontApplicationConfiguration
|
||||
if ($response_exception) {
|
||||
// If we encountered an exception while building a normal response, then
|
||||
// encountered another exception while building a response for the first
|
||||
// exception, just throw the original exception. It is more likely to be
|
||||
// useful and point at a root cause than the second exception we ran into
|
||||
// while telling the user about it.
|
||||
// exception, throw an aggregate exception that will be unpacked by the
|
||||
// higher-level handler. This is above our pay grade.
|
||||
if ($original_exception) {
|
||||
throw $original_exception;
|
||||
throw new PhutilAggregateException(
|
||||
pht(
|
||||
'Encountered a processing exception, then another exception when '.
|
||||
'trying to build a response for the first exception.'),
|
||||
array(
|
||||
$response_exception,
|
||||
$original_exception,
|
||||
));
|
||||
}
|
||||
|
||||
// If we built a response successfully and then ran into an exception
|
||||
|
@@ -8,16 +8,19 @@ final class Aphront404Response extends AphrontHTMLResponse {
|
||||
|
||||
public function buildResponseString() {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
$viewer = $request->getViewer();
|
||||
|
||||
$dialog = id(new AphrontDialogView())
|
||||
->setUser($user)
|
||||
->setViewer($viewer)
|
||||
->setTitle(pht('404 Not Found'))
|
||||
->addCancelButton('/', pht('Focus'))
|
||||
->addCancelButton('/', pht('Return to Charted Waters'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'Do not dwell in the past, do not dream of the future, '.
|
||||
'concentrate the mind on the present moment.'));
|
||||
'You arrive at your destination, but there is nothing here.'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'Perhaps the real treasure was the friends you made '.
|
||||
'along the way.'));
|
||||
|
||||
$view = id(new PhabricatorStandardPageView())
|
||||
->setTitle(pht('404 Not Found'))
|
||||
|
@@ -61,9 +61,39 @@ final class AphrontUnhandledExceptionResponse
|
||||
return 'unhandled-exception';
|
||||
}
|
||||
|
||||
protected function getResponseBody() {
|
||||
$ex = $this->exception;
|
||||
private function getExceptionList() {
|
||||
return $this->expandException($this->exception);
|
||||
}
|
||||
|
||||
private function expandException($root) {
|
||||
if ($root instanceof PhutilAggregateException) {
|
||||
$list = array();
|
||||
|
||||
$list[] = $root;
|
||||
|
||||
foreach ($root->getExceptions() as $ex) {
|
||||
foreach ($this->expandException($ex) as $child) {
|
||||
$list[] = $child;
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
return array($root);
|
||||
}
|
||||
|
||||
protected function getResponseBody() {
|
||||
$body = array();
|
||||
|
||||
foreach ($this->getExceptionList() as $ex) {
|
||||
$body[] = $this->newHTMLMessage($ex);
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
private function newHTMLMessage($ex) {
|
||||
if ($ex instanceof AphrontMalformedRequestException) {
|
||||
$title = $ex->getTitle();
|
||||
} else {
|
||||
@@ -122,12 +152,20 @@ final class AphrontUnhandledExceptionResponse
|
||||
}
|
||||
|
||||
protected function buildPlainTextResponseString() {
|
||||
$ex = $this->exception;
|
||||
$messages = array();
|
||||
|
||||
foreach ($this->getExceptionList() as $exception) {
|
||||
$messages[] = $this->newPlainTextMessage($exception);
|
||||
}
|
||||
|
||||
return implode("\n\n", $messages);
|
||||
}
|
||||
|
||||
private function newPlainTextMessage($exception) {
|
||||
return pht(
|
||||
'%s: %s',
|
||||
get_class($ex),
|
||||
$ex->getMessage());
|
||||
get_class($exception),
|
||||
$exception->getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -5,10 +5,6 @@ final class PhabricatorAuthChangePasswordAction
|
||||
|
||||
const TYPECONST = 'auth.password';
|
||||
|
||||
public function getActionConstant() {
|
||||
return self::TYPECONST;
|
||||
}
|
||||
|
||||
public function getScoreThreshold() {
|
||||
return 20 / phutil_units('1 hour in seconds');
|
||||
}
|
||||
|
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthEmailLoginAction extends PhabricatorSystemAction {
|
||||
|
||||
const TYPECONST = 'mail.login';
|
||||
|
||||
public function getScoreThreshold() {
|
||||
return 3 / phutil_units('1 hour in seconds');
|
||||
}
|
||||
|
||||
public function getLimitExplanation() {
|
||||
return pht(
|
||||
'Too many account recovery email links have been sent to this account '.
|
||||
'in a short period of time.');
|
||||
}
|
||||
|
||||
}
|
@@ -4,10 +4,6 @@ final class PhabricatorAuthNewFactorAction extends PhabricatorSystemAction {
|
||||
|
||||
const TYPECONST = 'auth.factor.new';
|
||||
|
||||
public function getActionConstant() {
|
||||
return self::TYPECONST;
|
||||
}
|
||||
|
||||
public function getScoreThreshold() {
|
||||
return 60 / phutil_units('1 hour in seconds');
|
||||
}
|
||||
|
@@ -4,10 +4,6 @@ final class PhabricatorAuthTestSMSAction extends PhabricatorSystemAction {
|
||||
|
||||
const TYPECONST = 'auth.sms.test';
|
||||
|
||||
public function getActionConstant() {
|
||||
return self::TYPECONST;
|
||||
}
|
||||
|
||||
public function getScoreThreshold() {
|
||||
return 60 / phutil_units('1 hour in seconds');
|
||||
}
|
||||
|
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthTryEmailLoginAction
|
||||
extends PhabricatorSystemAction {
|
||||
|
||||
const TYPECONST = 'mail.try-login';
|
||||
|
||||
public function getScoreThreshold() {
|
||||
return 20 / phutil_units('1 hour in seconds');
|
||||
}
|
||||
|
||||
public function getLimitExplanation() {
|
||||
return pht(
|
||||
'You have made too many account recovery requests in a short period '.
|
||||
'of time.');
|
||||
}
|
||||
|
||||
}
|
@@ -4,10 +4,6 @@ final class PhabricatorAuthTryFactorAction extends PhabricatorSystemAction {
|
||||
|
||||
const TYPECONST = 'auth.factor';
|
||||
|
||||
public function getActionConstant() {
|
||||
return self::TYPECONST;
|
||||
}
|
||||
|
||||
public function getScoreThreshold() {
|
||||
return 10 / phutil_units('1 hour in seconds');
|
||||
}
|
||||
|
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthTryPasswordAction
|
||||
extends PhabricatorSystemAction {
|
||||
|
||||
const TYPECONST = 'auth.password';
|
||||
|
||||
public function getScoreThreshold() {
|
||||
return 100 / phutil_units('1 hour in seconds');
|
||||
}
|
||||
|
||||
public function getLimitExplanation() {
|
||||
return pht(
|
||||
'Your remote address has made too many login attempts in a short '.
|
||||
'period of time.');
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthTryPasswordWithoutCAPTCHAAction
|
||||
extends PhabricatorSystemAction {
|
||||
|
||||
const TYPECONST = 'auth.password-without-captcha';
|
||||
|
||||
public function getScoreThreshold() {
|
||||
return 10 / phutil_units('1 hour in seconds');
|
||||
}
|
||||
|
||||
}
|
@@ -108,7 +108,7 @@ final class PhabricatorAuthApplication extends PhabricatorApplication {
|
||||
'PhabricatorAuthMessageListController',
|
||||
$this->getEditRoutePattern('edit/') =>
|
||||
'PhabricatorAuthMessageEditController',
|
||||
'(?P<id>[1-9]\d*)/' =>
|
||||
'(?P<id>[^/]+)/' =>
|
||||
'PhabricatorAuthMessageViewController',
|
||||
),
|
||||
|
||||
|
@@ -53,6 +53,14 @@ final class PhabricatorEmailLoginController
|
||||
// it expensive to fish for valid email addresses while giving the user
|
||||
// a better error if they goof their email.
|
||||
|
||||
$action_actor = PhabricatorSystemActionEngine::newActorFromRequest(
|
||||
$request);
|
||||
|
||||
PhabricatorSystemActionEngine::willTakeAction(
|
||||
array($action_actor),
|
||||
new PhabricatorAuthTryEmailLoginAction(),
|
||||
1);
|
||||
|
||||
$target_email = id(new PhabricatorUserEmail())->loadOneWhere(
|
||||
'address = %s',
|
||||
$v_email);
|
||||
@@ -94,29 +102,40 @@ final class PhabricatorEmailLoginController
|
||||
}
|
||||
|
||||
if (!$errors) {
|
||||
$body = $this->newAccountLoginMailBody(
|
||||
$target_user,
|
||||
$is_logged_in);
|
||||
$target_address = new PhutilEmailAddress($target_email->getAddress());
|
||||
|
||||
$user_log = PhabricatorUserLog::initializeNewLog(
|
||||
$viewer,
|
||||
$target_user->getPHID(),
|
||||
PhabricatorEmailLoginUserLogType::LOGTYPE);
|
||||
|
||||
$mail_engine = id(new PhabricatorPeopleEmailLoginMailEngine())
|
||||
->setSender($viewer)
|
||||
->setRecipient($target_user)
|
||||
->setRecipientAddress($target_address)
|
||||
->setActivityLog($user_log);
|
||||
|
||||
try {
|
||||
$mail_engine->validateMail();
|
||||
} catch (PhabricatorPeopleMailEngineException $ex) {
|
||||
return $this->newDialog()
|
||||
->setTitle($ex->getTitle())
|
||||
->appendParagraph($ex->getBody())
|
||||
->addCancelButton('/auth/start/', pht('Done'));
|
||||
}
|
||||
|
||||
$mail_engine->sendMail();
|
||||
|
||||
if ($is_logged_in) {
|
||||
$subject = pht('[Phabricator] Account Password Link');
|
||||
$instructions = pht(
|
||||
'An email has been sent containing a link you can use to set '.
|
||||
'a password for your account.');
|
||||
} else {
|
||||
$subject = pht('[Phabricator] Account Login Link');
|
||||
$instructions = pht(
|
||||
'An email has been sent containing a link you can use to log '.
|
||||
'in to your account.');
|
||||
}
|
||||
|
||||
$mail = id(new PhabricatorMetaMTAMail())
|
||||
->setSubject($subject)
|
||||
->setForceDelivery(true)
|
||||
->addRawTos(array($target_email->getAddress()))
|
||||
->setBody($body)
|
||||
->saveAndSend();
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Check Your Email'))
|
||||
->setShortTitle(pht('Email Sent'))
|
||||
@@ -182,55 +201,6 @@ final class PhabricatorEmailLoginController
|
||||
->addSubmitButton(pht('Send Email'));
|
||||
}
|
||||
|
||||
private function newAccountLoginMailBody(
|
||||
PhabricatorUser $user,
|
||||
$is_logged_in) {
|
||||
|
||||
$engine = new PhabricatorAuthSessionEngine();
|
||||
$uri = $engine->getOneTimeLoginURI(
|
||||
$user,
|
||||
null,
|
||||
PhabricatorAuthSessionEngine::ONETIME_RESET);
|
||||
|
||||
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
|
||||
$have_passwords = $this->isPasswordAuthEnabled();
|
||||
|
||||
if ($have_passwords) {
|
||||
if ($is_logged_in) {
|
||||
$body = pht(
|
||||
'You can use this link to set a password on your account:'.
|
||||
"\n\n %s\n",
|
||||
$uri);
|
||||
} else if ($is_serious) {
|
||||
$body = pht(
|
||||
"You can use this link to reset your Phabricator password:".
|
||||
"\n\n %s\n",
|
||||
$uri);
|
||||
} else {
|
||||
$body = pht(
|
||||
"Condolences on forgetting your password. You can use this ".
|
||||
"link to reset it:\n\n".
|
||||
" %s\n\n".
|
||||
"After you set a new password, consider writing it down on a ".
|
||||
"sticky note and attaching it to your monitor so you don't ".
|
||||
"forget again! Choosing a very short, easy-to-remember password ".
|
||||
"like \"cat\" or \"1234\" might also help.\n\n".
|
||||
"Best Wishes,\nPhabricator\n",
|
||||
$uri);
|
||||
|
||||
}
|
||||
} else {
|
||||
$body = pht(
|
||||
"You can use this login link to regain access to your Phabricator ".
|
||||
"account:".
|
||||
"\n\n".
|
||||
" %s\n",
|
||||
$uri);
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
private function isPasswordAuthEnabled() {
|
||||
return (bool)PhabricatorPasswordAuthProvider::getPasswordProvider();
|
||||
}
|
||||
|
@@ -79,6 +79,7 @@ final class PhabricatorAuthEditController
|
||||
}
|
||||
|
||||
$errors = array();
|
||||
$validation_exception = null;
|
||||
|
||||
$v_login = $config->getShouldAllowLogin();
|
||||
$v_registration = $config->getShouldAllowRegistration();
|
||||
@@ -153,12 +154,16 @@ final class PhabricatorAuthEditController
|
||||
$editor = id(new PhabricatorAuthProviderConfigEditor())
|
||||
->setActor($viewer)
|
||||
->setContentSourceFromRequest($request)
|
||||
->setContinueOnNoEffect(true)
|
||||
->applyTransactions($config, $xactions);
|
||||
->setContinueOnNoEffect(true);
|
||||
|
||||
$next_uri = $config->getURI();
|
||||
try {
|
||||
$editor->applyTransactions($config, $xactions);
|
||||
$next_uri = $config->getURI();
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI($next_uri);
|
||||
return id(new AphrontRedirectResponse())->setURI($next_uri);
|
||||
} catch (Exception $ex) {
|
||||
$validation_exception = $ex;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$properties = $provider->readFormValuesFromProvider();
|
||||
@@ -325,12 +330,35 @@ final class PhabricatorAuthEditController
|
||||
|
||||
$provider->extendEditForm($request, $form, $properties, $issues);
|
||||
|
||||
$locked_config_key = 'auth.lock-config';
|
||||
$is_locked = PhabricatorEnv::getEnvConfig($locked_config_key);
|
||||
|
||||
$locked_warning = null;
|
||||
if ($is_locked && !$validation_exception) {
|
||||
$message = pht(
|
||||
'Authentication provider configuration is locked, and can not be '.
|
||||
'changed without being unlocked. See the configuration setting %s '.
|
||||
'for details.',
|
||||
phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => '/config/edit/'.$locked_config_key,
|
||||
),
|
||||
$locked_config_key));
|
||||
$locked_warning = id(new PHUIInfoView())
|
||||
->setViewer($viewer)
|
||||
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
|
||||
->setErrors(array($message));
|
||||
}
|
||||
|
||||
$form
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->addCancelButton($cancel_uri)
|
||||
->setDisabled($is_locked)
|
||||
->setValue($button));
|
||||
|
||||
|
||||
$help = $provider->getConfigurationHelp();
|
||||
if ($help) {
|
||||
$form->appendChild(id(new PHUIFormDividerControl()));
|
||||
@@ -346,12 +374,16 @@ final class PhabricatorAuthEditController
|
||||
$form_box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Provider'))
|
||||
->setFormErrors($errors)
|
||||
->setValidationException($validation_exception)
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setForm($form);
|
||||
|
||||
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setFooter(array(
|
||||
$locked_warning,
|
||||
$form_box,
|
||||
$footer,
|
||||
));
|
||||
|
@@ -78,12 +78,14 @@ final class PhabricatorAuthListController
|
||||
->setGuidanceContext($guidance_context)
|
||||
->newInfoView();
|
||||
|
||||
$is_disabled = (!$can_manage || $is_locked);
|
||||
$button = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setButtonType(PHUIButtonView::BUTTONTYPE_SIMPLE)
|
||||
->setHref($this->getApplicationURI('config/new/'))
|
||||
->setIcon('fa-plus')
|
||||
->setDisabled(!$can_manage || $is_locked)
|
||||
->setDisabled($is_disabled)
|
||||
->setWorkflow($is_disabled)
|
||||
->setHref($this->getApplicationURI('config/new/'))
|
||||
->setText(pht('Add Provider'));
|
||||
|
||||
$list->setFlush(true);
|
||||
|
@@ -9,6 +9,27 @@ final class PhabricatorAuthNewController
|
||||
|
||||
$viewer = $this->getViewer();
|
||||
$cancel_uri = $this->getApplicationURI();
|
||||
$locked_config_key = 'auth.lock-config';
|
||||
$is_locked = PhabricatorEnv::getEnvConfig($locked_config_key);
|
||||
|
||||
if ($is_locked) {
|
||||
$message = pht(
|
||||
'Authentication provider configuration is locked, and can not be '.
|
||||
'changed without being unlocked. See the configuration setting %s '.
|
||||
'for details.',
|
||||
phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => '/config/edit/'.$locked_config_key,
|
||||
),
|
||||
$locked_config_key));
|
||||
|
||||
return $this->newDialog()
|
||||
->setUser($viewer)
|
||||
->setTitle(pht('Authentication Config Locked'))
|
||||
->appendChild($message)
|
||||
->addCancelButton($cancel_uri);
|
||||
}
|
||||
|
||||
$providers = PhabricatorAuthProvider::getAllBaseProviders();
|
||||
|
||||
|
@@ -114,6 +114,86 @@ final class PhabricatorAuthProviderViewController
|
||||
pht('Provider Type'),
|
||||
$config->getProvider()->getProviderName());
|
||||
|
||||
$status = $this->buildStatus($config);
|
||||
$view->addProperty(pht('Status'), $status);
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
private function buildStatus(PhabricatorAuthProviderConfig $config) {
|
||||
$viewer = $this->getViewer();
|
||||
$view = id(new PHUIStatusListView())
|
||||
->setViewer($viewer);
|
||||
|
||||
$icon_enabled = PHUIStatusItemView::ICON_ACCEPT;
|
||||
$icon_disabled = PHUIStatusItemView::ICON_REJECT;
|
||||
|
||||
$icon_map = array(
|
||||
true => $icon_enabled,
|
||||
false => $icon_disabled,
|
||||
);
|
||||
|
||||
$color_map = array(
|
||||
true => 'green',
|
||||
false => 'red',
|
||||
);
|
||||
|
||||
$provider = $config->getProvider();
|
||||
|
||||
$view->addItem(
|
||||
id(new PHUIStatusItemView())
|
||||
->setIcon(
|
||||
$icon_map[$config->getIsEnabled()],
|
||||
$color_map[$config->getIsEnabled()])
|
||||
->setTarget(pht('Provider Enabled')));
|
||||
|
||||
$view->addItem(
|
||||
id(new PHUIStatusItemView())
|
||||
->setIcon(
|
||||
$icon_map[$config->getShouldAllowLogin()],
|
||||
$color_map[$config->getShouldAllowLogin()])
|
||||
->setTarget(pht('Allow Logins')));
|
||||
|
||||
$view->addItem(
|
||||
id(new PHUIStatusItemView())
|
||||
->setIcon(
|
||||
$icon_map[$config->getShouldAllowRegistration()],
|
||||
$color_map[$config->getShouldAllowRegistration()])
|
||||
->setTarget(pht('Allow Registration')));
|
||||
|
||||
$view->addItem(
|
||||
id(new PHUIStatusItemView())
|
||||
->setIcon(
|
||||
$icon_map[$config->getShouldAllowLink()],
|
||||
$color_map[$config->getShouldAllowLink()])
|
||||
->setTarget(pht('Allow Account Linking')));
|
||||
|
||||
$view->addItem(
|
||||
id(new PHUIStatusItemView())
|
||||
->setIcon(
|
||||
$icon_map[$config->getShouldAllowUnlink()],
|
||||
$color_map[$config->getShouldAllowUnlink()])
|
||||
->setTarget(pht('Allow Account Unlinking')));
|
||||
|
||||
if ($provider->shouldAllowEmailTrustConfiguration()) {
|
||||
$view->addItem(
|
||||
id(new PHUIStatusItemView())
|
||||
->setIcon(
|
||||
$icon_map[$config->getShouldTrustEmails()],
|
||||
$color_map[$config->getShouldTrustEmails()])
|
||||
->setTarget(pht('Trust Email Addresses')));
|
||||
}
|
||||
|
||||
if ($provider->supportsAutoLogin()) {
|
||||
$view->addItem(
|
||||
id(new PHUIStatusItemView())
|
||||
->setIcon(
|
||||
$icon_map[$config->getShouldAutoLogin()],
|
||||
$color_map[$config->getShouldAutoLogin()])
|
||||
->setTarget(pht('Allow Auto Login')));
|
||||
}
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -19,11 +19,14 @@ final class PhabricatorAuthMessageListController
|
||||
$list = new PHUIObjectItemListView();
|
||||
foreach ($types as $type) {
|
||||
$message = idx($messages, $type->getMessageTypeKey());
|
||||
|
||||
if ($message) {
|
||||
$href = $message->getURI();
|
||||
$name = $message->getMessageTypeDisplayName();
|
||||
} else {
|
||||
$href = '/auth/message/edit/?messageKey='.$type->getMessageTypeKey();
|
||||
$href = urisprintf(
|
||||
'/auth/message/%s/',
|
||||
$type->getMessageTypeKey());
|
||||
$name = $type->getDisplayName();
|
||||
}
|
||||
|
||||
|
@@ -9,26 +9,61 @@ final class PhabricatorAuthMessageViewController
|
||||
$this->requireApplicationCapability(
|
||||
AuthManageProvidersCapability::CAPABILITY);
|
||||
|
||||
$message = id(new PhabricatorAuthMessageQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($request->getURIData('id')))
|
||||
->executeOne();
|
||||
if (!$message) {
|
||||
return new Aphront404Response();
|
||||
// The "id" in the URI may either be an actual storage record ID (if a
|
||||
// message has already been created) or a message type key (for a message
|
||||
// type which does not have a record yet).
|
||||
|
||||
// This flow allows messages which have not been set yet to have a detail
|
||||
// page (so users can get detailed information about the message and see
|
||||
// any default value).
|
||||
|
||||
$id = $request->getURIData('id');
|
||||
if (ctype_digit($id)) {
|
||||
$message = id(new PhabricatorAuthMessageQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
if (!$message) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
} else {
|
||||
$types = PhabricatorAuthMessageType::getAllMessageTypes();
|
||||
if (!isset($types[$id])) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
// If this message type already has a storage record, redirect to the
|
||||
// canonical page for the record.
|
||||
$message = id(new PhabricatorAuthMessageQuery())
|
||||
->setViewer($viewer)
|
||||
->withMessageKeys(array($id))
|
||||
->executeOne();
|
||||
if ($message) {
|
||||
$message_uri = $message->getURI();
|
||||
return id(new AphrontRedirectResponse())->setURI($message_uri);
|
||||
}
|
||||
|
||||
// Otherwise, create an empty placeholder message object with the
|
||||
// appropriate message type.
|
||||
$message = PhabricatorAuthMessage::initializeNewMessage($types[$id]);
|
||||
}
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs()
|
||||
->addTextCrumb($message->getObjectName())
|
||||
->addTextCrumb($message->getMessageType()->getDisplayName())
|
||||
->setBorder(true);
|
||||
|
||||
$header = $this->buildHeaderView($message);
|
||||
$properties = $this->buildPropertiesView($message);
|
||||
$curtain = $this->buildCurtain($message);
|
||||
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$message,
|
||||
new PhabricatorAuthMessageTransactionQuery());
|
||||
$timeline->setShouldTerminate(true);
|
||||
if ($message->getID()) {
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$message,
|
||||
new PhabricatorAuthMessageTransactionQuery());
|
||||
$timeline->setShouldTerminate(true);
|
||||
} else {
|
||||
$timeline = null;
|
||||
}
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
@@ -62,19 +97,36 @@ final class PhabricatorAuthMessageViewController
|
||||
private function buildPropertiesView(PhabricatorAuthMessage $message) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$message_type = $message->getMessageType();
|
||||
|
||||
$view = id(new PHUIPropertyListView())
|
||||
->setViewer($viewer);
|
||||
|
||||
$view->addProperty(
|
||||
pht('Description'),
|
||||
$message->getMessageType()->getShortDescription());
|
||||
$full_description = $message_type->getFullDescription();
|
||||
if (strlen($full_description)) {
|
||||
$view->addTextContent(new PHUIRemarkupView($viewer, $full_description));
|
||||
} else {
|
||||
$short_description = $message_type->getShortDescription();
|
||||
$view->addProperty(pht('Description'), $short_description);
|
||||
}
|
||||
|
||||
$view->addSectionHeader(
|
||||
pht('Message Preview'),
|
||||
PHUIPropertyListView::ICON_SUMMARY);
|
||||
$message_text = $message->getMessageText();
|
||||
if (strlen($message_text)) {
|
||||
$view->addSectionHeader(
|
||||
pht('Message Preview'),
|
||||
PHUIPropertyListView::ICON_SUMMARY);
|
||||
|
||||
$view->addTextContent(
|
||||
new PHUIRemarkupView($viewer, $message->getMessageText()));
|
||||
$view->addTextContent(new PHUIRemarkupView($viewer, $message_text));
|
||||
}
|
||||
|
||||
$default_text = $message_type->getDefaultMessageText();
|
||||
if (strlen($default_text)) {
|
||||
$view->addSectionHeader(
|
||||
pht('Default Message'),
|
||||
PHUIPropertyListView::ICON_SUMMARY);
|
||||
|
||||
$view->addTextContent(new PHUIRemarkupView($viewer, $default_text));
|
||||
}
|
||||
|
||||
return $view;
|
||||
}
|
||||
@@ -88,13 +140,27 @@ final class PhabricatorAuthMessageViewController
|
||||
$message,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
if ($id) {
|
||||
$edit_uri = urisprintf('message/edit/%s/', $id);
|
||||
$edit_name = pht('Edit Message');
|
||||
} else {
|
||||
$edit_uri = urisprintf('message/edit/');
|
||||
$params = array(
|
||||
'messageKey' => $message->getMessageKey(),
|
||||
);
|
||||
$edit_uri = new PhutilURI($edit_uri, $params);
|
||||
|
||||
$edit_name = pht('Customize Message');
|
||||
}
|
||||
$edit_uri = $this->getApplicationURI($edit_uri);
|
||||
|
||||
$curtain = $this->newCurtainView($message);
|
||||
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Edit Message'))
|
||||
->setName($edit_name)
|
||||
->setIcon('fa-pencil')
|
||||
->setHref($this->getApplicationURI("message/edit/{$id}/"))
|
||||
->setHref($edit_uri)
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(!$can_edit));
|
||||
|
||||
|
@@ -125,4 +125,25 @@ final class PhabricatorAuthProviderConfigEditor
|
||||
return parent::mergeTransactions($u, $v);
|
||||
}
|
||||
|
||||
protected function validateAllTransactions(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
$errors = parent::validateAllTransactions($object, $xactions);
|
||||
|
||||
$locked_config_key = 'auth.lock-config';
|
||||
$is_locked = PhabricatorEnv::getEnvConfig($locked_config_key);
|
||||
|
||||
if ($is_locked) {
|
||||
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
||||
null,
|
||||
pht('Config Locked'),
|
||||
pht('Authentication provider configuration is locked, and can not be '.
|
||||
'changed without being unlocked.'),
|
||||
null);
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -294,8 +294,8 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
null,
|
||||
$identity_phid,
|
||||
($partial
|
||||
? PhabricatorUserLog::ACTION_LOGIN_PARTIAL
|
||||
: PhabricatorUserLog::ACTION_LOGIN));
|
||||
? PhabricatorPartialLoginUserLogType::LOGTYPE
|
||||
: PhabricatorLoginUserLogType::LOGTYPE));
|
||||
|
||||
$log->setDetails(
|
||||
array(
|
||||
@@ -366,7 +366,7 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
$log = PhabricatorUserLog::initializeNewLog(
|
||||
$user,
|
||||
$user->getPHID(),
|
||||
PhabricatorUserLog::ACTION_LOGOUT);
|
||||
PhabricatorLogoutUserLogType::LOGTYPE);
|
||||
$log->save();
|
||||
|
||||
$extensions = PhabricatorAuthSessionEngineExtension::getAllExtensions();
|
||||
@@ -688,13 +688,13 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
$log = PhabricatorUserLog::initializeNewLog(
|
||||
$viewer,
|
||||
$viewer->getPHID(),
|
||||
PhabricatorUserLog::ACTION_ENTER_HISEC);
|
||||
PhabricatorEnterHisecUserLogType::LOGTYPE);
|
||||
$log->save();
|
||||
} else {
|
||||
$log = PhabricatorUserLog::initializeNewLog(
|
||||
$viewer,
|
||||
$viewer->getPHID(),
|
||||
PhabricatorUserLog::ACTION_FAIL_HISEC);
|
||||
PhabricatorFailHisecUserLogType::LOGTYPE);
|
||||
$log->save();
|
||||
}
|
||||
}
|
||||
@@ -831,7 +831,7 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
$log = PhabricatorUserLog::initializeNewLog(
|
||||
$viewer,
|
||||
$viewer->getPHID(),
|
||||
PhabricatorUserLog::ACTION_EXIT_HISEC);
|
||||
PhabricatorExitHisecUserLogType::LOGTYPE);
|
||||
$log->save();
|
||||
}
|
||||
|
||||
@@ -872,7 +872,7 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
$log = PhabricatorUserLog::initializeNewLog(
|
||||
$viewer,
|
||||
$viewer->getPHID(),
|
||||
PhabricatorUserLog::ACTION_LOGIN_FULL);
|
||||
PhabricatorFullLoginUserLogType::LOGTYPE);
|
||||
$log->save();
|
||||
unset($unguarded);
|
||||
}
|
||||
@@ -917,7 +917,7 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
$log = PhabricatorUserLog::initializeNewLog(
|
||||
$viewer,
|
||||
$viewer->getPHID(),
|
||||
PhabricatorUserLog::ACTION_LOGIN_LEGALPAD);
|
||||
PhabricatorSignDocumentsUserLogType::LOGTYPE);
|
||||
$log->save();
|
||||
}
|
||||
unset($unguarded);
|
||||
|
@@ -194,6 +194,7 @@ final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor {
|
||||
$control = id(new PHUIFormNumberControl())
|
||||
->setName($name)
|
||||
->setDisableAutocomplete(true)
|
||||
->setAutofocus(true)
|
||||
->setValue($value)
|
||||
->setError($error);
|
||||
}
|
||||
|
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthEmailLoginMessageType
|
||||
extends PhabricatorAuthMessageType {
|
||||
|
||||
const MESSAGEKEY = 'mail.login';
|
||||
|
||||
public function getDisplayName() {
|
||||
return pht('Mail Body: Email Login');
|
||||
}
|
||||
|
||||
public function getShortDescription() {
|
||||
return pht(
|
||||
'Guidance in the message body when users request an email link '.
|
||||
'to access their account.');
|
||||
}
|
||||
|
||||
public function getFullDescription() {
|
||||
return pht(
|
||||
'Guidance included in the mail message body when users request an '.
|
||||
'email link to access their account.'.
|
||||
"\n\n".
|
||||
'For installs with password authentication enabled, users access this '.
|
||||
'workflow by using the "Forgot your password?" link on the login '.
|
||||
'screen.'.
|
||||
"\n\n".
|
||||
'For installs without password authentication enabled, users access '.
|
||||
'this workflow by using the "Send a login link to your email address." '.
|
||||
'link on the login screen. This workflow allows users to recover '.
|
||||
'access to their account if there is an issue with an external '.
|
||||
'login service.');
|
||||
}
|
||||
|
||||
public function getDefaultMessageText() {
|
||||
return pht(
|
||||
'You (or someone pretending to be you) recently requested an account '.
|
||||
'recovery link be sent to this email address. If you did not make '.
|
||||
'this request, you can ignore this message.');
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthEmailSetPasswordMessageType
|
||||
extends PhabricatorAuthMessageType {
|
||||
|
||||
const MESSAGEKEY = 'mail.set-password';
|
||||
|
||||
public function getDisplayName() {
|
||||
return pht('Mail Body: Set Password');
|
||||
}
|
||||
|
||||
public function getShortDescription() {
|
||||
return pht(
|
||||
'Guidance in the message body when users set a password on an account '.
|
||||
'which did not previously have a password.');
|
||||
}
|
||||
|
||||
}
|
@@ -28,5 +28,14 @@ abstract class PhabricatorAuthMessageType
|
||||
}
|
||||
|
||||
abstract public function getDisplayName();
|
||||
abstract public function getShortDescription();
|
||||
|
||||
public function getFullDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getDefaultMessageText() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@ final class PhabricatorAuthWelcomeMailMessageType
|
||||
const MESSAGEKEY = 'mail.welcome';
|
||||
|
||||
public function getDisplayName() {
|
||||
return pht('Welcome Email Body');
|
||||
return pht('Mail Body: Welcome');
|
||||
}
|
||||
|
||||
public function getShortDescription() {
|
||||
|
@@ -59,6 +59,14 @@ final class PhabricatorAsanaAuthProvider
|
||||
return null;
|
||||
}
|
||||
|
||||
if (strlen($uri->getFragment())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($uri->getQueryParamsAsPairList()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$context_id = $matches[1];
|
||||
$task_id = $matches[2];
|
||||
|
||||
|
@@ -341,6 +341,14 @@ final class PhabricatorJIRAAuthProvider
|
||||
return null;
|
||||
}
|
||||
|
||||
if (strlen($uri->getFragment())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($uri->getQueryParamsAsPairList()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$domain = $matches[1];
|
||||
$issue = $matches[2];
|
||||
|
||||
|
@@ -282,48 +282,29 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider {
|
||||
$viewer = $request->getUser();
|
||||
$content_source = PhabricatorContentSource::newFromRequest($request);
|
||||
|
||||
$captcha_limit = 5;
|
||||
$hard_limit = 32;
|
||||
$limit_window = phutil_units('15 minutes in seconds');
|
||||
$rate_actor = PhabricatorSystemActionEngine::newActorFromRequest($request);
|
||||
|
||||
$failed_attempts = PhabricatorUserLog::loadRecentEventsFromThisIP(
|
||||
PhabricatorUserLog::ACTION_LOGIN_FAILURE,
|
||||
$limit_window);
|
||||
PhabricatorSystemActionEngine::willTakeAction(
|
||||
array($rate_actor),
|
||||
new PhabricatorAuthTryPasswordAction(),
|
||||
1);
|
||||
|
||||
// If the same remote address has submitted several failed login attempts
|
||||
// recently, require they provide a CAPTCHA response for new attempts.
|
||||
$require_captcha = false;
|
||||
$captcha_valid = false;
|
||||
if (AphrontFormRecaptchaControl::isRecaptchaEnabled()) {
|
||||
if (count($failed_attempts) > $captcha_limit) {
|
||||
try {
|
||||
PhabricatorSystemActionEngine::willTakeAction(
|
||||
array($rate_actor),
|
||||
new PhabricatorAuthTryPasswordWithoutCAPTCHAAction(),
|
||||
1);
|
||||
} catch (PhabricatorSystemActionRateLimitException $ex) {
|
||||
$require_captcha = true;
|
||||
$captcha_valid = AphrontFormRecaptchaControl::processCaptcha($request);
|
||||
}
|
||||
}
|
||||
|
||||
// If the user has submitted quite a few failed login attempts recently,
|
||||
// give them a hard limit.
|
||||
if (count($failed_attempts) > $hard_limit) {
|
||||
$guidance = array();
|
||||
|
||||
$guidance[] = pht(
|
||||
'Your remote address has failed too many login attempts recently. '.
|
||||
'Wait a few minutes before trying again.');
|
||||
|
||||
$guidance[] = pht(
|
||||
'If you are unable to log in to your account, you can '.
|
||||
'[[ /login/email | send a reset link to your email address ]].');
|
||||
|
||||
$guidance = implode("\n\n", $guidance);
|
||||
|
||||
$dialog = $controller->newDialog()
|
||||
->setTitle(pht('Too Many Login Attempts'))
|
||||
->appendChild(new PHUIRemarkupView($viewer, $guidance))
|
||||
->addCancelButton('/auth/start/', pht('Wait Patiently'));
|
||||
|
||||
return array(null, $dialog);
|
||||
}
|
||||
|
||||
$response = null;
|
||||
$account = null;
|
||||
$log_user = null;
|
||||
@@ -364,7 +345,7 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider {
|
||||
$log = PhabricatorUserLog::initializeNewLog(
|
||||
null,
|
||||
$log_user ? $log_user->getPHID() : null,
|
||||
PhabricatorUserLog::ACTION_LOGIN_FAILURE);
|
||||
PhabricatorLoginFailureUserLogType::LOGTYPE);
|
||||
$log->save();
|
||||
}
|
||||
|
||||
|
@@ -45,7 +45,7 @@ final class PhabricatorAuthMessage
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
return urisprintf('/auth/message/%s', $this->getID());
|
||||
return urisprintf('/auth/message/%s/', $this->getID());
|
||||
}
|
||||
|
||||
public function attachMessageType(PhabricatorAuthMessageType $type) {
|
||||
@@ -75,12 +75,16 @@ final class PhabricatorAuthMessage
|
||||
$message_key) {
|
||||
|
||||
$message = self::loadMessage($viewer, $message_key);
|
||||
|
||||
if (!$message) {
|
||||
return null;
|
||||
if ($message) {
|
||||
$message_text = $message->getMessageText();
|
||||
if (strlen($message_text)) {
|
||||
return $message_text;
|
||||
}
|
||||
}
|
||||
|
||||
return $message->getMessageText();
|
||||
$message_type = PhabricatorAuthMessageType::newFromKey($message_key);
|
||||
|
||||
return $message_type->getDefaultMessageText();
|
||||
}
|
||||
|
||||
|
||||
|
@@ -14,15 +14,8 @@ final class PhabricatorAuthProviderConfigTransaction
|
||||
|
||||
const PROPERTY_KEY = 'auth:property';
|
||||
|
||||
private $provider;
|
||||
|
||||
public function setProvider(PhabricatorAuthProvider $provider) {
|
||||
$this->provider = $provider;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getProvider() {
|
||||
return $this->provider;
|
||||
return $this->getObject()->getProvider();
|
||||
}
|
||||
|
||||
public function getApplicationName() {
|
||||
|
@@ -481,7 +481,7 @@ abstract class PhabricatorController extends AphrontController {
|
||||
|
||||
protected function buildTransactionTimeline(
|
||||
PhabricatorApplicationTransactionInterface $object,
|
||||
PhabricatorApplicationTransactionQuery $query,
|
||||
PhabricatorApplicationTransactionQuery $query = null,
|
||||
PhabricatorMarkupEngine $engine = null,
|
||||
$view_data = array()) {
|
||||
|
||||
@@ -489,6 +489,17 @@ abstract class PhabricatorController extends AphrontController {
|
||||
$viewer = $this->getViewer();
|
||||
$xaction = $object->getApplicationTransactionTemplate();
|
||||
|
||||
if (!$query) {
|
||||
$query = PhabricatorApplicationTransactionQuery::newQueryForObject(
|
||||
$object);
|
||||
if (!$query) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unable to find transaction query for object of class "%s".',
|
||||
get_class($object)));
|
||||
}
|
||||
}
|
||||
|
||||
$pager = id(new AphrontCursorPagerView())
|
||||
->readFromRequest($request)
|
||||
->setURI(new PhutilURI(
|
||||
|
@@ -30,4 +30,8 @@ final class PhabricatorMarkupCache extends PhabricatorCacheDAO {
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
public function getSchemaPersistence() {
|
||||
return PhabricatorConfigTableSchema::PERSISTENCE_CACHE;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -41,7 +41,7 @@ final class ConduitGetCertificateConduitAPIMethod extends ConduitAPIMethod {
|
||||
|
||||
protected function execute(ConduitAPIRequest $request) {
|
||||
$failed_attempts = PhabricatorUserLog::loadRecentEventsFromThisIP(
|
||||
PhabricatorUserLog::ACTION_CONDUIT_CERTIFICATE_FAILURE,
|
||||
PhabricatorConduitCertificateFailureUserLogType::LOGTYPE,
|
||||
60 * 5);
|
||||
|
||||
if (count($failed_attempts) > 5) {
|
||||
@@ -61,7 +61,7 @@ final class ConduitGetCertificateConduitAPIMethod extends ConduitAPIMethod {
|
||||
$log = PhabricatorUserLog::initializeNewLog(
|
||||
$request->getUser(),
|
||||
$info->getUserPHID(),
|
||||
PhabricatorUserLog::ACTION_CONDUIT_CERTIFICATE)
|
||||
PhabricatorConduitCertificateUserLogType::LOGTYPE)
|
||||
->save();
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ final class ConduitGetCertificateConduitAPIMethod extends ConduitAPIMethod {
|
||||
$log = PhabricatorUserLog::initializeNewLog(
|
||||
$request->getUser(),
|
||||
$info ? $info->getUserPHID() : '-',
|
||||
PhabricatorUserLog::ACTION_CONDUIT_CERTIFICATE_FAILURE)
|
||||
PhabricatorConduitCertificateFailureUserLogType::LOGTYPE)
|
||||
->save();
|
||||
}
|
||||
|
||||
|
@@ -3,8 +3,24 @@
|
||||
final class ConduitEpochParameterType
|
||||
extends ConduitParameterType {
|
||||
|
||||
private $allowNull;
|
||||
|
||||
public function setAllowNull($allow_null) {
|
||||
$this->allowNull = $allow_null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAllowNull() {
|
||||
return $this->allowNull;
|
||||
}
|
||||
|
||||
protected function getParameterValue(array $request, $key, $strict) {
|
||||
$value = parent::getParameterValue($request, $key, $strict);
|
||||
|
||||
if ($this->allowNull && ($value === null)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$value = $this->parseIntValue($request, $key, $value, $strict);
|
||||
|
||||
if ($value <= 0) {
|
||||
|
@@ -536,6 +536,9 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
|
||||
|
||||
'differential.whitespace-matters' => pht(
|
||||
'Whitespace rendering is now handled automatically.'),
|
||||
|
||||
'phd.pid-directory' => pht(
|
||||
'Phabricator daemons no longer use PID files.'),
|
||||
);
|
||||
|
||||
return $ancient_config;
|
||||
|
29
src/applications/config/check/PhabricatorZipSetupCheck.php
Normal file
29
src/applications/config/check/PhabricatorZipSetupCheck.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorZipSetupCheck extends PhabricatorSetupCheck {
|
||||
|
||||
public function getDefaultGroup() {
|
||||
return self::GROUP_OTHER;
|
||||
}
|
||||
|
||||
protected function executeChecks() {
|
||||
if (!extension_loaded('zip')) {
|
||||
$message = pht(
|
||||
'The PHP "zip" extension is not installed. This extension is '.
|
||||
'required by certain data export operations, including exporting '.
|
||||
'data to Excel.'.
|
||||
"\n\n".
|
||||
'To clear this setup issue, install the extension and restart your '.
|
||||
'webserver.'.
|
||||
"\n\n".
|
||||
'You may safely ignore this issue if you do not plan to export '.
|
||||
'data in Zip archives or Excel spreadsheets, or intend to install '.
|
||||
'the extension later.');
|
||||
|
||||
$this->newIssue('extension.zip')
|
||||
->setName(pht('Missing "zip" Extension'))
|
||||
->setMessage($message)
|
||||
->addPHPExtension('zip');
|
||||
}
|
||||
}
|
||||
}
|
@@ -30,11 +30,10 @@ final class PhabricatorConfigManagementSetWorkflow
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
$argv = $args->getArg('args');
|
||||
if (count($argv) == 0) {
|
||||
if (!$argv) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('Specify a configuration key and a value to set it to.'));
|
||||
pht('Specify the configuration key you want to set.'));
|
||||
}
|
||||
|
||||
$is_stdin = $args->getArg('stdin');
|
||||
@@ -45,7 +44,8 @@ final class PhabricatorConfigManagementSetWorkflow
|
||||
if (count($argv) > 1) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Too many arguments: expected only a key when using "--stdin".'));
|
||||
'Too many arguments: expected only a configuration key when '.
|
||||
'using "--stdin".'));
|
||||
}
|
||||
|
||||
fprintf(STDERR, tsprintf("%s\n", pht('Reading value from stdin...')));
|
||||
@@ -54,7 +54,8 @@ final class PhabricatorConfigManagementSetWorkflow
|
||||
if (count($argv) == 1) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
"Specify a value to set the key '%s' to.",
|
||||
'Specify a value to set the configuration key "%s" to, or '.
|
||||
'use "--stdin" to read a value from stdin.',
|
||||
$key));
|
||||
}
|
||||
|
||||
@@ -67,14 +68,13 @@ final class PhabricatorConfigManagementSetWorkflow
|
||||
$value = $argv[1];
|
||||
}
|
||||
|
||||
|
||||
$options = PhabricatorApplicationConfigOptions::loadAllOptions();
|
||||
if (empty($options[$key])) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
"No such configuration key '%s'! Use `%s` to list all keys.",
|
||||
$key,
|
||||
'config list'));
|
||||
'Configuration key "%s" is unknown. Use "bin/config list" to list '.
|
||||
'all known keys.',
|
||||
$key));
|
||||
}
|
||||
|
||||
$option = $options[$key];
|
||||
@@ -99,7 +99,7 @@ final class PhabricatorConfigManagementSetWorkflow
|
||||
switch ($type) {
|
||||
default:
|
||||
$message = pht(
|
||||
'Config key "%s" is of type "%s". Specify it in JSON.',
|
||||
'Configuration key "%s" is of type "%s". Specify it in JSON.',
|
||||
$key,
|
||||
$type);
|
||||
break;
|
||||
@@ -128,7 +128,6 @@ final class PhabricatorConfigManagementSetWorkflow
|
||||
}
|
||||
|
||||
if ($use_database) {
|
||||
$config_type = 'database';
|
||||
$config_entry = PhabricatorConfigEntry::loadConfigEntry($key);
|
||||
$config_entry->setValue($value);
|
||||
|
||||
@@ -136,15 +135,28 @@ final class PhabricatorConfigManagementSetWorkflow
|
||||
$config_entry->setIsDeleted(0);
|
||||
|
||||
$config_entry->save();
|
||||
|
||||
$write_message = pht(
|
||||
'Wrote configuration key "%s" to database storage.',
|
||||
$key);
|
||||
} else {
|
||||
$config_type = 'local';
|
||||
id(new PhabricatorConfigLocalSource())
|
||||
$config_source = id(new PhabricatorConfigLocalSource())
|
||||
->setKeys(array($key => $value));
|
||||
|
||||
$local_path = $config_source->getReadablePath();
|
||||
|
||||
$write_message = pht(
|
||||
'Wrote configuration key "%s" to local storage (in file "%s").',
|
||||
$key,
|
||||
$local_path);
|
||||
}
|
||||
|
||||
$console->writeOut(
|
||||
"%s\n",
|
||||
pht("Set '%s' in %s configuration.", $key, $config_type));
|
||||
echo tsprintf(
|
||||
"<bg:green>** %s **</bg> %s\n",
|
||||
pht('DONE'),
|
||||
$write_message);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -21,10 +21,6 @@ final class PhabricatorPHDConfigOptions
|
||||
|
||||
public function getOptions() {
|
||||
return array(
|
||||
$this->newOption('phd.pid-directory', 'string', '/var/tmp/phd/pid')
|
||||
->setLocked(true)
|
||||
->setDescription(
|
||||
pht('Directory that phd should use to track running daemons.')),
|
||||
$this->newOption('phd.log-directory', 'string', '/var/tmp/phd/log')
|
||||
->setLocked(true)
|
||||
->setDescription(
|
||||
|
@@ -48,11 +48,19 @@ abstract class PhabricatorConfigSchemaSpec extends Phobject {
|
||||
abstract public function buildSchemata();
|
||||
|
||||
protected function buildLiskObjectSchema(PhabricatorLiskDAO $object) {
|
||||
$index_options = array();
|
||||
|
||||
$persistence = $object->getSchemaPersistence();
|
||||
if ($persistence !== null) {
|
||||
$index_options['persistence'] = $persistence;
|
||||
}
|
||||
|
||||
$this->buildRawSchema(
|
||||
$object->getApplicationName(),
|
||||
$object->getTableName(),
|
||||
$object->getSchemaColumns(),
|
||||
$object->getSchemaKeys());
|
||||
$object->getSchemaKeys(),
|
||||
$index_options);
|
||||
}
|
||||
|
||||
protected function buildFerretIndexSchema(PhabricatorFerretEngine $engine) {
|
||||
|
@@ -85,6 +85,7 @@ final class PhabricatorDaemonConsoleController
|
||||
phutil_tag('em', array(), pht('Temporary Failures')),
|
||||
count($failed),
|
||||
null,
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -6,7 +6,10 @@ final class PhabricatorDaemonManagementRestartWorkflow
|
||||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('restart')
|
||||
->setSynopsis(pht('Stop, then start the standard daemon loadout.'))
|
||||
->setSynopsis(
|
||||
pht(
|
||||
'Stop daemon processes on this host, then start the standard '.
|
||||
'daemon loadout.'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
@@ -17,17 +20,15 @@ final class PhabricatorDaemonManagementRestartWorkflow
|
||||
'seconds. Defaults to __15__ seconds.'),
|
||||
'default' => 15,
|
||||
),
|
||||
array(
|
||||
'name' => 'gently',
|
||||
'help' => pht(
|
||||
'Ignore running processes that look like daemons but do not '.
|
||||
'have corresponding PID files.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'force',
|
||||
'help' => pht(
|
||||
'Also stop running processes that look like daemons but do '.
|
||||
'not have corresponding PID files.'),
|
||||
'Stop all daemon processes on this host, even if they belong '.
|
||||
'to another Phabricator instance.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'gently',
|
||||
'help' => pht('Deprecated. Has no effect.'),
|
||||
),
|
||||
$this->getAutoscaleReserveArgument(),
|
||||
));
|
||||
@@ -35,12 +36,11 @@ final class PhabricatorDaemonManagementRestartWorkflow
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$err = $this->executeStopCommand(
|
||||
array(),
|
||||
array(
|
||||
'graceful' => $args->getArg('graceful'),
|
||||
'force' => $args->getArg('force'),
|
||||
'gently' => $args->getArg('gently'),
|
||||
));
|
||||
|
||||
if ($err) {
|
||||
return $err;
|
||||
}
|
||||
|
@@ -6,101 +6,52 @@ final class PhabricatorDaemonManagementStatusWorkflow
|
||||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('status')
|
||||
->setSynopsis(pht('Show status of running daemons.'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'local',
|
||||
'help' => pht('Show only local daemons.'),
|
||||
),
|
||||
));
|
||||
->setSynopsis(pht('Show daemon processes on this host.'));
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
$process_refs = $this->getOverseerProcessRefs();
|
||||
|
||||
if ($args->getArg('local')) {
|
||||
$daemons = $this->loadRunningDaemons();
|
||||
} else {
|
||||
$daemons = $this->loadAllRunningDaemons();
|
||||
}
|
||||
if (!$process_refs) {
|
||||
$instance = $this->getInstance();
|
||||
if ($instance !== null) {
|
||||
$this->logInfo(
|
||||
pht('NO DAEMONS'),
|
||||
pht(
|
||||
'There are no running daemon processes for the current '.
|
||||
'instance ("%s").',
|
||||
$instance));
|
||||
} else {
|
||||
$this->logInfo(
|
||||
pht('NO DAEMONS'),
|
||||
pht('There are no running daemon processes.'));
|
||||
}
|
||||
|
||||
if (!$daemons) {
|
||||
$console->writeErr(
|
||||
"%s\n",
|
||||
pht('There are no running Phabricator daemons.'));
|
||||
return 1;
|
||||
}
|
||||
|
||||
$status = 0;
|
||||
|
||||
$table = id(new PhutilConsoleTable())
|
||||
->addColumns(array(
|
||||
'id' => array(
|
||||
'title' => pht('Log'),
|
||||
),
|
||||
'daemonID' => array(
|
||||
'title' => pht('Daemon'),
|
||||
),
|
||||
'host' => array(
|
||||
'title' => pht('Host'),
|
||||
),
|
||||
'pid' => array(
|
||||
'title' => pht('Overseer'),
|
||||
),
|
||||
'started' => array(
|
||||
'title' => pht('Started'),
|
||||
),
|
||||
'daemon' => array(
|
||||
'title' => pht('Class'),
|
||||
),
|
||||
'argv' => array(
|
||||
'title' => pht('Arguments'),
|
||||
),
|
||||
));
|
||||
|
||||
foreach ($daemons as $daemon) {
|
||||
if ($daemon instanceof PhabricatorDaemonLog) {
|
||||
$table->addRow(array(
|
||||
'id' => $daemon->getID(),
|
||||
'daemonID' => $daemon->getDaemonID(),
|
||||
'host' => $daemon->getHost(),
|
||||
'pid' => $daemon->getPID(),
|
||||
'started' => date('M j Y, g:i:s A', $daemon->getDateCreated()),
|
||||
'daemon' => $daemon->getDaemon(),
|
||||
'argv' => csprintf('%LR', $daemon->getExplicitArgv()),
|
||||
->addColumns(
|
||||
array(
|
||||
'pid' => array(
|
||||
'title' => pht('PID'),
|
||||
),
|
||||
'command' => array(
|
||||
'title' => pht('Command'),
|
||||
),
|
||||
));
|
||||
} else if ($daemon instanceof PhabricatorDaemonReference) {
|
||||
$name = $daemon->getName();
|
||||
if (!$daemon->isRunning()) {
|
||||
$daemon->updateStatus(PhabricatorDaemonLog::STATUS_DEAD);
|
||||
$status = 2;
|
||||
$name = pht('<DEAD> %s', $name);
|
||||
}
|
||||
|
||||
$daemon_log = $daemon->getDaemonLog();
|
||||
$id = null;
|
||||
$daemon_id = null;
|
||||
if ($daemon_log) {
|
||||
$id = $daemon_log->getID();
|
||||
$daemon_id = $daemon_log->getDaemonID();
|
||||
}
|
||||
|
||||
$table->addRow(array(
|
||||
'id' => $id,
|
||||
'daemonID' => $daemon_id,
|
||||
'host' => 'localhost',
|
||||
'pid' => $daemon->getPID(),
|
||||
'started' => $daemon->getEpochStarted()
|
||||
? date('M j Y, g:i:s A', $daemon->getEpochStarted())
|
||||
: null,
|
||||
'daemon' => $name,
|
||||
'argv' => csprintf('%LR', $daemon->getArgv()),
|
||||
foreach ($process_refs as $process_ref) {
|
||||
$table->addRow(
|
||||
array(
|
||||
'pid' => $process_ref->getPID(),
|
||||
'command' => $process_ref->getCommand(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
$table->draw();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -6,11 +6,7 @@ final class PhabricatorDaemonManagementStopWorkflow
|
||||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('stop')
|
||||
->setSynopsis(
|
||||
pht(
|
||||
'Stop all running daemons, or specific daemons identified by PIDs. '.
|
||||
'Use **%s** to find PIDs.',
|
||||
'phd status'))
|
||||
->setSynopsis(pht('Stop daemon processes on this host.'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
@@ -24,29 +20,21 @@ final class PhabricatorDaemonManagementStopWorkflow
|
||||
array(
|
||||
'name' => 'force',
|
||||
'help' => pht(
|
||||
'Also stop running processes that look like daemons but do '.
|
||||
'not have corresponding PID files.'),
|
||||
'Stop all daemon processes on this host, even if they belong '.
|
||||
'to another Phabricator instance.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'gently',
|
||||
'help' => pht(
|
||||
'Ignore running processes that look like daemons but do not '.
|
||||
'have corresponding PID files.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'pids',
|
||||
'wildcard' => true,
|
||||
'help' => pht('Deprecated. Has no effect.'),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
return $this->executeStopCommand(
|
||||
$args->getArg('pids'),
|
||||
array(
|
||||
'graceful' => $args->getArg('graceful'),
|
||||
'force' => $args->getArg('force'),
|
||||
'gently' => $args->getArg('gently'),
|
||||
));
|
||||
}
|
||||
|
||||
|
@@ -12,11 +12,6 @@ abstract class PhabricatorDaemonManagementWorkflow
|
||||
->selectSymbolsWithoutLoading();
|
||||
}
|
||||
|
||||
final protected function getPIDDirectory() {
|
||||
$path = PhabricatorEnv::getEnvConfig('phd.pid-directory');
|
||||
return $this->getControlDirectory($path);
|
||||
}
|
||||
|
||||
final protected function getLogDirectory() {
|
||||
$path = PhabricatorEnv::getEnvConfig('phd.log-directory');
|
||||
return $this->getControlDirectory($path);
|
||||
@@ -30,56 +25,16 @@ abstract class PhabricatorDaemonManagementWorkflow
|
||||
pht(
|
||||
"%s requires the directory '%s' to exist, but it does not exist ".
|
||||
"and could not be created. Create this directory or update ".
|
||||
"'%s' / '%s' in your configuration to point to an existing ".
|
||||
"'%s' in your configuration to point to an existing ".
|
||||
"directory.",
|
||||
'phd',
|
||||
$path,
|
||||
'phd.pid-directory',
|
||||
'phd.log-directory'));
|
||||
}
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
final protected function loadRunningDaemons() {
|
||||
$daemons = array();
|
||||
|
||||
$pid_dir = $this->getPIDDirectory();
|
||||
$pid_files = Filesystem::listDirectory($pid_dir);
|
||||
|
||||
foreach ($pid_files as $pid_file) {
|
||||
$path = $pid_dir.'/'.$pid_file;
|
||||
$daemons[] = PhabricatorDaemonReference::loadReferencesFromFile($path);
|
||||
}
|
||||
|
||||
return array_mergev($daemons);
|
||||
}
|
||||
|
||||
final protected function loadAllRunningDaemons() {
|
||||
$local_daemons = $this->loadRunningDaemons();
|
||||
|
||||
$local_ids = array();
|
||||
foreach ($local_daemons as $daemon) {
|
||||
$daemon_log = $daemon->getDaemonLog();
|
||||
|
||||
if ($daemon_log) {
|
||||
$local_ids[] = $daemon_log->getID();
|
||||
}
|
||||
}
|
||||
|
||||
$daemon_query = id(new PhabricatorDaemonLogQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE);
|
||||
|
||||
if ($local_ids) {
|
||||
$daemon_query->withoutIDs($local_ids);
|
||||
}
|
||||
|
||||
$remote_daemons = $daemon_query->execute();
|
||||
|
||||
return array_merge($local_daemons, $remote_daemons);
|
||||
}
|
||||
|
||||
private function findDaemonClass($substring) {
|
||||
$symbols = $this->loadAvailableDaemonClasses();
|
||||
|
||||
@@ -169,7 +124,7 @@ abstract class PhabricatorDaemonManagementWorkflow
|
||||
$flags[] = '--verbose';
|
||||
}
|
||||
|
||||
$instance = PhabricatorEnv::getEnvConfig('cluster.instance');
|
||||
$instance = $this->getInstance();
|
||||
if ($instance) {
|
||||
$flags[] = '-l';
|
||||
$flags[] = $instance;
|
||||
@@ -185,14 +140,6 @@ abstract class PhabricatorDaemonManagementWorkflow
|
||||
$config['log'] = $this->getLogDirectory().'/daemons.log';
|
||||
}
|
||||
|
||||
$pid_dir = $this->getPIDDirectory();
|
||||
|
||||
// TODO: This should be a much better user experience.
|
||||
Filesystem::assertExists($pid_dir);
|
||||
Filesystem::assertIsDirectory($pid_dir);
|
||||
Filesystem::assertWritable($pid_dir);
|
||||
|
||||
$config['piddir'] = $pid_dir;
|
||||
$config['daemons'] = $daemons;
|
||||
|
||||
$command = csprintf('./phd-daemon %Ls', $flags);
|
||||
@@ -324,28 +271,31 @@ abstract class PhabricatorDaemonManagementWorkflow
|
||||
$console = PhutilConsole::getConsole();
|
||||
|
||||
if (!idx($options, 'force')) {
|
||||
$running = $this->loadRunningDaemons();
|
||||
$process_refs = $this->getOverseerProcessRefs();
|
||||
if ($process_refs) {
|
||||
$this->logWarn(
|
||||
pht('RUNNING DAEMONS'),
|
||||
pht('Daemons are already running:'));
|
||||
|
||||
// This may include daemons which were launched but which are no longer
|
||||
// running; check that we actually have active daemons before failing.
|
||||
foreach ($running as $daemon) {
|
||||
if ($daemon->isRunning()) {
|
||||
$message = pht(
|
||||
"phd start: Unable to start daemons because daemons are already ".
|
||||
"running.\n\n".
|
||||
"You can view running daemons with '%s'.\n".
|
||||
"You can stop running daemons with '%s'.\n".
|
||||
"You can use '%s' to stop all daemons before starting ".
|
||||
"new daemons.\n".
|
||||
"You can force daemons to start anyway with %s.",
|
||||
'phd status',
|
||||
'phd stop',
|
||||
'phd restart',
|
||||
'--force');
|
||||
|
||||
$console->writeErr("%s\n", $message);
|
||||
exit(1);
|
||||
fprintf(STDERR, '%s', "\n");
|
||||
foreach ($process_refs as $process_ref) {
|
||||
fprintf(
|
||||
STDERR,
|
||||
'%s',
|
||||
tsprintf(
|
||||
" %s %s\n",
|
||||
$process_ref->getPID(),
|
||||
$process_ref->getCommand()));
|
||||
}
|
||||
fprintf(STDERR, '%s', "\n");
|
||||
|
||||
$this->logFail(
|
||||
pht('RUNNING DAEMONS'),
|
||||
pht(
|
||||
'Use "phd stop" to stop daemons, "phd restart" to restart '.
|
||||
'daemons, or "phd start --force" to ignore running processes.'));
|
||||
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,148 +336,115 @@ abstract class PhabricatorDaemonManagementWorkflow
|
||||
return 0;
|
||||
}
|
||||
|
||||
final protected function executeStopCommand(
|
||||
array $pids,
|
||||
array $options) {
|
||||
|
||||
$console = PhutilConsole::getConsole();
|
||||
|
||||
final protected function executeStopCommand(array $options) {
|
||||
$grace_period = idx($options, 'graceful', 15);
|
||||
$force = idx($options, 'force');
|
||||
$gently = idx($options, 'gently');
|
||||
|
||||
if ($gently && $force) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
$query = id(new PhutilProcessQuery())
|
||||
->withIsOverseer(true);
|
||||
|
||||
$instance = $this->getInstance();
|
||||
if ($instance !== null && !$force) {
|
||||
$query->withInstances(array($instance));
|
||||
}
|
||||
|
||||
try {
|
||||
$process_refs = $query->execute();
|
||||
} catch (Exception $ex) {
|
||||
// See T13321. If this fails for some reason, just continue for now so
|
||||
// that daemon management still works. In the long run, we don't expect
|
||||
// this to fail, but I don't want to break this workflow while we iron
|
||||
// bugs out.
|
||||
|
||||
// See T12827. Particularly, this is likely to fail on Solaris.
|
||||
|
||||
phlog($ex);
|
||||
|
||||
$process_refs = array();
|
||||
}
|
||||
|
||||
if (!$process_refs) {
|
||||
if ($instance !== null && !$force) {
|
||||
$this->logInfo(
|
||||
pht('NO DAEMONS'),
|
||||
pht(
|
||||
'There are no running daemons for the current instance ("%s"). '.
|
||||
'Use "--force" to stop daemons for all instances.',
|
||||
$instance));
|
||||
} else {
|
||||
$this->logInfo(
|
||||
pht('NO DAEMONS'),
|
||||
pht('There are no running daemons.'));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$process_refs = mpull($process_refs, null, 'getPID');
|
||||
|
||||
$stop_pids = array_keys($process_refs);
|
||||
$live_pids = $this->sendStopSignals($stop_pids, $grace_period);
|
||||
|
||||
$stop_pids = array_fuse($stop_pids);
|
||||
$live_pids = array_fuse($live_pids);
|
||||
|
||||
$dead_pids = array_diff_key($stop_pids, $live_pids);
|
||||
|
||||
foreach ($dead_pids as $dead_pid) {
|
||||
$dead_ref = $process_refs[$dead_pid];
|
||||
$this->logOkay(
|
||||
pht('STOP'),
|
||||
pht(
|
||||
'You can not specify conflicting options %s and %s together.',
|
||||
'--gently',
|
||||
'--force'));
|
||||
'Stopped PID %d ("%s")',
|
||||
$dead_pid,
|
||||
$dead_ref->getCommand()));
|
||||
}
|
||||
|
||||
$daemons = $this->loadRunningDaemons();
|
||||
if (!$daemons) {
|
||||
$survivors = array();
|
||||
if (!$pids && !$gently) {
|
||||
$survivors = $this->processRogueDaemons(
|
||||
$grace_period,
|
||||
$warn = true,
|
||||
$force);
|
||||
}
|
||||
if (!$survivors) {
|
||||
$console->writeErr(
|
||||
"%s\n",
|
||||
pht('There are no running Phabricator daemons.'));
|
||||
}
|
||||
return 0;
|
||||
foreach ($live_pids as $live_pid) {
|
||||
$live_ref = $process_refs[$live_pid];
|
||||
$this->logFail(
|
||||
pht('SURVIVED'),
|
||||
pht(
|
||||
'Unable to stop PID %d ("%s").',
|
||||
$live_pid,
|
||||
$live_ref->getCommand()));
|
||||
}
|
||||
|
||||
$stop_pids = $this->selectDaemonPIDs($daemons, $pids);
|
||||
|
||||
if (!$stop_pids) {
|
||||
$console->writeErr("%s\n", pht('No daemons to kill.'));
|
||||
return 0;
|
||||
}
|
||||
|
||||
$survivors = $this->sendStopSignals($stop_pids, $grace_period);
|
||||
|
||||
// Try to clean up PID files for daemons we killed.
|
||||
$remove = array();
|
||||
foreach ($daemons as $daemon) {
|
||||
$pid = $daemon->getPID();
|
||||
if (empty($stop_pids[$pid])) {
|
||||
// We did not try to stop this overseer.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($survivors[$pid])) {
|
||||
// We weren't able to stop this overseer.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$daemon->getPIDFile()) {
|
||||
// We don't know where the PID file is.
|
||||
continue;
|
||||
}
|
||||
|
||||
$remove[] = $daemon->getPIDFile();
|
||||
}
|
||||
|
||||
foreach (array_unique($remove) as $remove_file) {
|
||||
Filesystem::remove($remove_file);
|
||||
}
|
||||
|
||||
if (!$gently) {
|
||||
$this->processRogueDaemons($grace_period, !$pids, $force);
|
||||
if ($live_pids) {
|
||||
$this->logWarn(
|
||||
pht('SURVIVORS'),
|
||||
pht(
|
||||
'Unable to stop all daemon processes. You may need to run this '.
|
||||
'command as root with "sudo".'));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
final protected function executeReloadCommand(array $pids) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
$process_refs = $this->getOverseerProcessRefs();
|
||||
|
||||
if (!$process_refs) {
|
||||
$this->logInfo(
|
||||
pht('NO DAEMONS'),
|
||||
pht('There are no running daemon processes to reload.'));
|
||||
|
||||
$daemons = $this->loadRunningDaemons();
|
||||
if (!$daemons) {
|
||||
$console->writeErr(
|
||||
"%s\n",
|
||||
pht('There are no running daemons to reload.'));
|
||||
return 0;
|
||||
}
|
||||
|
||||
$reload_pids = $this->selectDaemonPIDs($daemons, $pids);
|
||||
if (!$reload_pids) {
|
||||
$console->writeErr(
|
||||
"%s\n",
|
||||
pht('No daemons to reload.'));
|
||||
return 0;
|
||||
}
|
||||
foreach ($process_refs as $process_ref) {
|
||||
$pid = $process_ref->getPID();
|
||||
|
||||
foreach ($reload_pids as $pid) {
|
||||
$console->writeOut(
|
||||
"%s\n",
|
||||
$this->logInfo(
|
||||
pht('RELOAD'),
|
||||
pht('Reloading process %d...', $pid));
|
||||
|
||||
posix_kill($pid, SIGHUP);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function processRogueDaemons($grace_period, $warn, $force_stop) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
|
||||
$rogue_daemons = PhutilDaemonOverseer::findRunningDaemons();
|
||||
if ($rogue_daemons) {
|
||||
if ($force_stop) {
|
||||
$rogue_pids = ipull($rogue_daemons, 'pid');
|
||||
$survivors = $this->sendStopSignals($rogue_pids, $grace_period);
|
||||
if ($survivors) {
|
||||
$console->writeErr(
|
||||
"%s\n",
|
||||
pht(
|
||||
'Unable to stop processes running without PID files. '.
|
||||
'Try running this command again with sudo.'));
|
||||
}
|
||||
} else if ($warn) {
|
||||
$console->writeErr("%s\n", $this->getForceStopHint($rogue_daemons));
|
||||
}
|
||||
}
|
||||
|
||||
return $rogue_daemons;
|
||||
}
|
||||
|
||||
private function getForceStopHint($rogue_daemons) {
|
||||
$debug_output = '';
|
||||
foreach ($rogue_daemons as $rogue) {
|
||||
$debug_output .= $rogue['pid'].' '.$rogue['command']."\n";
|
||||
}
|
||||
return pht(
|
||||
"There are processes running that look like Phabricator daemons but ".
|
||||
"have no corresponding PID files:\n\n%s\n\n".
|
||||
"Stop these processes by re-running this command with the %s parameter.",
|
||||
$debug_output,
|
||||
'--force');
|
||||
}
|
||||
|
||||
private function sendStopSignals($pids, $grace_period) {
|
||||
// If we're doing a graceful shutdown, try SIGINT first.
|
||||
if ($grace_period) {
|
||||
@@ -674,4 +591,21 @@ abstract class PhabricatorDaemonManagementWorkflow
|
||||
return $select_pids;
|
||||
}
|
||||
|
||||
protected function getOverseerProcessRefs() {
|
||||
$query = id(new PhutilProcessQuery())
|
||||
->withIsOverseer(true);
|
||||
|
||||
$instance = PhabricatorEnv::getEnvConfig('cluster.instance');
|
||||
if ($instance !== null) {
|
||||
$query->withInstances(array($instance));
|
||||
}
|
||||
|
||||
return $query->execute();
|
||||
}
|
||||
|
||||
protected function getInstance() {
|
||||
return PhabricatorEnv::getEnvConfig('cluster.instance');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -119,8 +119,11 @@ final class DifferentialCreateDiffConduitAPIMethod
|
||||
break;
|
||||
}
|
||||
|
||||
$source_path = $request->getValue('sourcePath');
|
||||
$source_path = $this->normalizeSourcePath($source_path);
|
||||
|
||||
$diff_data_dict = array(
|
||||
'sourcePath' => $request->getValue('sourcePath'),
|
||||
'sourcePath' => $source_path,
|
||||
'sourceMachine' => $request->getValue('sourceMachine'),
|
||||
'branch' => $request->getValue('branch'),
|
||||
'creationMethod' => $request->getValue('creationMethod'),
|
||||
@@ -158,4 +161,18 @@ final class DifferentialCreateDiffConduitAPIMethod
|
||||
);
|
||||
}
|
||||
|
||||
private function normalizeSourcePath($source_path) {
|
||||
// See T13385. This property is probably headed for deletion. Until we get
|
||||
// there, stop errors arising from running "arc diff" in a working copy
|
||||
// with too many characters.
|
||||
|
||||
$max_size = id(new DifferentialDiff())
|
||||
->getColumnMaximumByteLength('sourcePath');
|
||||
|
||||
return id(new PhutilUTF8StringTruncator())
|
||||
->setMaximumBytes($max_size)
|
||||
->setTerminator('')
|
||||
->truncateString($source_path);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -51,6 +51,12 @@ final class DifferentialRevisionSearchEngine
|
||||
$map['createdEnd']);
|
||||
}
|
||||
|
||||
if ($map['modifiedStart'] || $map['modifiedEnd']) {
|
||||
$query->withUpdatedEpochBetween(
|
||||
$map['modifiedStart'],
|
||||
$map['modifiedEnd']);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
@@ -100,6 +106,18 @@ final class DifferentialRevisionSearchEngine
|
||||
->setKey('createdEnd')
|
||||
->setDescription(
|
||||
pht('Find revisions created at or before a particular time.')),
|
||||
id(new PhabricatorSearchDateField())
|
||||
->setLabel(pht('Modified After'))
|
||||
->setKey('modifiedStart')
|
||||
->setIsHidden(true)
|
||||
->setDescription(
|
||||
pht('Find revisions modified at or after a particular time.')),
|
||||
id(new PhabricatorSearchDateField())
|
||||
->setLabel(pht('Modified Before'))
|
||||
->setKey('modifiedEnd')
|
||||
->setIsHidden(true)
|
||||
->setDescription(
|
||||
pht('Find revisions modified at or before a particular time.')),
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -112,11 +112,6 @@ final class DifferentialRevision extends DifferentialDAO
|
||||
'repositoryPHID' => 'phid?',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'key_phid' => null,
|
||||
'phid' => array(
|
||||
'columns' => array('phid'),
|
||||
'unique' => true,
|
||||
),
|
||||
'authorPHID' => array(
|
||||
'columns' => array('authorPHID', 'status'),
|
||||
),
|
||||
@@ -131,6 +126,9 @@ final class DifferentialRevision extends DifferentialDAO
|
||||
'key_status' => array(
|
||||
'columns' => array('status', 'phid'),
|
||||
),
|
||||
'key_modified' => array(
|
||||
'columns' => array('dateModified'),
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
@@ -7,17 +7,6 @@ abstract class DiffusionQueryConduitAPIMethod
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getMethodStatus() {
|
||||
return self::METHOD_STATUS_UNSTABLE;
|
||||
}
|
||||
|
||||
public function getMethodStatusDescription() {
|
||||
return pht(
|
||||
'See T2784 - migrating Diffusion working copy calls to conduit methods. '.
|
||||
'Until that task is completed (and possibly after) these methods are '.
|
||||
'unstable.');
|
||||
}
|
||||
|
||||
private $diffusionRequest;
|
||||
private $repository;
|
||||
|
||||
|
@@ -145,13 +145,26 @@ final class DiffusionRepositoryController extends DiffusionController {
|
||||
->setRight(array($this->branchButton, $actions_button, $clone_button))
|
||||
->addClass('diffusion-action-bar');
|
||||
|
||||
$status_view = null;
|
||||
if ($repository->isReadOnly()) {
|
||||
$status_view = id(new PHUIInfoView())
|
||||
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
|
||||
->setErrors(
|
||||
array(
|
||||
phutil_escape_html_newlines(
|
||||
$repository->getReadOnlyMessageForDisplay()),
|
||||
));
|
||||
}
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setFooter(array(
|
||||
$bar,
|
||||
$description,
|
||||
$content,
|
||||
));
|
||||
->setFooter(
|
||||
array(
|
||||
$status_view,
|
||||
$bar,
|
||||
$description,
|
||||
$content,
|
||||
));
|
||||
|
||||
if ($page_has_content) {
|
||||
$view->setTabs($tabs);
|
||||
@@ -327,6 +340,8 @@ final class DiffusionRepositoryController extends DiffusionController {
|
||||
|
||||
if (!$repository->isTracked()) {
|
||||
$header->setStatus('fa-ban', 'dark', pht('Inactive'));
|
||||
} else if ($repository->isReadOnly()) {
|
||||
$header->setStatus('fa-wrench', 'indigo', pht('Under Maintenance'));
|
||||
} else if ($repository->isImporting()) {
|
||||
$ratio = $repository->loadImportProgress();
|
||||
$percentage = sprintf('%.2f%%', 100 * $ratio);
|
||||
|
@@ -17,32 +17,31 @@ final class DiffusionRepositoryEditDeleteController
|
||||
->setRepository($repository)
|
||||
->getPanelURI();
|
||||
|
||||
$dialog = new AphrontDialogView();
|
||||
$text_1 = pht(
|
||||
'If you really want to delete the repository, run this command from '.
|
||||
'the command line:');
|
||||
$command = csprintf(
|
||||
'phabricator/ $ ./bin/remove destroy %R',
|
||||
$repository->getMonogram());
|
||||
$text_2 = pht(
|
||||
'Repositories touch many objects and as such deletes are '.
|
||||
'prohibitively expensive to run from the web UI.');
|
||||
$body = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'phabricator-remarkup',
|
||||
),
|
||||
array(
|
||||
phutil_tag('p', array(), $text_1),
|
||||
phutil_tag('p', array(),
|
||||
phutil_tag('tt', array(), $command)),
|
||||
phutil_tag('p', array(), $text_2),
|
||||
));
|
||||
$doc_uri = PhabricatorEnv::getDoclink(
|
||||
'Permanently Destroying Data');
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Really want to delete the repository?'))
|
||||
->appendChild($body)
|
||||
->addCancelButton($panel_uri, pht('Okay'));
|
||||
->setTitle(pht('Delete Repository'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'To permanently destroy this repository, run this command from '.
|
||||
'the command line:'))
|
||||
->appendCommand(
|
||||
csprintf(
|
||||
'phabricator/ $ ./bin/remove destroy %R',
|
||||
$repository->getMonogram()))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'Repositories can not be permanently destroyed from the web '.
|
||||
'interface. See %s in the documentation for more information.',
|
||||
phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $doc_uri,
|
||||
'target' => '_blank',
|
||||
),
|
||||
pht('Permanently Destroying Data'))))
|
||||
->addCancelButton($panel_uri, pht('Close'));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -302,6 +302,12 @@ final class DiffusionServeController extends DiffusionController {
|
||||
}
|
||||
|
||||
if ($is_push) {
|
||||
if ($repository->isReadOnly()) {
|
||||
return new PhabricatorVCSResponse(
|
||||
503,
|
||||
$repository->getReadOnlyMessageForDisplay());
|
||||
}
|
||||
|
||||
$can_write =
|
||||
$repository->canServeProtocol($proto_https, true) ||
|
||||
$repository->canServeProtocol($proto_http, true);
|
||||
|
@@ -48,7 +48,8 @@ final class DiffusionCommitEditEngine
|
||||
return id(new DiffusionCommitQuery())
|
||||
->needCommitData(true)
|
||||
->needAuditRequests(true)
|
||||
->needAuditAuthority(array($viewer));
|
||||
->needAuditAuthority(array($viewer))
|
||||
->needIdentities(true);
|
||||
}
|
||||
|
||||
protected function getEditorURI() {
|
||||
|
@@ -155,8 +155,6 @@ final class DiffusionRepositoryBasicsManagementPanel
|
||||
->setName(pht('Delete Repository'))
|
||||
->setHref($delete_uri)
|
||||
->setIcon('fa-times')
|
||||
->setColor(PhabricatorActionView::RED)
|
||||
->setDisabled(true)
|
||||
->setWorkflow(true));
|
||||
|
||||
return $this->newCurtainView()
|
||||
|
@@ -255,6 +255,10 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
|
||||
'user account.'));
|
||||
}
|
||||
|
||||
if ($repository->isReadOnly()) {
|
||||
throw new Exception($repository->getReadOnlyMessageForDisplay());
|
||||
}
|
||||
|
||||
$protocol = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH;
|
||||
if ($repository->canServeProtocol($protocol, true)) {
|
||||
$can_push = PhabricatorPolicyFilter::hasCapability(
|
||||
|
@@ -47,7 +47,7 @@ final class DiffusionPatternSearchView extends DiffusionView {
|
||||
$offset = $match[1];
|
||||
if ($cursor != $offset) {
|
||||
$output[] = array(
|
||||
'text' => substr($string, $cursor, $offset),
|
||||
'text' => substr($string, $cursor, ($offset - $cursor)),
|
||||
'highlight' => false,
|
||||
);
|
||||
}
|
||||
|
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
final class DrydockResourceSearchConduitAPIMethod
|
||||
extends PhabricatorSearchEngineAPIMethod {
|
||||
|
||||
public function getAPIMethodName() {
|
||||
return 'drydock.resource.search';
|
||||
}
|
||||
|
||||
public function newSearchEngine() {
|
||||
return new DrydockResourceSearchEngine();
|
||||
}
|
||||
|
||||
public function getMethodSummary() {
|
||||
return pht('Retrieve information about Drydock resources.');
|
||||
}
|
||||
|
||||
}
|
@@ -44,6 +44,10 @@ final class DrydockLeaseSearchEngine
|
||||
$query->withOwnerPHIDs($map['ownerPHIDs']);
|
||||
}
|
||||
|
||||
if ($map['resourcePHIDs']) {
|
||||
$query->withResourcePHIDs($map['resourcePHIDs']);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
@@ -58,6 +62,11 @@ final class DrydockLeaseSearchEngine
|
||||
->setKey('ownerPHIDs')
|
||||
->setAliases(array('owner', 'owners', 'ownerPHID'))
|
||||
->setDescription(pht('Search leases by owner.')),
|
||||
id(new PhabricatorPHIDsSearchField())
|
||||
->setLabel(pht('Resources'))
|
||||
->setKey('resourcePHIDs')
|
||||
->setAliases(array('resorucePHID', 'resource', 'resources'))
|
||||
->setDescription(pht('Search leases by resource.')),
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -100,46 +100,50 @@ final class DrydockResourceQuery extends DrydockQuery {
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'id IN (%Ld)',
|
||||
'resource.id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'phid IN (%Ls)',
|
||||
'resource.phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->types !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'type IN (%Ls)',
|
||||
'resource.type IN (%Ls)',
|
||||
$this->types);
|
||||
}
|
||||
|
||||
if ($this->statuses !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'status IN (%Ls)',
|
||||
'resource.status IN (%Ls)',
|
||||
$this->statuses);
|
||||
}
|
||||
|
||||
if ($this->blueprintPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'blueprintPHID IN (%Ls)',
|
||||
'resource.blueprintPHID IN (%Ls)',
|
||||
$this->blueprintPHIDs);
|
||||
}
|
||||
|
||||
if ($this->datasourceQuery !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'name LIKE %>',
|
||||
'resource.name LIKE %>',
|
||||
$this->datasourceQuery);
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
protected function getPrimaryTableAlias() {
|
||||
return 'resource';
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -40,6 +40,10 @@ final class DrydockResourceSearchEngine
|
||||
$query->withStatuses($map['statuses']);
|
||||
}
|
||||
|
||||
if ($map['blueprintPHIDs']) {
|
||||
$query->withBlueprintPHIDs($map['blueprintPHIDs']);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
@@ -49,6 +53,12 @@ final class DrydockResourceSearchEngine
|
||||
->setLabel(pht('Statuses'))
|
||||
->setKey('statuses')
|
||||
->setOptions(DrydockResourceStatus::getStatusMap()),
|
||||
id(new PhabricatorPHIDsSearchField())
|
||||
->setLabel(pht('Blueprints'))
|
||||
->setKey('blueprintPHIDs')
|
||||
->setAliases(array('blueprintPHID', 'blueprints', 'blueprint'))
|
||||
->setDescription(
|
||||
pht('Search for resources generated by particular blueprints.')),
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
|
||||
final class DrydockResource extends DrydockDAO
|
||||
implements PhabricatorPolicyInterface {
|
||||
implements
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorConduitResultInterface {
|
||||
|
||||
protected $id;
|
||||
protected $phid;
|
||||
@@ -340,4 +342,38 @@ final class DrydockResource extends DrydockDAO
|
||||
public function describeAutomaticCapability($capability) {
|
||||
return pht('Resources inherit the policies of their blueprints.');
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorConduitResultInterface )---------------------------------- */
|
||||
|
||||
|
||||
public function getFieldSpecificationsForConduit() {
|
||||
return array(
|
||||
id(new PhabricatorConduitSearchFieldSpecification())
|
||||
->setKey('blueprintPHID')
|
||||
->setType('phid')
|
||||
->setDescription(pht('The blueprint which generated this resource.')),
|
||||
id(new PhabricatorConduitSearchFieldSpecification())
|
||||
->setKey('status')
|
||||
->setType('map<string, wild>')
|
||||
->setDescription(pht('Information about resource status.')),
|
||||
);
|
||||
}
|
||||
|
||||
public function getFieldValuesForConduit() {
|
||||
$status = $this->getStatus();
|
||||
|
||||
return array(
|
||||
'blueprintPHID' => $this->getBlueprintPHID(),
|
||||
'status' => array(
|
||||
'value' => $status,
|
||||
'name' => DrydockResourceStatus::getNameForStatus($status),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function getConduitSearchAttachments() {
|
||||
return array();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -5,10 +5,6 @@ final class PhabricatorFilesOutboundRequestAction
|
||||
|
||||
const TYPECONST = 'files.outbound';
|
||||
|
||||
public function getActionConstant() {
|
||||
return self::TYPECONST;
|
||||
}
|
||||
|
||||
public function getScoreThreshold() {
|
||||
return 60 / phutil_units('1 hour in seconds');
|
||||
}
|
||||
|
@@ -4,41 +4,25 @@ final class PhabricatorFilesManagementCompactWorkflow
|
||||
extends PhabricatorFilesManagementWorkflow {
|
||||
|
||||
protected function didConstruct() {
|
||||
$arguments = $this->newIteratorArguments();
|
||||
$arguments[] = array(
|
||||
'name' => 'dry-run',
|
||||
'help' => pht('Show what would be compacted.'),
|
||||
);
|
||||
|
||||
$this
|
||||
->setName('compact')
|
||||
->setSynopsis(
|
||||
pht(
|
||||
'Merge identical files to share the same storage. In some cases, '.
|
||||
'this can repair files with missing data.'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'dry-run',
|
||||
'help' => pht('Show what would be compacted.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'all',
|
||||
'help' => pht('Compact all files.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'names',
|
||||
'wildcard' => true,
|
||||
),
|
||||
));
|
||||
->setArguments($arguments);
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
|
||||
$iterator = $this->buildIterator($args);
|
||||
if (!$iterator) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Either specify a list of files to compact, or use `%s` '.
|
||||
'to compact all files.',
|
||||
'--all'));
|
||||
}
|
||||
|
||||
$is_dry_run = $args->getArg('dry-run');
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
|
@@ -4,36 +4,22 @@ final class PhabricatorFilesManagementCycleWorkflow
|
||||
extends PhabricatorFilesManagementWorkflow {
|
||||
|
||||
protected function didConstruct() {
|
||||
$arguments = $this->newIteratorArguments();
|
||||
$arguments[] = array(
|
||||
'name' => 'key',
|
||||
'param' => 'keyname',
|
||||
'help' => pht('Select a specific storage key to cycle to.'),
|
||||
);
|
||||
|
||||
$this
|
||||
->setName('cycle')
|
||||
->setSynopsis(
|
||||
pht('Cycle master key for encrypted files.'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'key',
|
||||
'param' => 'keyname',
|
||||
'help' => pht('Select a specific storage key to cycle to.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'all',
|
||||
'help' => pht('Change encoding for all files.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'names',
|
||||
'wildcard' => true,
|
||||
),
|
||||
));
|
||||
->setArguments($arguments);
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$iterator = $this->buildIterator($args);
|
||||
if (!$iterator) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Either specify a list of files to cycle, or use --all to cycle '.
|
||||
'all files.'));
|
||||
}
|
||||
|
||||
$format_map = PhabricatorFileStorageFormat::getAllFormats();
|
||||
$engines = PhabricatorFileStorageEngine::loadAllEngines();
|
||||
|
@@ -4,47 +4,36 @@ final class PhabricatorFilesManagementEncodeWorkflow
|
||||
extends PhabricatorFilesManagementWorkflow {
|
||||
|
||||
protected function didConstruct() {
|
||||
$arguments = $this->newIteratorArguments();
|
||||
|
||||
$arguments[] = array(
|
||||
'name' => 'as',
|
||||
'param' => 'format',
|
||||
'help' => pht('Select the storage format to use.'),
|
||||
);
|
||||
|
||||
$arguments[] = array(
|
||||
'name' => 'key',
|
||||
'param' => 'keyname',
|
||||
'help' => pht('Select a specific storage key.'),
|
||||
);
|
||||
|
||||
$arguments[] = array(
|
||||
'name' => 'force',
|
||||
'help' => pht(
|
||||
'Re-encode files which are already stored in the target '.
|
||||
'encoding.'),
|
||||
);
|
||||
|
||||
$this
|
||||
->setName('encode')
|
||||
->setSynopsis(
|
||||
pht('Change the storage encoding of files.'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'as',
|
||||
'param' => 'format',
|
||||
'help' => pht('Select the storage format to use.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'key',
|
||||
'param' => 'keyname',
|
||||
'help' => pht('Select a specific storage key.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'all',
|
||||
'help' => pht('Change encoding for all files.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'force',
|
||||
'help' => pht(
|
||||
'Re-encode files which are already stored in the target '.
|
||||
'encoding.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'names',
|
||||
'wildcard' => true,
|
||||
),
|
||||
));
|
||||
->setArguments($arguments);
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$iterator = $this->buildIterator($args);
|
||||
if (!$iterator) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Either specify a list of files to encode, or use --all to '.
|
||||
'encode all files.'));
|
||||
}
|
||||
|
||||
$force = (bool)$args->getArg('force');
|
||||
|
||||
|
@@ -4,52 +4,50 @@ final class PhabricatorFilesManagementIntegrityWorkflow
|
||||
extends PhabricatorFilesManagementWorkflow {
|
||||
|
||||
protected function didConstruct() {
|
||||
$arguments = $this->newIteratorArguments();
|
||||
|
||||
$arguments[] = array(
|
||||
'name' => 'strip',
|
||||
'help' => pht(
|
||||
'DANGEROUS. Strip integrity hashes from files. This makes '.
|
||||
'files vulnerable to corruption or tampering.'),
|
||||
);
|
||||
|
||||
$arguments[] = array(
|
||||
'name' => 'corrupt',
|
||||
'help' => pht(
|
||||
'Corrupt integrity hashes for given files. This is intended '.
|
||||
'for debugging.'),
|
||||
);
|
||||
|
||||
$arguments[] = array(
|
||||
'name' => 'compute',
|
||||
'help' => pht(
|
||||
'Compute and update integrity hashes for files which do not '.
|
||||
'yet have them.'),
|
||||
);
|
||||
|
||||
$arguments[] = array(
|
||||
'name' => 'overwrite',
|
||||
'help' => pht(
|
||||
'DANGEROUS. Recompute and update integrity hashes, overwriting '.
|
||||
'invalid hashes. This may mark corrupt or dangerous files as '.
|
||||
'valid.'),
|
||||
);
|
||||
|
||||
$arguments[] = array(
|
||||
'name' => 'force',
|
||||
'short' => 'f',
|
||||
'help' => pht(
|
||||
'Execute dangerous operations without prompting for '.
|
||||
'confirmation.'),
|
||||
);
|
||||
|
||||
|
||||
$this
|
||||
->setName('integrity')
|
||||
->setSynopsis(pht('Verify or recalculate file integrity hashes.'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'all',
|
||||
'help' => pht('Affect all files.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'strip',
|
||||
'help' => pht(
|
||||
'DANGEROUS. Strip integrity hashes from files. This makes '.
|
||||
'files vulnerable to corruption or tampering.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'corrupt',
|
||||
'help' => pht(
|
||||
'Corrupt integrity hashes for given files. This is intended '.
|
||||
'for debugging.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'compute',
|
||||
'help' => pht(
|
||||
'Compute and update integrity hashes for files which do not '.
|
||||
'yet have them.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'overwrite',
|
||||
'help' => pht(
|
||||
'DANGEROUS. Recompute and update integrity hashes, overwriting '.
|
||||
'invalid hashes. This may mark corrupt or dangerous files as '.
|
||||
'valid.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'force',
|
||||
'short' => 'f',
|
||||
'help' => pht(
|
||||
'Execute dangerous operations without prompting for '.
|
||||
'confirmation.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'names',
|
||||
'wildcard' => true,
|
||||
),
|
||||
));
|
||||
->setArguments($arguments);
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
@@ -120,12 +118,6 @@ final class PhabricatorFilesManagementIntegrityWorkflow
|
||||
}
|
||||
|
||||
$iterator = $this->buildIterator($args);
|
||||
if (!$iterator) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Either specify a list of files to affect, or use "--all" to '.
|
||||
'affect all files.'));
|
||||
}
|
||||
|
||||
$failure_count = 0;
|
||||
$total_count = 0;
|
||||
|
@@ -4,61 +4,54 @@ final class PhabricatorFilesManagementMigrateWorkflow
|
||||
extends PhabricatorFilesManagementWorkflow {
|
||||
|
||||
protected function didConstruct() {
|
||||
$arguments = $this->newIteratorArguments();
|
||||
|
||||
$arguments[] = array(
|
||||
'name' => 'engine',
|
||||
'param' => 'storage-engine',
|
||||
'help' => pht('Migrate to the named storage engine.'),
|
||||
);
|
||||
|
||||
$arguments[] = array(
|
||||
'name' => 'dry-run',
|
||||
'help' => pht('Show what would be migrated.'),
|
||||
);
|
||||
|
||||
$arguments[] = array(
|
||||
'name' => 'min-size',
|
||||
'param' => 'bytes',
|
||||
'help' => pht(
|
||||
'Do not migrate data for files which are smaller than a given '.
|
||||
'filesize.'),
|
||||
);
|
||||
|
||||
$arguments[] = array(
|
||||
'name' => 'max-size',
|
||||
'param' => 'bytes',
|
||||
'help' => pht(
|
||||
'Do not migrate data for files which are larger than a given '.
|
||||
'filesize.'),
|
||||
);
|
||||
|
||||
$arguments[] = array(
|
||||
'name' => 'copy',
|
||||
'help' => pht(
|
||||
'Copy file data instead of moving it: after migrating, do not '.
|
||||
'remove the old data even if it is no longer referenced.'),
|
||||
);
|
||||
|
||||
$arguments[] = array(
|
||||
'name' => 'local-disk-source',
|
||||
'param' => 'path',
|
||||
'help' => pht(
|
||||
'When migrating from a local disk source, use the specified '.
|
||||
'path as the root directory.'),
|
||||
);
|
||||
|
||||
$this
|
||||
->setName('migrate')
|
||||
->setSynopsis(pht('Migrate files between storage engines.'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'engine',
|
||||
'param' => 'storage_engine',
|
||||
'help' => pht('Migrate to the named storage engine.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'dry-run',
|
||||
'help' => pht('Show what would be migrated.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'min-size',
|
||||
'param' => 'bytes',
|
||||
'help' => pht(
|
||||
'Do not migrate data for files which are smaller than a given '.
|
||||
'filesize.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'max-size',
|
||||
'param' => 'bytes',
|
||||
'help' => pht(
|
||||
'Do not migrate data for files which are larger than a given '.
|
||||
'filesize.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'all',
|
||||
'help' => pht('Migrate all files.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'copy',
|
||||
'help' => pht(
|
||||
'Copy file data instead of moving it: after migrating, do not '.
|
||||
'remove the old data even if it is no longer referenced.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'names',
|
||||
'wildcard' => true,
|
||||
),
|
||||
array(
|
||||
'name' => 'from-engine',
|
||||
'param' => 'engine',
|
||||
'help' => pht('Migrate files from the named storage engine.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'local-disk-source',
|
||||
'param' => 'path',
|
||||
'help' => pht(
|
||||
'When migrating from a local disk source, use the specified '.
|
||||
'path as the root directory.'),
|
||||
),
|
||||
));
|
||||
->setArguments($arguments);
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
@@ -97,14 +90,6 @@ final class PhabricatorFilesManagementMigrateWorkflow
|
||||
$target_engine = PhabricatorFile::buildEngine($target_key);
|
||||
|
||||
$iterator = $this->buildIterator($args);
|
||||
if (!$iterator) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Either specify a list of files to migrate, or use `%s` '.
|
||||
'to migrate all files.',
|
||||
'--all'));
|
||||
}
|
||||
|
||||
$is_dry_run = $args->getArg('dry-run');
|
||||
|
||||
$min_size = (int)$args->getArg('min-size');
|
||||
|
@@ -4,45 +4,33 @@ final class PhabricatorFilesManagementRebuildWorkflow
|
||||
extends PhabricatorFilesManagementWorkflow {
|
||||
|
||||
protected function didConstruct() {
|
||||
$arguments = $this->newIteratorArguments();
|
||||
|
||||
$arguments[] = array(
|
||||
'name' => 'dry-run',
|
||||
'help' => pht('Show what would be updated.'),
|
||||
);
|
||||
|
||||
$arguments[] = array(
|
||||
'name' => 'rebuild-mime',
|
||||
'help' => pht('Rebuild MIME information.'),
|
||||
);
|
||||
|
||||
$arguments[] = array(
|
||||
'name' => 'rebuild-dimensions',
|
||||
'help' => pht('Rebuild image dimension information.'),
|
||||
);
|
||||
|
||||
$this
|
||||
->setName('rebuild')
|
||||
->setSynopsis(pht('Rebuild metadata of old files.'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'all',
|
||||
'help' => pht('Update all files.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'dry-run',
|
||||
'help' => pht('Show what would be updated.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'rebuild-mime',
|
||||
'help' => pht('Rebuild MIME information.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'rebuild-dimensions',
|
||||
'help' => pht('Rebuild image dimension information.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'names',
|
||||
'wildcard' => true,
|
||||
),
|
||||
));
|
||||
->setArguments($arguments);
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
|
||||
$iterator = $this->buildIterator($args);
|
||||
if (!$iterator) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Either specify a list of files to update, or use `%s` '.
|
||||
'to update all files.',
|
||||
'--all'));
|
||||
}
|
||||
|
||||
$update = array(
|
||||
'mime' => $args->getArg('rebuild-mime'),
|
||||
|
@@ -3,11 +3,30 @@
|
||||
abstract class PhabricatorFilesManagementWorkflow
|
||||
extends PhabricatorManagementWorkflow {
|
||||
|
||||
protected function newIteratorArguments() {
|
||||
return array(
|
||||
array(
|
||||
'name' => 'all',
|
||||
'help' => pht('Operate on all files.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'names',
|
||||
'wildcard' => true,
|
||||
),
|
||||
array(
|
||||
'name' => 'from-engine',
|
||||
'param' => 'storage-engine',
|
||||
'help' => pht('Operate on files stored in a specified engine.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
protected function buildIterator(PhutilArgumentParser $args) {
|
||||
$viewer = $this->getViewer();
|
||||
$names = $args->getArg('names');
|
||||
|
||||
$is_all = $args->getArg('all');
|
||||
|
||||
$names = $args->getArg('names');
|
||||
$from_engine = $args->getArg('from-engine');
|
||||
|
||||
$any_constraint = ($from_engine || $names);
|
||||
@@ -15,15 +34,16 @@ abstract class PhabricatorFilesManagementWorkflow
|
||||
if (!$is_all && !$any_constraint) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Use "--all" to migrate all files, or choose files to migrate '.
|
||||
'with "--names" or "--from-engine".'));
|
||||
'Specify which files to operate on, or use "--all" to operate on '.
|
||||
'all files.'));
|
||||
}
|
||||
|
||||
if ($is_all && $any_constraint) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'You can not migrate all files with "--all" and also migrate only '.
|
||||
'a subset of files with "--from-engine" or "--names".'));
|
||||
'You can not operate on all files with "--all" and also operate '.
|
||||
'on a subset of files by naming them explicitly or using '.
|
||||
'constraint flags like "--from-engine".'));
|
||||
}
|
||||
|
||||
// If we're migrating specific named files, convert the names into IDs
|
||||
|
@@ -6,6 +6,7 @@ final class PhabricatorFlagQuery
|
||||
const GROUP_COLOR = 'color';
|
||||
const GROUP_NONE = 'none';
|
||||
|
||||
private $ids;
|
||||
private $ownerPHIDs;
|
||||
private $types;
|
||||
private $objectPHIDs;
|
||||
@@ -15,6 +16,11 @@ final class PhabricatorFlagQuery
|
||||
private $needHandles;
|
||||
private $needObjects;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withOwnerPHIDs(array $owner_phids) {
|
||||
$this->ownerPHIDs = $owner_phids;
|
||||
return $this;
|
||||
@@ -126,6 +132,13 @@ final class PhabricatorFlagQuery
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn) {
|
||||
$where = array();
|
||||
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'flag.id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->ownerPHIDs) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
|
@@ -24,26 +24,27 @@ final class HarbormasterBuildableActionController
|
||||
|
||||
$issuable = array();
|
||||
|
||||
foreach ($buildable->getBuilds() as $build) {
|
||||
$builds = $buildable->getBuilds();
|
||||
foreach ($builds as $key => $build) {
|
||||
switch ($action) {
|
||||
case HarbormasterBuildCommand::COMMAND_RESTART:
|
||||
if ($build->canRestartBuild()) {
|
||||
$issuable[] = $build;
|
||||
$issuable[$key] = $build;
|
||||
}
|
||||
break;
|
||||
case HarbormasterBuildCommand::COMMAND_PAUSE:
|
||||
if ($build->canPauseBuild()) {
|
||||
$issuable[] = $build;
|
||||
$issuable[$key] = $build;
|
||||
}
|
||||
break;
|
||||
case HarbormasterBuildCommand::COMMAND_RESUME:
|
||||
if ($build->canResumeBuild()) {
|
||||
$issuable[] = $build;
|
||||
$issuable[$key] = $build;
|
||||
}
|
||||
break;
|
||||
case HarbormasterBuildCommand::COMMAND_ABORT:
|
||||
if ($build->canAbortBuild()) {
|
||||
$issuable[] = $build;
|
||||
$issuable[$key] = $build;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -59,6 +60,14 @@ final class HarbormasterBuildableActionController
|
||||
}
|
||||
}
|
||||
|
||||
$building = false;
|
||||
foreach ($issuable as $key => $build) {
|
||||
if ($build->isBuilding()) {
|
||||
$building = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$return_uri = '/'.$buildable->getMonogram();
|
||||
if ($request->isDialogFormPost() && $issuable) {
|
||||
$editor = id(new HarbormasterBuildableTransactionEditor())
|
||||
@@ -89,34 +98,137 @@ final class HarbormasterBuildableActionController
|
||||
return id(new AphrontRedirectResponse())->setURI($return_uri);
|
||||
}
|
||||
|
||||
$width = AphrontDialogView::WIDTH_DEFAULT;
|
||||
|
||||
switch ($action) {
|
||||
case HarbormasterBuildCommand::COMMAND_RESTART:
|
||||
// See T13348. The "Restart Builds" action may restart only a subset
|
||||
// of builds, so show the user a preview of which builds will actually
|
||||
// restart.
|
||||
|
||||
$body = array();
|
||||
|
||||
if ($issuable) {
|
||||
$title = pht('Really restart builds?');
|
||||
|
||||
if ($restricted) {
|
||||
$body = pht(
|
||||
'You only have permission to restart some builds. Progress '.
|
||||
'on builds you have permission to restart will be discarded '.
|
||||
'and they will restart. Side effects of these builds will '.
|
||||
'occur again. Really restart all builds?');
|
||||
} else {
|
||||
$body = pht(
|
||||
'Progress on all builds will be discarded, and all builds will '.
|
||||
'restart. Side effects of the builds will occur again. Really '.
|
||||
'restart all builds?');
|
||||
}
|
||||
|
||||
$title = pht('Restart Builds');
|
||||
$submit = pht('Restart Builds');
|
||||
} else {
|
||||
$title = pht('Unable to Restart Builds');
|
||||
}
|
||||
|
||||
if ($builds) {
|
||||
$width = AphrontDialogView::WIDTH_FORM;
|
||||
|
||||
$body[] = pht('Builds for this buildable:');
|
||||
|
||||
$rows = array();
|
||||
foreach ($builds as $key => $build) {
|
||||
if (isset($issuable[$key])) {
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIcon('fa-repeat green');
|
||||
$build_note = pht('Will Restart');
|
||||
} else {
|
||||
$icon = null;
|
||||
|
||||
try {
|
||||
$build->assertCanRestartBuild();
|
||||
} catch (HarbormasterRestartException $ex) {
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIcon('fa-times red');
|
||||
$build_note = pht(
|
||||
'%s: %s',
|
||||
phutil_tag('strong', array(), pht('Not Restartable')),
|
||||
$ex->getTitle());
|
||||
}
|
||||
|
||||
if (!$icon) {
|
||||
try {
|
||||
$build->assertCanIssueCommand($viewer, $action);
|
||||
} catch (PhabricatorPolicyException $ex) {
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIcon('fa-lock red');
|
||||
$build_note = pht(
|
||||
'%s: %s',
|
||||
phutil_tag('strong', array(), pht('Not Restartable')),
|
||||
pht('You do not have permission to restart this build.'));
|
||||
}
|
||||
}
|
||||
|
||||
if (!$icon) {
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIcon('fa-times red');
|
||||
$build_note = pht('Will Not Restart');
|
||||
}
|
||||
}
|
||||
|
||||
$build_name = phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $build->getURI(),
|
||||
'target' => '_blank',
|
||||
),
|
||||
pht('%s %s', $build->getObjectName(), $build->getName()));
|
||||
|
||||
$rows[] = array(
|
||||
$icon,
|
||||
$build_name,
|
||||
$build_note,
|
||||
);
|
||||
}
|
||||
|
||||
$table = id(new AphrontTableView($rows))
|
||||
->setHeaders(
|
||||
array(
|
||||
null,
|
||||
pht('Build'),
|
||||
pht('Action'),
|
||||
))
|
||||
->setColumnClasses(
|
||||
array(
|
||||
null,
|
||||
'pri',
|
||||
'wide',
|
||||
));
|
||||
|
||||
$table = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'mlt mlb',
|
||||
),
|
||||
$table);
|
||||
|
||||
$body[] = $table;
|
||||
}
|
||||
|
||||
if ($issuable) {
|
||||
$warnings = array();
|
||||
|
||||
if ($restricted) {
|
||||
$body = pht('You do not have permission to restart any builds.');
|
||||
$warnings[] = pht(
|
||||
'You only have permission to restart some builds.');
|
||||
}
|
||||
|
||||
if ($building) {
|
||||
$warnings[] = pht(
|
||||
'Progress on running builds will be discarded.');
|
||||
}
|
||||
|
||||
$warnings[] = pht(
|
||||
'When a build is restarted, side effects associated with '.
|
||||
'the build may occur again.');
|
||||
|
||||
$body[] = id(new PHUIInfoView())
|
||||
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
|
||||
->setErrors($warnings);
|
||||
|
||||
$body[] = pht('Really restart builds?');
|
||||
} else {
|
||||
if ($restricted) {
|
||||
$body[] = pht('You do not have permission to restart any builds.');
|
||||
} else {
|
||||
$body = pht('No builds can be restarted.');
|
||||
$body[] = pht('No builds can be restarted.');
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case HarbormasterBuildCommand::COMMAND_PAUSE:
|
||||
if ($issuable) {
|
||||
@@ -193,6 +305,7 @@ final class HarbormasterBuildableActionController
|
||||
|
||||
$dialog = id(new AphrontDialogView())
|
||||
->setUser($viewer)
|
||||
->setWidth($width)
|
||||
->setTitle($title)
|
||||
->appendChild($body)
|
||||
->addCancelButton($return_uri);
|
||||
|
@@ -128,7 +128,7 @@ final class HarbormasterBuildableViewController
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setIcon('fa-repeat')
|
||||
->setName(pht('Restart All Builds'))
|
||||
->setName(pht('Restart Builds'))
|
||||
->setHref($this->getApplicationURI($restart_uri))
|
||||
->setWorkflow(true)
|
||||
->setDisabled(!$can_restart || !$can_edit));
|
||||
@@ -136,7 +136,7 @@ final class HarbormasterBuildableViewController
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setIcon('fa-pause')
|
||||
->setName(pht('Pause All Builds'))
|
||||
->setName(pht('Pause Builds'))
|
||||
->setHref($this->getApplicationURI($pause_uri))
|
||||
->setWorkflow(true)
|
||||
->setDisabled(!$can_pause || !$can_edit));
|
||||
@@ -144,7 +144,7 @@ final class HarbormasterBuildableViewController
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setIcon('fa-play')
|
||||
->setName(pht('Resume All Builds'))
|
||||
->setName(pht('Resume Builds'))
|
||||
->setHref($this->getApplicationURI($resume_uri))
|
||||
->setWorkflow(true)
|
||||
->setDisabled(!$can_resume || !$can_edit));
|
||||
@@ -152,7 +152,7 @@ final class HarbormasterBuildableViewController
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setIcon('fa-exclamation-triangle')
|
||||
->setName(pht('Abort All Builds'))
|
||||
->setName(pht('Abort Builds'))
|
||||
->setHref($this->getApplicationURI($abort_uri))
|
||||
->setWorkflow(true)
|
||||
->setDisabled(!$can_abort || !$can_edit));
|
||||
|
@@ -25,8 +25,7 @@ final class HarbormasterBuildGraph extends AbstractDirectedGraph {
|
||||
$graph = id(new HarbormasterBuildGraph($steps_by_phid))
|
||||
->addNodes($step_phids);
|
||||
|
||||
$raw_results =
|
||||
$graph->getBestEffortTopographicallySortedNodes();
|
||||
$raw_results = $graph->getNodesInRoughTopologicalOrder();
|
||||
|
||||
$results = array();
|
||||
foreach ($raw_results as $node) {
|
||||
|
@@ -40,17 +40,10 @@ final class HeraldRuleEditTransaction
|
||||
public function newChangeDetailView() {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
$json = new PhutilJSON();
|
||||
$old_json = $json->encodeFormatted($old);
|
||||
$new_json = $json->encodeFormatted($new);
|
||||
|
||||
return id(new PhabricatorApplicationTransactionTextDiffDetailView())
|
||||
return id(new PhabricatorApplicationTransactionJSONDiffDetailView())
|
||||
->setViewer($viewer)
|
||||
->setOldText($old_json)
|
||||
->setNewText($new_json);
|
||||
->setOld($this->getOldValue())
|
||||
->setNew($this->getNewValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -451,15 +451,20 @@ You can choose the default priority for newly created tasks with
|
||||
EOTEXT
|
||||
));
|
||||
|
||||
$fields_description = $this->deformat(pht(<<<EOTEXT
|
||||
List of custom fields for Maniphest tasks.
|
||||
|
||||
For details on adding custom fields to Maniphest, see [[ %s | %s ]] in the
|
||||
documentation.
|
||||
EOTEXT
|
||||
,
|
||||
PhabricatorEnv::getDoclink('Configuring Custom Fields'),
|
||||
pht('Configuring Custom Fields')));
|
||||
|
||||
return array(
|
||||
$this->newOption('maniphest.custom-field-definitions', 'wild', array())
|
||||
->setSummary(pht('Custom Maniphest fields.'))
|
||||
->setDescription(
|
||||
pht(
|
||||
'Array of custom fields for Maniphest tasks. For details on '.
|
||||
'adding custom fields to Maniphest, see "Configuring Custom '.
|
||||
'Fields" in the documentation.'))
|
||||
->setDescription($fields_description)
|
||||
->addExample($fields_json, pht('Valid setting')),
|
||||
$this->newOption('maniphest.fields', $custom_field_type, $default_fields)
|
||||
->setCustomData(id(new ManiphestTask())->getCustomFieldBaseClass())
|
||||
|
@@ -264,6 +264,7 @@ EODOCS
|
||||
|
||||
$parent_type = ManiphestTaskDependedOnByTaskEdgeType::EDGECONST;
|
||||
$subtask_type = ManiphestTaskDependsOnTaskEdgeType::EDGECONST;
|
||||
$commit_type = ManiphestTaskHasCommitEdgeType::EDGECONST;
|
||||
|
||||
$src_phid = $object->getPHID();
|
||||
if ($src_phid) {
|
||||
@@ -273,6 +274,7 @@ EODOCS
|
||||
array(
|
||||
$parent_type,
|
||||
$subtask_type,
|
||||
$commit_type,
|
||||
));
|
||||
$edge_query->execute();
|
||||
|
||||
@@ -283,9 +285,14 @@ EODOCS
|
||||
$subtask_phids = $edge_query->getDestinationPHIDs(
|
||||
array($src_phid),
|
||||
array($subtask_type));
|
||||
|
||||
$commit_phids = $edge_query->getDestinationPHIDs(
|
||||
array($src_phid),
|
||||
array($commit_type));
|
||||
} else {
|
||||
$parent_phids = array();
|
||||
$subtask_phids = array();
|
||||
$commit_phids = array();
|
||||
}
|
||||
|
||||
$fields[] = id(new PhabricatorHandlesEditField())
|
||||
@@ -310,7 +317,19 @@ EODOCS
|
||||
->setIsFormField(false)
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
|
||||
->setMetadataValue('edge:type', $subtask_type)
|
||||
->setValue($parent_phids);
|
||||
->setValue($subtask_phids);
|
||||
|
||||
$fields[] = id(new PhabricatorHandlesEditField())
|
||||
->setKey('commits')
|
||||
->setLabel(pht('Commits'))
|
||||
->setDescription(pht('Related commits.'))
|
||||
->setConduitDescription(pht('Change the related commits for this task.'))
|
||||
->setConduitTypeDescription(pht('List of related commit PHIDs.'))
|
||||
->setUseEdgeTransactions(true)
|
||||
->setIsFormField(false)
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
|
||||
->setMetadataValue('edge:type', $commit_type)
|
||||
->setValue($commit_phids);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
@@ -437,7 +456,7 @@ EODOCS
|
||||
$engine = id(new PhabricatorBoardResponseEngine())
|
||||
->setViewer($viewer)
|
||||
->setBoardPHID($board_phid)
|
||||
->setObjectPHID($object_phid)
|
||||
->setUpdatePHIDs(array($object_phid))
|
||||
->setVisiblePHIDs($visible_phids);
|
||||
|
||||
if ($ordering) {
|
||||
|
@@ -3,6 +3,7 @@
|
||||
final class ManiphestTransactionEditor
|
||||
extends PhabricatorApplicationTransactionEditor {
|
||||
|
||||
private $oldProjectPHIDs;
|
||||
private $moreValidationErrors = array();
|
||||
|
||||
public function getEditorApplicationClass() {
|
||||
@@ -378,6 +379,11 @@ final class ManiphestTransactionEditor
|
||||
}
|
||||
}
|
||||
|
||||
$send_notifications = PhabricatorNotificationClient::isEnabled();
|
||||
if ($send_notifications) {
|
||||
$this->oldProjectPHIDs = $this->loadProjectPHIDs($object);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
@@ -859,4 +865,71 @@ final class ManiphestTransactionEditor
|
||||
return array_values($phid_list);
|
||||
}
|
||||
|
||||
protected function didApplyTransactions($object, array $xactions) {
|
||||
$send_notifications = PhabricatorNotificationClient::isEnabled();
|
||||
if ($send_notifications) {
|
||||
$old_phids = $this->oldProjectPHIDs;
|
||||
$new_phids = $this->loadProjectPHIDs($object);
|
||||
|
||||
// We want to emit update notifications for all old and new tagged
|
||||
// projects, and all parents of those projects. For example, if an
|
||||
// edit removes project "A > B" from a task, the "A" workboard should
|
||||
// receive an update event.
|
||||
|
||||
$project_phids = array_fuse($old_phids) + array_fuse($new_phids);
|
||||
$project_phids = array_keys($project_phids);
|
||||
|
||||
if ($project_phids) {
|
||||
$projects = id(new PhabricatorProjectQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withPHIDs($project_phids)
|
||||
->execute();
|
||||
|
||||
$notify_projects = array();
|
||||
foreach ($projects as $project) {
|
||||
$notify_projects[$project->getPHID()] = $project;
|
||||
foreach ($project->getAncestorProjects() as $ancestor) {
|
||||
$notify_projects[$ancestor->getPHID()] = $ancestor;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($notify_projects as $key => $project) {
|
||||
if (!$project->getHasWorkboard()) {
|
||||
unset($notify_projects[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$notify_phids = array_keys($notify_projects);
|
||||
|
||||
if ($notify_phids) {
|
||||
$data = array(
|
||||
'type' => 'workboards',
|
||||
'subscribers' => $notify_phids,
|
||||
);
|
||||
|
||||
PhabricatorNotificationClient::tryToPostMessage($data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $xactions;
|
||||
}
|
||||
|
||||
private function loadProjectPHIDs(ManiphestTask $task) {
|
||||
if (!$task->getPHID()) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$edge_query = id(new PhabricatorEdgeQuery())
|
||||
->withSourcePHIDs(array($task->getPHID()))
|
||||
->withEdgeTypes(
|
||||
array(
|
||||
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
|
||||
));
|
||||
|
||||
$edge_query->execute();
|
||||
|
||||
return $edge_query->getDestinationPHIDs();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -2,9 +2,7 @@
|
||||
|
||||
final class PhabricatorMetaMTAErrorMailAction extends PhabricatorSystemAction {
|
||||
|
||||
public function getActionConstant() {
|
||||
return 'email.error';
|
||||
}
|
||||
const TYPECONST = 'email.error';
|
||||
|
||||
public function getScoreThreshold() {
|
||||
return 6 / phutil_units('1 hour in seconds');
|
||||
|
@@ -37,4 +37,8 @@ final class PhabricatorNotificationClient extends Phobject {
|
||||
}
|
||||
}
|
||||
|
||||
public static function isEnabled() {
|
||||
return (bool)PhabricatorNotificationServerRef::getEnabledAdminServers();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -15,12 +15,11 @@ final class PhabricatorOAuthClientViewController
|
||||
}
|
||||
|
||||
$header = $this->buildHeaderView($client);
|
||||
$actions = $this->buildActionView($client);
|
||||
$properties = $this->buildPropertyListView($client);
|
||||
$properties->setActionList($actions);
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb($client->getName());
|
||||
$crumbs = $this->buildApplicationCrumbs()
|
||||
->addTextCrumb($client->getName())
|
||||
->setBorder(true);
|
||||
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$client,
|
||||
@@ -28,19 +27,27 @@ final class PhabricatorOAuthClientViewController
|
||||
$timeline->setShouldTerminate(true);
|
||||
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->setHeaderText(pht('Details'))
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->addPropertyList($properties);
|
||||
|
||||
$title = pht('OAuth Application: %s', $client->getName());
|
||||
|
||||
return $this->newPage()
|
||||
->setCrumbs($crumbs)
|
||||
->setTitle($title)
|
||||
->appendChild(
|
||||
$curtain = $this->buildCurtain($client);
|
||||
|
||||
$columns = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setCurtain($curtain)
|
||||
->setMainColumn(
|
||||
array(
|
||||
$box,
|
||||
$timeline,
|
||||
));
|
||||
|
||||
return $this->newPage()
|
||||
->setCrumbs($crumbs)
|
||||
->setTitle($title)
|
||||
->appendChild($columns);
|
||||
}
|
||||
|
||||
private function buildHeaderView(PhabricatorOAuthServerClient $client) {
|
||||
@@ -60,8 +67,9 @@ final class PhabricatorOAuthClientViewController
|
||||
return $header;
|
||||
}
|
||||
|
||||
private function buildActionView(PhabricatorOAuthServerClient $client) {
|
||||
private function buildCurtain(PhabricatorOAuthServerClient $client) {
|
||||
$viewer = $this->getViewer();
|
||||
$actions = array();
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
@@ -70,24 +78,19 @@ final class PhabricatorOAuthClientViewController
|
||||
|
||||
$id = $client->getID();
|
||||
|
||||
$view = id(new PhabricatorActionListView())
|
||||
->setUser($viewer);
|
||||
$actions[] = id(new PhabricatorActionView())
|
||||
->setName(pht('Edit Application'))
|
||||
->setIcon('fa-pencil')
|
||||
->setWorkflow(!$can_edit)
|
||||
->setDisabled(!$can_edit)
|
||||
->setHref($client->getEditURI());
|
||||
|
||||
$view->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Edit Application'))
|
||||
->setIcon('fa-pencil')
|
||||
->setWorkflow(!$can_edit)
|
||||
->setDisabled(!$can_edit)
|
||||
->setHref($client->getEditURI()));
|
||||
|
||||
$view->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Show Application Secret'))
|
||||
->setIcon('fa-eye')
|
||||
->setHref($this->getApplicationURI("client/secret/{$id}/"))
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(true));
|
||||
$actions[] = id(new PhabricatorActionView())
|
||||
->setName(pht('Show Application Secret'))
|
||||
->setIcon('fa-eye')
|
||||
->setHref($this->getApplicationURI("client/secret/{$id}/"))
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(true);
|
||||
|
||||
$is_disabled = $client->getIsDisabled();
|
||||
if ($is_disabled) {
|
||||
@@ -100,22 +103,26 @@ final class PhabricatorOAuthClientViewController
|
||||
|
||||
$disable_uri = $this->getApplicationURI("client/disable/{$id}/");
|
||||
|
||||
$view->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName($disable_text)
|
||||
->setIcon($disable_icon)
|
||||
->setWorkflow(true)
|
||||
->setDisabled(!$can_edit)
|
||||
->setHref($disable_uri));
|
||||
$actions[] = id(new PhabricatorActionView())
|
||||
->setName($disable_text)
|
||||
->setIcon($disable_icon)
|
||||
->setWorkflow(true)
|
||||
->setDisabled(!$can_edit)
|
||||
->setHref($disable_uri);
|
||||
|
||||
$view->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Generate Test Token'))
|
||||
->setIcon('fa-plus')
|
||||
->setWorkflow(true)
|
||||
->setHref($this->getApplicationURI("client/test/{$id}/")));
|
||||
$actions[] = id(new PhabricatorActionView())
|
||||
->setName(pht('Generate Test Token'))
|
||||
->setIcon('fa-plus')
|
||||
->setWorkflow(true)
|
||||
->setHref($this->getApplicationURI("client/test/{$id}/"));
|
||||
|
||||
return $view;
|
||||
$curtain = $this->newCurtainView($client);
|
||||
|
||||
foreach ($actions as $action) {
|
||||
$curtain->addAction($action);
|
||||
}
|
||||
|
||||
return $curtain;
|
||||
}
|
||||
|
||||
private function buildPropertyListView(PhabricatorOAuthServerClient $client) {
|
||||
@@ -132,10 +139,6 @@ final class PhabricatorOAuthClientViewController
|
||||
pht('Redirect URI'),
|
||||
$client->getRedirectURI());
|
||||
|
||||
$view->addProperty(
|
||||
pht('Created'),
|
||||
phabricator_datetime($client->getDateCreated(), $viewer));
|
||||
|
||||
return $view;
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user