mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-26 14:38:19 +01:00
(stable) Promote 2019 Week 10
This commit is contained in:
commit
2387a99d71
104 changed files with 2717 additions and 896 deletions
|
@ -9,9 +9,9 @@ return array(
|
||||||
'names' => array(
|
'names' => array(
|
||||||
'conpherence.pkg.css' => '3c8a0668',
|
'conpherence.pkg.css' => '3c8a0668',
|
||||||
'conpherence.pkg.js' => '020aebcf',
|
'conpherence.pkg.js' => '020aebcf',
|
||||||
'core.pkg.css' => 'e3c1a8f2',
|
'core.pkg.css' => '34ce1741',
|
||||||
'core.pkg.js' => '2cda17a4',
|
'core.pkg.js' => '2cda17a4',
|
||||||
'differential.pkg.css' => 'ab23bd75',
|
'differential.pkg.css' => '1755a478',
|
||||||
'differential.pkg.js' => '67e02996',
|
'differential.pkg.js' => '67e02996',
|
||||||
'diffusion.pkg.css' => '42c75c37',
|
'diffusion.pkg.css' => '42c75c37',
|
||||||
'diffusion.pkg.js' => '91192d85',
|
'diffusion.pkg.js' => '91192d85',
|
||||||
|
@ -61,7 +61,7 @@ return array(
|
||||||
'rsrc/css/application/dashboard/dashboard.css' => '4267d6c6',
|
'rsrc/css/application/dashboard/dashboard.css' => '4267d6c6',
|
||||||
'rsrc/css/application/diff/inline-comment-summary.css' => '81eb368d',
|
'rsrc/css/application/diff/inline-comment-summary.css' => '81eb368d',
|
||||||
'rsrc/css/application/differential/add-comment.css' => '7e5900d9',
|
'rsrc/css/application/differential/add-comment.css' => '7e5900d9',
|
||||||
'rsrc/css/application/differential/changeset-view.css' => 'd92bed0d',
|
'rsrc/css/application/differential/changeset-view.css' => '4193eeff',
|
||||||
'rsrc/css/application/differential/core.css' => '7300a73e',
|
'rsrc/css/application/differential/core.css' => '7300a73e',
|
||||||
'rsrc/css/application/differential/phui-inline-comment.css' => '48acce5b',
|
'rsrc/css/application/differential/phui-inline-comment.css' => '48acce5b',
|
||||||
'rsrc/css/application/differential/revision-comment.css' => '7dbc8d1d',
|
'rsrc/css/application/differential/revision-comment.css' => '7dbc8d1d',
|
||||||
|
@ -127,7 +127,7 @@ return array(
|
||||||
'rsrc/css/phui/calendar/phui-calendar-list.css' => 'ccd7e4e2',
|
'rsrc/css/phui/calendar/phui-calendar-list.css' => 'ccd7e4e2',
|
||||||
'rsrc/css/phui/calendar/phui-calendar-month.css' => 'cb758c42',
|
'rsrc/css/phui/calendar/phui-calendar-month.css' => 'cb758c42',
|
||||||
'rsrc/css/phui/calendar/phui-calendar.css' => 'f11073aa',
|
'rsrc/css/phui/calendar/phui-calendar.css' => 'f11073aa',
|
||||||
'rsrc/css/phui/object-item/phui-oi-big-ui.css' => '9e037c7a',
|
'rsrc/css/phui/object-item/phui-oi-big-ui.css' => '534f1757',
|
||||||
'rsrc/css/phui/object-item/phui-oi-color.css' => 'b517bfa0',
|
'rsrc/css/phui/object-item/phui-oi-color.css' => 'b517bfa0',
|
||||||
'rsrc/css/phui/object-item/phui-oi-drag-ui.css' => 'da15d3dc',
|
'rsrc/css/phui/object-item/phui-oi-drag-ui.css' => 'da15d3dc',
|
||||||
'rsrc/css/phui/object-item/phui-oi-flush-ui.css' => '490e2e2e',
|
'rsrc/css/phui/object-item/phui-oi-flush-ui.css' => '490e2e2e',
|
||||||
|
@ -540,7 +540,7 @@ return array(
|
||||||
'conpherence-thread-manager' => 'aec8e38c',
|
'conpherence-thread-manager' => 'aec8e38c',
|
||||||
'conpherence-transaction-css' => '3a3f5e7e',
|
'conpherence-transaction-css' => '3a3f5e7e',
|
||||||
'd3' => 'd67475f5',
|
'd3' => 'd67475f5',
|
||||||
'differential-changeset-view-css' => 'd92bed0d',
|
'differential-changeset-view-css' => '4193eeff',
|
||||||
'differential-core-view-css' => '7300a73e',
|
'differential-core-view-css' => '7300a73e',
|
||||||
'differential-revision-add-comment-css' => '7e5900d9',
|
'differential-revision-add-comment-css' => '7e5900d9',
|
||||||
'differential-revision-comment-css' => '7dbc8d1d',
|
'differential-revision-comment-css' => '7dbc8d1d',
|
||||||
|
@ -834,7 +834,7 @@ return array(
|
||||||
'phui-lightbox-css' => '4ebf22da',
|
'phui-lightbox-css' => '4ebf22da',
|
||||||
'phui-list-view-css' => '470b1adb',
|
'phui-list-view-css' => '470b1adb',
|
||||||
'phui-object-box-css' => 'f434b6be',
|
'phui-object-box-css' => 'f434b6be',
|
||||||
'phui-oi-big-ui-css' => '9e037c7a',
|
'phui-oi-big-ui-css' => '534f1757',
|
||||||
'phui-oi-color-css' => 'b517bfa0',
|
'phui-oi-color-css' => 'b517bfa0',
|
||||||
'phui-oi-drag-ui-css' => 'da15d3dc',
|
'phui-oi-drag-ui-css' => 'da15d3dc',
|
||||||
'phui-oi-flush-ui-css' => '490e2e2e',
|
'phui-oi-flush-ui-css' => '490e2e2e',
|
||||||
|
@ -1220,6 +1220,9 @@ return array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-uri',
|
'javelin-uri',
|
||||||
),
|
),
|
||||||
|
'4193eeff' => array(
|
||||||
|
'phui-inline-comment-view-css',
|
||||||
|
),
|
||||||
'4234f572' => array(
|
'4234f572' => array(
|
||||||
'syntax-default-css',
|
'syntax-default-css',
|
||||||
),
|
),
|
||||||
|
@ -1345,6 +1348,9 @@ return array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'javelin-fx',
|
'javelin-fx',
|
||||||
),
|
),
|
||||||
|
'534f1757' => array(
|
||||||
|
'phui-oi-list-view-css',
|
||||||
|
),
|
||||||
'541f81c3' => array(
|
'541f81c3' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
),
|
),
|
||||||
|
@ -1721,9 +1727,6 @@ return array(
|
||||||
'javelin-uri',
|
'javelin-uri',
|
||||||
'phabricator-textareautils',
|
'phabricator-textareautils',
|
||||||
),
|
),
|
||||||
'9e037c7a' => array(
|
|
||||||
'phui-oi-list-view-css',
|
|
||||||
),
|
|
||||||
'9f081f05' => array(
|
'9f081f05' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
@ -1997,9 +2000,6 @@ return array(
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
'phabricator-shaped-request',
|
'phabricator-shaped-request',
|
||||||
),
|
),
|
||||||
'd92bed0d' => array(
|
|
||||||
'phui-inline-comment-view-css',
|
|
||||||
),
|
|
||||||
'da15d3dc' => array(
|
'da15d3dc' => array(
|
||||||
'phui-oi-list-view-css',
|
'phui-oi-list-view-css',
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$table = new PhabricatorOwnersPackage();
|
// This was an old reindexing migration that has been obsoleted. See T13253.
|
||||||
|
|
||||||
foreach (new LiskMigrationIterator($table) as $package) {
|
|
||||||
PhabricatorSearchWorker::queueDocumentForIndexing(
|
|
||||||
$package->getPHID(),
|
|
||||||
array(
|
|
||||||
'force' => true,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$table = new AlmanacDevice();
|
// This was an old reindexing migration that has been obsoleted. See T13253.
|
||||||
|
|
||||||
foreach (new LiskMigrationIterator($table) as $device) {
|
|
||||||
PhabricatorSearchWorker::queueDocumentForIndexing(
|
|
||||||
$device->getPHID(),
|
|
||||||
array(
|
|
||||||
'force' => true,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$table = new AlmanacService();
|
// This was an old reindexing migration that has been obsoleted. See T13253.
|
||||||
|
|
||||||
foreach (new LiskMigrationIterator($table) as $service) {
|
|
||||||
PhabricatorSearchWorker::queueDocumentForIndexing(
|
|
||||||
$service->getPHID(),
|
|
||||||
array(
|
|
||||||
'force' => true,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$table = new AlmanacNetwork();
|
// This was an old reindexing migration that has been obsoleted. See T13253.
|
||||||
|
|
||||||
foreach (new LiskMigrationIterator($table) as $network) {
|
|
||||||
PhabricatorSearchWorker::queueDocumentForIndexing(
|
|
||||||
$network->getPHID(),
|
|
||||||
array(
|
|
||||||
'force' => true,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$table = new HarbormasterBuildPlan();
|
// This was an old reindexing migration that has been obsoleted. See T13253.
|
||||||
|
|
||||||
foreach (new LiskMigrationIterator($table) as $plan) {
|
|
||||||
PhabricatorSearchWorker::queueDocumentForIndexing(
|
|
||||||
$plan->getPHID(),
|
|
||||||
array(
|
|
||||||
'force' => true,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$table = new DrydockBlueprint();
|
// This was an old reindexing migration that has been obsoleted. See T13253.
|
||||||
|
|
||||||
foreach (new LiskMigrationIterator($table) as $blueprint) {
|
|
||||||
PhabricatorSearchWorker::queueDocumentForIndexing(
|
|
||||||
$blueprint->getPHID(),
|
|
||||||
array(
|
|
||||||
'force' => true,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$table = new NuanceSource();
|
// This was an old reindexing migration that has been obsoleted. See T13253.
|
||||||
|
|
||||||
foreach (new LiskMigrationIterator($table) as $source) {
|
|
||||||
PhabricatorSearchWorker::queueDocumentForIndexing(
|
|
||||||
$source->getPHID(),
|
|
||||||
array(
|
|
||||||
'force' => true,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$table = new PhabricatorBadgesBadge();
|
// This was an old reindexing migration that has been obsoleted. See T13253.
|
||||||
|
|
||||||
foreach (new LiskMigrationIterator($table) as $badge) {
|
|
||||||
PhabricatorSearchWorker::queueDocumentForIndexing(
|
|
||||||
$badge->getPHID(),
|
|
||||||
array(
|
|
||||||
'force' => true,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$table = new PhabricatorPhurlURL();
|
// This was an old reindexing migration that has been obsoleted. See T13253.
|
||||||
|
|
||||||
foreach (new LiskMigrationIterator($table) as $url) {
|
|
||||||
PhabricatorSearchWorker::queueDocumentForIndexing(
|
|
||||||
$url->getPHID(),
|
|
||||||
array(
|
|
||||||
'force' => true,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$table = new ConpherenceThread();
|
// This was an old reindexing migration that has been obsoleted. See T13253.
|
||||||
|
|
||||||
foreach (new LiskMigrationIterator($table) as $thread) {
|
|
||||||
PhabricatorSearchWorker::queueDocumentForIndexing(
|
|
||||||
$thread->getPHID(),
|
|
||||||
array(
|
|
||||||
'force' => true,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,21 +1,3 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$table_db = new PhabricatorDashboard();
|
// This was an old reindexing migration that has been obsoleted. See T13253.
|
||||||
|
|
||||||
foreach (new LiskMigrationIterator($table_db) as $dashboard) {
|
|
||||||
PhabricatorSearchWorker::queueDocumentForIndexing(
|
|
||||||
$dashboard->getPHID(),
|
|
||||||
array(
|
|
||||||
'force' => true,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
$table_dbp = new PhabricatorDashboardPanel();
|
|
||||||
|
|
||||||
foreach (new LiskMigrationIterator($table_dbp) as $panel) {
|
|
||||||
PhabricatorSearchWorker::queueDocumentForIndexing(
|
|
||||||
$panel->getPHID(),
|
|
||||||
array(
|
|
||||||
'force' => true,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$table = new PhabricatorProject();
|
// This was an old reindexing migration that has been obsoleted. See T13253.
|
||||||
|
|
||||||
foreach (new LiskMigrationIterator($table) as $project) {
|
|
||||||
PhabricatorSearchWorker::queueDocumentForIndexing(
|
|
||||||
$project->getPHID(),
|
|
||||||
array(
|
|
||||||
'force' => true,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$table = new PonderQuestion();
|
// This was an old reindexing migration that has been obsoleted. See T13253.
|
||||||
|
|
||||||
foreach (new LiskMigrationIterator($table) as $question) {
|
|
||||||
PhabricatorSearchWorker::queueDocumentForIndexing(
|
|
||||||
$question->getPHID(),
|
|
||||||
array(
|
|
||||||
'force' => true,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_buildplan
|
||||||
|
ADD properties LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT};
|
|
@ -0,0 +1,2 @@
|
||||||
|
UPDATE {$NAMESPACE}_harbormaster.harbormaster_buildplan
|
||||||
|
SET properties = '{}' WHERE properties = '';
|
|
@ -0,0 +1 @@
|
||||||
|
DROP TABLE {$NAMESPACE}_herald.herald_ruletransaction_comment;
|
|
@ -653,6 +653,7 @@ phutil_register_library_map(array(
|
||||||
'DifferentialRevisionUpdateTransaction' => 'applications/differential/xaction/DifferentialRevisionUpdateTransaction.php',
|
'DifferentialRevisionUpdateTransaction' => 'applications/differential/xaction/DifferentialRevisionUpdateTransaction.php',
|
||||||
'DifferentialRevisionViewController' => 'applications/differential/controller/DifferentialRevisionViewController.php',
|
'DifferentialRevisionViewController' => 'applications/differential/controller/DifferentialRevisionViewController.php',
|
||||||
'DifferentialRevisionVoidTransaction' => 'applications/differential/xaction/DifferentialRevisionVoidTransaction.php',
|
'DifferentialRevisionVoidTransaction' => 'applications/differential/xaction/DifferentialRevisionVoidTransaction.php',
|
||||||
|
'DifferentialRevisionWrongBuildsTransaction' => 'applications/differential/xaction/DifferentialRevisionWrongBuildsTransaction.php',
|
||||||
'DifferentialRevisionWrongStateTransaction' => 'applications/differential/xaction/DifferentialRevisionWrongStateTransaction.php',
|
'DifferentialRevisionWrongStateTransaction' => 'applications/differential/xaction/DifferentialRevisionWrongStateTransaction.php',
|
||||||
'DifferentialSchemaSpec' => 'applications/differential/storage/DifferentialSchemaSpec.php',
|
'DifferentialSchemaSpec' => 'applications/differential/storage/DifferentialSchemaSpec.php',
|
||||||
'DifferentialSetDiffPropertyConduitAPIMethod' => 'applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php',
|
'DifferentialSetDiffPropertyConduitAPIMethod' => 'applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php',
|
||||||
|
@ -1328,18 +1329,26 @@ phutil_register_library_map(array(
|
||||||
'HarbormasterBuildMessageQuery' => 'applications/harbormaster/query/HarbormasterBuildMessageQuery.php',
|
'HarbormasterBuildMessageQuery' => 'applications/harbormaster/query/HarbormasterBuildMessageQuery.php',
|
||||||
'HarbormasterBuildPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildPHIDType.php',
|
'HarbormasterBuildPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildPHIDType.php',
|
||||||
'HarbormasterBuildPlan' => 'applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php',
|
'HarbormasterBuildPlan' => 'applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php',
|
||||||
|
'HarbormasterBuildPlanBehavior' => 'applications/harbormaster/plan/HarbormasterBuildPlanBehavior.php',
|
||||||
|
'HarbormasterBuildPlanBehaviorOption' => 'applications/harbormaster/plan/HarbormasterBuildPlanBehaviorOption.php',
|
||||||
|
'HarbormasterBuildPlanBehaviorTransaction' => 'applications/harbormaster/xaction/plan/HarbormasterBuildPlanBehaviorTransaction.php',
|
||||||
'HarbormasterBuildPlanDatasource' => 'applications/harbormaster/typeahead/HarbormasterBuildPlanDatasource.php',
|
'HarbormasterBuildPlanDatasource' => 'applications/harbormaster/typeahead/HarbormasterBuildPlanDatasource.php',
|
||||||
'HarbormasterBuildPlanDefaultEditCapability' => 'applications/harbormaster/capability/HarbormasterBuildPlanDefaultEditCapability.php',
|
'HarbormasterBuildPlanDefaultEditCapability' => 'applications/harbormaster/capability/HarbormasterBuildPlanDefaultEditCapability.php',
|
||||||
'HarbormasterBuildPlanDefaultViewCapability' => 'applications/harbormaster/capability/HarbormasterBuildPlanDefaultViewCapability.php',
|
'HarbormasterBuildPlanDefaultViewCapability' => 'applications/harbormaster/capability/HarbormasterBuildPlanDefaultViewCapability.php',
|
||||||
|
'HarbormasterBuildPlanEditAPIMethod' => 'applications/harbormaster/conduit/HarbormasterBuildPlanEditAPIMethod.php',
|
||||||
'HarbormasterBuildPlanEditEngine' => 'applications/harbormaster/editor/HarbormasterBuildPlanEditEngine.php',
|
'HarbormasterBuildPlanEditEngine' => 'applications/harbormaster/editor/HarbormasterBuildPlanEditEngine.php',
|
||||||
'HarbormasterBuildPlanEditor' => 'applications/harbormaster/editor/HarbormasterBuildPlanEditor.php',
|
'HarbormasterBuildPlanEditor' => 'applications/harbormaster/editor/HarbormasterBuildPlanEditor.php',
|
||||||
'HarbormasterBuildPlanNameNgrams' => 'applications/harbormaster/storage/configuration/HarbormasterBuildPlanNameNgrams.php',
|
'HarbormasterBuildPlanNameNgrams' => 'applications/harbormaster/storage/configuration/HarbormasterBuildPlanNameNgrams.php',
|
||||||
|
'HarbormasterBuildPlanNameTransaction' => 'applications/harbormaster/xaction/plan/HarbormasterBuildPlanNameTransaction.php',
|
||||||
'HarbormasterBuildPlanPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildPlanPHIDType.php',
|
'HarbormasterBuildPlanPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildPlanPHIDType.php',
|
||||||
|
'HarbormasterBuildPlanPolicyCodex' => 'applications/harbormaster/codex/HarbormasterBuildPlanPolicyCodex.php',
|
||||||
'HarbormasterBuildPlanQuery' => 'applications/harbormaster/query/HarbormasterBuildPlanQuery.php',
|
'HarbormasterBuildPlanQuery' => 'applications/harbormaster/query/HarbormasterBuildPlanQuery.php',
|
||||||
'HarbormasterBuildPlanSearchAPIMethod' => 'applications/harbormaster/conduit/HarbormasterBuildPlanSearchAPIMethod.php',
|
'HarbormasterBuildPlanSearchAPIMethod' => 'applications/harbormaster/conduit/HarbormasterBuildPlanSearchAPIMethod.php',
|
||||||
'HarbormasterBuildPlanSearchEngine' => 'applications/harbormaster/query/HarbormasterBuildPlanSearchEngine.php',
|
'HarbormasterBuildPlanSearchEngine' => 'applications/harbormaster/query/HarbormasterBuildPlanSearchEngine.php',
|
||||||
|
'HarbormasterBuildPlanStatusTransaction' => 'applications/harbormaster/xaction/plan/HarbormasterBuildPlanStatusTransaction.php',
|
||||||
'HarbormasterBuildPlanTransaction' => 'applications/harbormaster/storage/configuration/HarbormasterBuildPlanTransaction.php',
|
'HarbormasterBuildPlanTransaction' => 'applications/harbormaster/storage/configuration/HarbormasterBuildPlanTransaction.php',
|
||||||
'HarbormasterBuildPlanTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildPlanTransactionQuery.php',
|
'HarbormasterBuildPlanTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildPlanTransactionQuery.php',
|
||||||
|
'HarbormasterBuildPlanTransactionType' => 'applications/harbormaster/xaction/plan/HarbormasterBuildPlanTransactionType.php',
|
||||||
'HarbormasterBuildQuery' => 'applications/harbormaster/query/HarbormasterBuildQuery.php',
|
'HarbormasterBuildQuery' => 'applications/harbormaster/query/HarbormasterBuildQuery.php',
|
||||||
'HarbormasterBuildRequest' => 'applications/harbormaster/engine/HarbormasterBuildRequest.php',
|
'HarbormasterBuildRequest' => 'applications/harbormaster/engine/HarbormasterBuildRequest.php',
|
||||||
'HarbormasterBuildSearchConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterBuildSearchConduitAPIMethod.php',
|
'HarbormasterBuildSearchConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterBuildSearchConduitAPIMethod.php',
|
||||||
|
@ -1366,6 +1375,7 @@ phutil_register_library_map(array(
|
||||||
'HarbormasterBuildTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildTransactionQuery.php',
|
'HarbormasterBuildTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildTransactionQuery.php',
|
||||||
'HarbormasterBuildUnitMessage' => 'applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php',
|
'HarbormasterBuildUnitMessage' => 'applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php',
|
||||||
'HarbormasterBuildUnitMessageQuery' => 'applications/harbormaster/query/HarbormasterBuildUnitMessageQuery.php',
|
'HarbormasterBuildUnitMessageQuery' => 'applications/harbormaster/query/HarbormasterBuildUnitMessageQuery.php',
|
||||||
|
'HarbormasterBuildView' => 'applications/harbormaster/view/HarbormasterBuildView.php',
|
||||||
'HarbormasterBuildViewController' => 'applications/harbormaster/controller/HarbormasterBuildViewController.php',
|
'HarbormasterBuildViewController' => 'applications/harbormaster/controller/HarbormasterBuildViewController.php',
|
||||||
'HarbormasterBuildWorker' => 'applications/harbormaster/worker/HarbormasterBuildWorker.php',
|
'HarbormasterBuildWorker' => 'applications/harbormaster/worker/HarbormasterBuildWorker.php',
|
||||||
'HarbormasterBuildable' => 'applications/harbormaster/storage/HarbormasterBuildable.php',
|
'HarbormasterBuildable' => 'applications/harbormaster/storage/HarbormasterBuildable.php',
|
||||||
|
@ -1419,6 +1429,7 @@ phutil_register_library_map(array(
|
||||||
'HarbormasterMessageType' => 'applications/harbormaster/engine/HarbormasterMessageType.php',
|
'HarbormasterMessageType' => 'applications/harbormaster/engine/HarbormasterMessageType.php',
|
||||||
'HarbormasterObject' => 'applications/harbormaster/storage/HarbormasterObject.php',
|
'HarbormasterObject' => 'applications/harbormaster/storage/HarbormasterObject.php',
|
||||||
'HarbormasterOtherBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterOtherBuildStepGroup.php',
|
'HarbormasterOtherBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterOtherBuildStepGroup.php',
|
||||||
|
'HarbormasterPlanBehaviorController' => 'applications/harbormaster/controller/HarbormasterPlanBehaviorController.php',
|
||||||
'HarbormasterPlanController' => 'applications/harbormaster/controller/HarbormasterPlanController.php',
|
'HarbormasterPlanController' => 'applications/harbormaster/controller/HarbormasterPlanController.php',
|
||||||
'HarbormasterPlanDisableController' => 'applications/harbormaster/controller/HarbormasterPlanDisableController.php',
|
'HarbormasterPlanDisableController' => 'applications/harbormaster/controller/HarbormasterPlanDisableController.php',
|
||||||
'HarbormasterPlanEditController' => 'applications/harbormaster/controller/HarbormasterPlanEditController.php',
|
'HarbormasterPlanEditController' => 'applications/harbormaster/controller/HarbormasterPlanEditController.php',
|
||||||
|
@ -1432,6 +1443,7 @@ phutil_register_library_map(array(
|
||||||
'HarbormasterQueryBuildsConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterQueryBuildsConduitAPIMethod.php',
|
'HarbormasterQueryBuildsConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterQueryBuildsConduitAPIMethod.php',
|
||||||
'HarbormasterQueryBuildsSearchEngineAttachment' => 'applications/harbormaster/engineextension/HarbormasterQueryBuildsSearchEngineAttachment.php',
|
'HarbormasterQueryBuildsSearchEngineAttachment' => 'applications/harbormaster/engineextension/HarbormasterQueryBuildsSearchEngineAttachment.php',
|
||||||
'HarbormasterRemarkupRule' => 'applications/harbormaster/remarkup/HarbormasterRemarkupRule.php',
|
'HarbormasterRemarkupRule' => 'applications/harbormaster/remarkup/HarbormasterRemarkupRule.php',
|
||||||
|
'HarbormasterRestartException' => 'applications/harbormaster/exception/HarbormasterRestartException.php',
|
||||||
'HarbormasterRunBuildPlansHeraldAction' => 'applications/harbormaster/herald/HarbormasterRunBuildPlansHeraldAction.php',
|
'HarbormasterRunBuildPlansHeraldAction' => 'applications/harbormaster/herald/HarbormasterRunBuildPlansHeraldAction.php',
|
||||||
'HarbormasterSchemaSpec' => 'applications/harbormaster/storage/HarbormasterSchemaSpec.php',
|
'HarbormasterSchemaSpec' => 'applications/harbormaster/storage/HarbormasterSchemaSpec.php',
|
||||||
'HarbormasterScratchTable' => 'applications/harbormaster/storage/HarbormasterScratchTable.php',
|
'HarbormasterScratchTable' => 'applications/harbormaster/storage/HarbormasterScratchTable.php',
|
||||||
|
@ -1520,14 +1532,20 @@ phutil_register_library_map(array(
|
||||||
'HeraldRemarkupFieldValue' => 'applications/herald/value/HeraldRemarkupFieldValue.php',
|
'HeraldRemarkupFieldValue' => 'applications/herald/value/HeraldRemarkupFieldValue.php',
|
||||||
'HeraldRemarkupRule' => 'applications/herald/remarkup/HeraldRemarkupRule.php',
|
'HeraldRemarkupRule' => 'applications/herald/remarkup/HeraldRemarkupRule.php',
|
||||||
'HeraldRule' => 'applications/herald/storage/HeraldRule.php',
|
'HeraldRule' => 'applications/herald/storage/HeraldRule.php',
|
||||||
|
'HeraldRuleActionAffectsObjectEdgeType' => 'applications/herald/edge/HeraldRuleActionAffectsObjectEdgeType.php',
|
||||||
'HeraldRuleAdapter' => 'applications/herald/adapter/HeraldRuleAdapter.php',
|
'HeraldRuleAdapter' => 'applications/herald/adapter/HeraldRuleAdapter.php',
|
||||||
'HeraldRuleAdapterField' => 'applications/herald/field/rule/HeraldRuleAdapterField.php',
|
'HeraldRuleAdapterField' => 'applications/herald/field/rule/HeraldRuleAdapterField.php',
|
||||||
'HeraldRuleController' => 'applications/herald/controller/HeraldRuleController.php',
|
'HeraldRuleController' => 'applications/herald/controller/HeraldRuleController.php',
|
||||||
'HeraldRuleDatasource' => 'applications/herald/typeahead/HeraldRuleDatasource.php',
|
'HeraldRuleDatasource' => 'applications/herald/typeahead/HeraldRuleDatasource.php',
|
||||||
|
'HeraldRuleDisableTransaction' => 'applications/herald/xaction/HeraldRuleDisableTransaction.php',
|
||||||
|
'HeraldRuleEditTransaction' => 'applications/herald/xaction/HeraldRuleEditTransaction.php',
|
||||||
'HeraldRuleEditor' => 'applications/herald/editor/HeraldRuleEditor.php',
|
'HeraldRuleEditor' => 'applications/herald/editor/HeraldRuleEditor.php',
|
||||||
'HeraldRuleField' => 'applications/herald/field/rule/HeraldRuleField.php',
|
'HeraldRuleField' => 'applications/herald/field/rule/HeraldRuleField.php',
|
||||||
'HeraldRuleFieldGroup' => 'applications/herald/field/rule/HeraldRuleFieldGroup.php',
|
'HeraldRuleFieldGroup' => 'applications/herald/field/rule/HeraldRuleFieldGroup.php',
|
||||||
|
'HeraldRuleIndexEngineExtension' => 'applications/herald/engineextension/HeraldRuleIndexEngineExtension.php',
|
||||||
'HeraldRuleListController' => 'applications/herald/controller/HeraldRuleListController.php',
|
'HeraldRuleListController' => 'applications/herald/controller/HeraldRuleListController.php',
|
||||||
|
'HeraldRuleListView' => 'applications/herald/view/HeraldRuleListView.php',
|
||||||
|
'HeraldRuleNameTransaction' => 'applications/herald/xaction/HeraldRuleNameTransaction.php',
|
||||||
'HeraldRulePHIDType' => 'applications/herald/phid/HeraldRulePHIDType.php',
|
'HeraldRulePHIDType' => 'applications/herald/phid/HeraldRulePHIDType.php',
|
||||||
'HeraldRuleQuery' => 'applications/herald/query/HeraldRuleQuery.php',
|
'HeraldRuleQuery' => 'applications/herald/query/HeraldRuleQuery.php',
|
||||||
'HeraldRuleReplyHandler' => 'applications/herald/mail/HeraldRuleReplyHandler.php',
|
'HeraldRuleReplyHandler' => 'applications/herald/mail/HeraldRuleReplyHandler.php',
|
||||||
|
@ -1535,7 +1553,7 @@ phutil_register_library_map(array(
|
||||||
'HeraldRuleSerializer' => 'applications/herald/editor/HeraldRuleSerializer.php',
|
'HeraldRuleSerializer' => 'applications/herald/editor/HeraldRuleSerializer.php',
|
||||||
'HeraldRuleTestCase' => 'applications/herald/storage/__tests__/HeraldRuleTestCase.php',
|
'HeraldRuleTestCase' => 'applications/herald/storage/__tests__/HeraldRuleTestCase.php',
|
||||||
'HeraldRuleTransaction' => 'applications/herald/storage/HeraldRuleTransaction.php',
|
'HeraldRuleTransaction' => 'applications/herald/storage/HeraldRuleTransaction.php',
|
||||||
'HeraldRuleTransactionComment' => 'applications/herald/storage/HeraldRuleTransactionComment.php',
|
'HeraldRuleTransactionType' => 'applications/herald/xaction/HeraldRuleTransactionType.php',
|
||||||
'HeraldRuleTranscript' => 'applications/herald/storage/transcript/HeraldRuleTranscript.php',
|
'HeraldRuleTranscript' => 'applications/herald/storage/transcript/HeraldRuleTranscript.php',
|
||||||
'HeraldRuleTypeConfig' => 'applications/herald/config/HeraldRuleTypeConfig.php',
|
'HeraldRuleTypeConfig' => 'applications/herald/config/HeraldRuleTypeConfig.php',
|
||||||
'HeraldRuleTypeDatasource' => 'applications/herald/typeahead/HeraldRuleTypeDatasource.php',
|
'HeraldRuleTypeDatasource' => 'applications/herald/typeahead/HeraldRuleTypeDatasource.php',
|
||||||
|
@ -1770,6 +1788,7 @@ phutil_register_library_map(array(
|
||||||
'ManiphestTaskTitleTransaction' => 'applications/maniphest/xaction/ManiphestTaskTitleTransaction.php',
|
'ManiphestTaskTitleTransaction' => 'applications/maniphest/xaction/ManiphestTaskTitleTransaction.php',
|
||||||
'ManiphestTaskTransactionType' => 'applications/maniphest/xaction/ManiphestTaskTransactionType.php',
|
'ManiphestTaskTransactionType' => 'applications/maniphest/xaction/ManiphestTaskTransactionType.php',
|
||||||
'ManiphestTaskUnblockTransaction' => 'applications/maniphest/xaction/ManiphestTaskUnblockTransaction.php',
|
'ManiphestTaskUnblockTransaction' => 'applications/maniphest/xaction/ManiphestTaskUnblockTransaction.php',
|
||||||
|
'ManiphestTaskUnlockEngine' => 'applications/maniphest/engine/ManiphestTaskUnlockEngine.php',
|
||||||
'ManiphestTransaction' => 'applications/maniphest/storage/ManiphestTransaction.php',
|
'ManiphestTransaction' => 'applications/maniphest/storage/ManiphestTransaction.php',
|
||||||
'ManiphestTransactionComment' => 'applications/maniphest/storage/ManiphestTransactionComment.php',
|
'ManiphestTransactionComment' => 'applications/maniphest/storage/ManiphestTransactionComment.php',
|
||||||
'ManiphestTransactionEditor' => 'applications/maniphest/editor/ManiphestTransactionEditor.php',
|
'ManiphestTransactionEditor' => 'applications/maniphest/editor/ManiphestTransactionEditor.php',
|
||||||
|
@ -2960,6 +2979,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorDebugController' => 'applications/system/controller/PhabricatorDebugController.php',
|
'PhabricatorDebugController' => 'applications/system/controller/PhabricatorDebugController.php',
|
||||||
'PhabricatorDefaultRequestExceptionHandler' => 'aphront/handler/PhabricatorDefaultRequestExceptionHandler.php',
|
'PhabricatorDefaultRequestExceptionHandler' => 'aphront/handler/PhabricatorDefaultRequestExceptionHandler.php',
|
||||||
'PhabricatorDefaultSyntaxStyle' => 'infrastructure/syntax/PhabricatorDefaultSyntaxStyle.php',
|
'PhabricatorDefaultSyntaxStyle' => 'infrastructure/syntax/PhabricatorDefaultSyntaxStyle.php',
|
||||||
|
'PhabricatorDefaultUnlockEngine' => 'applications/system/engine/PhabricatorDefaultUnlockEngine.php',
|
||||||
'PhabricatorDestructibleCodex' => 'applications/system/codex/PhabricatorDestructibleCodex.php',
|
'PhabricatorDestructibleCodex' => 'applications/system/codex/PhabricatorDestructibleCodex.php',
|
||||||
'PhabricatorDestructibleCodexInterface' => 'applications/system/interface/PhabricatorDestructibleCodexInterface.php',
|
'PhabricatorDestructibleCodexInterface' => 'applications/system/interface/PhabricatorDestructibleCodexInterface.php',
|
||||||
'PhabricatorDestructibleInterface' => 'applications/system/interface/PhabricatorDestructibleInterface.php',
|
'PhabricatorDestructibleInterface' => 'applications/system/interface/PhabricatorDestructibleInterface.php',
|
||||||
|
@ -4687,6 +4707,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorUnitTestContentSource' => 'infrastructure/contentsource/PhabricatorUnitTestContentSource.php',
|
'PhabricatorUnitTestContentSource' => 'infrastructure/contentsource/PhabricatorUnitTestContentSource.php',
|
||||||
'PhabricatorUnitsTestCase' => 'view/__tests__/PhabricatorUnitsTestCase.php',
|
'PhabricatorUnitsTestCase' => 'view/__tests__/PhabricatorUnitsTestCase.php',
|
||||||
'PhabricatorUnknownContentSource' => 'infrastructure/contentsource/PhabricatorUnknownContentSource.php',
|
'PhabricatorUnknownContentSource' => 'infrastructure/contentsource/PhabricatorUnknownContentSource.php',
|
||||||
|
'PhabricatorUnlockEngine' => 'applications/system/engine/PhabricatorUnlockEngine.php',
|
||||||
|
'PhabricatorUnlockableInterface' => 'applications/system/interface/PhabricatorUnlockableInterface.php',
|
||||||
'PhabricatorUnsubscribedFromObjectEdgeType' => 'applications/transactions/edges/PhabricatorUnsubscribedFromObjectEdgeType.php',
|
'PhabricatorUnsubscribedFromObjectEdgeType' => 'applications/transactions/edges/PhabricatorUnsubscribedFromObjectEdgeType.php',
|
||||||
'PhabricatorUser' => 'applications/people/storage/PhabricatorUser.php',
|
'PhabricatorUser' => 'applications/people/storage/PhabricatorUser.php',
|
||||||
'PhabricatorUserApproveTransaction' => 'applications/people/xaction/PhabricatorUserApproveTransaction.php',
|
'PhabricatorUserApproveTransaction' => 'applications/people/xaction/PhabricatorUserApproveTransaction.php',
|
||||||
|
@ -6171,6 +6193,7 @@ phutil_register_library_map(array(
|
||||||
'DifferentialRevisionUpdateTransaction' => 'DifferentialRevisionTransactionType',
|
'DifferentialRevisionUpdateTransaction' => 'DifferentialRevisionTransactionType',
|
||||||
'DifferentialRevisionViewController' => 'DifferentialController',
|
'DifferentialRevisionViewController' => 'DifferentialController',
|
||||||
'DifferentialRevisionVoidTransaction' => 'DifferentialRevisionTransactionType',
|
'DifferentialRevisionVoidTransaction' => 'DifferentialRevisionTransactionType',
|
||||||
|
'DifferentialRevisionWrongBuildsTransaction' => 'DifferentialRevisionTransactionType',
|
||||||
'DifferentialRevisionWrongStateTransaction' => 'DifferentialRevisionTransactionType',
|
'DifferentialRevisionWrongStateTransaction' => 'DifferentialRevisionTransactionType',
|
||||||
'DifferentialSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
'DifferentialSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||||
'DifferentialSetDiffPropertyConduitAPIMethod' => 'DifferentialConduitAPIMethod',
|
'DifferentialSetDiffPropertyConduitAPIMethod' => 'DifferentialConduitAPIMethod',
|
||||||
|
@ -6936,19 +6959,28 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorNgramsInterface',
|
'PhabricatorNgramsInterface',
|
||||||
'PhabricatorConduitResultInterface',
|
'PhabricatorConduitResultInterface',
|
||||||
'PhabricatorProjectInterface',
|
'PhabricatorProjectInterface',
|
||||||
|
'PhabricatorPolicyCodexInterface',
|
||||||
),
|
),
|
||||||
|
'HarbormasterBuildPlanBehavior' => 'Phobject',
|
||||||
|
'HarbormasterBuildPlanBehaviorOption' => 'Phobject',
|
||||||
|
'HarbormasterBuildPlanBehaviorTransaction' => 'HarbormasterBuildPlanTransactionType',
|
||||||
'HarbormasterBuildPlanDatasource' => 'PhabricatorTypeaheadDatasource',
|
'HarbormasterBuildPlanDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||||
'HarbormasterBuildPlanDefaultEditCapability' => 'PhabricatorPolicyCapability',
|
'HarbormasterBuildPlanDefaultEditCapability' => 'PhabricatorPolicyCapability',
|
||||||
'HarbormasterBuildPlanDefaultViewCapability' => 'PhabricatorPolicyCapability',
|
'HarbormasterBuildPlanDefaultViewCapability' => 'PhabricatorPolicyCapability',
|
||||||
|
'HarbormasterBuildPlanEditAPIMethod' => 'PhabricatorEditEngineAPIMethod',
|
||||||
'HarbormasterBuildPlanEditEngine' => 'PhabricatorEditEngine',
|
'HarbormasterBuildPlanEditEngine' => 'PhabricatorEditEngine',
|
||||||
'HarbormasterBuildPlanEditor' => 'PhabricatorApplicationTransactionEditor',
|
'HarbormasterBuildPlanEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||||
'HarbormasterBuildPlanNameNgrams' => 'PhabricatorSearchNgrams',
|
'HarbormasterBuildPlanNameNgrams' => 'PhabricatorSearchNgrams',
|
||||||
|
'HarbormasterBuildPlanNameTransaction' => 'HarbormasterBuildPlanTransactionType',
|
||||||
'HarbormasterBuildPlanPHIDType' => 'PhabricatorPHIDType',
|
'HarbormasterBuildPlanPHIDType' => 'PhabricatorPHIDType',
|
||||||
|
'HarbormasterBuildPlanPolicyCodex' => 'PhabricatorPolicyCodex',
|
||||||
'HarbormasterBuildPlanQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'HarbormasterBuildPlanQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
'HarbormasterBuildPlanSearchAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
'HarbormasterBuildPlanSearchAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
||||||
'HarbormasterBuildPlanSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
'HarbormasterBuildPlanSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||||
'HarbormasterBuildPlanTransaction' => 'PhabricatorApplicationTransaction',
|
'HarbormasterBuildPlanStatusTransaction' => 'HarbormasterBuildPlanTransactionType',
|
||||||
|
'HarbormasterBuildPlanTransaction' => 'PhabricatorModularTransaction',
|
||||||
'HarbormasterBuildPlanTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
'HarbormasterBuildPlanTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||||
|
'HarbormasterBuildPlanTransactionType' => 'PhabricatorModularTransactionType',
|
||||||
'HarbormasterBuildQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'HarbormasterBuildQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
'HarbormasterBuildRequest' => 'Phobject',
|
'HarbormasterBuildRequest' => 'Phobject',
|
||||||
'HarbormasterBuildSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
'HarbormasterBuildSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
||||||
|
@ -6991,6 +7023,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPolicyInterface',
|
'PhabricatorPolicyInterface',
|
||||||
),
|
),
|
||||||
'HarbormasterBuildUnitMessageQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'HarbormasterBuildUnitMessageQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
|
'HarbormasterBuildView' => 'AphrontView',
|
||||||
'HarbormasterBuildViewController' => 'HarbormasterController',
|
'HarbormasterBuildViewController' => 'HarbormasterController',
|
||||||
'HarbormasterBuildWorker' => 'HarbormasterWorker',
|
'HarbormasterBuildWorker' => 'HarbormasterWorker',
|
||||||
'HarbormasterBuildable' => array(
|
'HarbormasterBuildable' => array(
|
||||||
|
@ -7047,6 +7080,7 @@ phutil_register_library_map(array(
|
||||||
'HarbormasterMessageType' => 'Phobject',
|
'HarbormasterMessageType' => 'Phobject',
|
||||||
'HarbormasterObject' => 'HarbormasterDAO',
|
'HarbormasterObject' => 'HarbormasterDAO',
|
||||||
'HarbormasterOtherBuildStepGroup' => 'HarbormasterBuildStepGroup',
|
'HarbormasterOtherBuildStepGroup' => 'HarbormasterBuildStepGroup',
|
||||||
|
'HarbormasterPlanBehaviorController' => 'HarbormasterPlanController',
|
||||||
'HarbormasterPlanController' => 'HarbormasterController',
|
'HarbormasterPlanController' => 'HarbormasterController',
|
||||||
'HarbormasterPlanDisableController' => 'HarbormasterPlanController',
|
'HarbormasterPlanDisableController' => 'HarbormasterPlanController',
|
||||||
'HarbormasterPlanEditController' => 'HarbormasterPlanController',
|
'HarbormasterPlanEditController' => 'HarbormasterPlanController',
|
||||||
|
@ -7060,6 +7094,7 @@ phutil_register_library_map(array(
|
||||||
'HarbormasterQueryBuildsConduitAPIMethod' => 'HarbormasterConduitAPIMethod',
|
'HarbormasterQueryBuildsConduitAPIMethod' => 'HarbormasterConduitAPIMethod',
|
||||||
'HarbormasterQueryBuildsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
'HarbormasterQueryBuildsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
||||||
'HarbormasterRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
'HarbormasterRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
||||||
|
'HarbormasterRestartException' => 'Exception',
|
||||||
'HarbormasterRunBuildPlansHeraldAction' => 'HeraldAction',
|
'HarbormasterRunBuildPlansHeraldAction' => 'HeraldAction',
|
||||||
'HarbormasterSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
'HarbormasterSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||||
'HarbormasterScratchTable' => 'HarbormasterDAO',
|
'HarbormasterScratchTable' => 'HarbormasterDAO',
|
||||||
|
@ -7159,24 +7194,31 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorFlaggableInterface',
|
'PhabricatorFlaggableInterface',
|
||||||
'PhabricatorPolicyInterface',
|
'PhabricatorPolicyInterface',
|
||||||
'PhabricatorDestructibleInterface',
|
'PhabricatorDestructibleInterface',
|
||||||
|
'PhabricatorIndexableInterface',
|
||||||
'PhabricatorSubscribableInterface',
|
'PhabricatorSubscribableInterface',
|
||||||
),
|
),
|
||||||
|
'HeraldRuleActionAffectsObjectEdgeType' => 'PhabricatorEdgeType',
|
||||||
'HeraldRuleAdapter' => 'HeraldAdapter',
|
'HeraldRuleAdapter' => 'HeraldAdapter',
|
||||||
'HeraldRuleAdapterField' => 'HeraldRuleField',
|
'HeraldRuleAdapterField' => 'HeraldRuleField',
|
||||||
'HeraldRuleController' => 'HeraldController',
|
'HeraldRuleController' => 'HeraldController',
|
||||||
'HeraldRuleDatasource' => 'PhabricatorTypeaheadDatasource',
|
'HeraldRuleDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||||
|
'HeraldRuleDisableTransaction' => 'HeraldRuleTransactionType',
|
||||||
|
'HeraldRuleEditTransaction' => 'HeraldRuleTransactionType',
|
||||||
'HeraldRuleEditor' => 'PhabricatorApplicationTransactionEditor',
|
'HeraldRuleEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||||
'HeraldRuleField' => 'HeraldField',
|
'HeraldRuleField' => 'HeraldField',
|
||||||
'HeraldRuleFieldGroup' => 'HeraldFieldGroup',
|
'HeraldRuleFieldGroup' => 'HeraldFieldGroup',
|
||||||
|
'HeraldRuleIndexEngineExtension' => 'PhabricatorIndexEngineExtension',
|
||||||
'HeraldRuleListController' => 'HeraldController',
|
'HeraldRuleListController' => 'HeraldController',
|
||||||
|
'HeraldRuleListView' => 'AphrontView',
|
||||||
|
'HeraldRuleNameTransaction' => 'HeraldRuleTransactionType',
|
||||||
'HeraldRulePHIDType' => 'PhabricatorPHIDType',
|
'HeraldRulePHIDType' => 'PhabricatorPHIDType',
|
||||||
'HeraldRuleQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'HeraldRuleQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
'HeraldRuleReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
|
'HeraldRuleReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
|
||||||
'HeraldRuleSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
'HeraldRuleSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||||
'HeraldRuleSerializer' => 'Phobject',
|
'HeraldRuleSerializer' => 'Phobject',
|
||||||
'HeraldRuleTestCase' => 'PhabricatorTestCase',
|
'HeraldRuleTestCase' => 'PhabricatorTestCase',
|
||||||
'HeraldRuleTransaction' => 'PhabricatorApplicationTransaction',
|
'HeraldRuleTransaction' => 'PhabricatorModularTransaction',
|
||||||
'HeraldRuleTransactionComment' => 'PhabricatorApplicationTransactionComment',
|
'HeraldRuleTransactionType' => 'PhabricatorModularTransactionType',
|
||||||
'HeraldRuleTranscript' => 'Phobject',
|
'HeraldRuleTranscript' => 'Phobject',
|
||||||
'HeraldRuleTypeConfig' => 'Phobject',
|
'HeraldRuleTypeConfig' => 'Phobject',
|
||||||
'HeraldRuleTypeDatasource' => 'PhabricatorTypeaheadDatasource',
|
'HeraldRuleTypeDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||||
|
@ -7388,6 +7430,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorEditEngineLockableInterface',
|
'PhabricatorEditEngineLockableInterface',
|
||||||
'PhabricatorEditEngineMFAInterface',
|
'PhabricatorEditEngineMFAInterface',
|
||||||
'PhabricatorPolicyCodexInterface',
|
'PhabricatorPolicyCodexInterface',
|
||||||
|
'PhabricatorUnlockableInterface',
|
||||||
),
|
),
|
||||||
'ManiphestTaskAssignHeraldAction' => 'HeraldAction',
|
'ManiphestTaskAssignHeraldAction' => 'HeraldAction',
|
||||||
'ManiphestTaskAssignOtherHeraldAction' => 'ManiphestTaskAssignHeraldAction',
|
'ManiphestTaskAssignOtherHeraldAction' => 'ManiphestTaskAssignHeraldAction',
|
||||||
|
@ -7465,6 +7508,7 @@ phutil_register_library_map(array(
|
||||||
'ManiphestTaskTitleTransaction' => 'ManiphestTaskTransactionType',
|
'ManiphestTaskTitleTransaction' => 'ManiphestTaskTransactionType',
|
||||||
'ManiphestTaskTransactionType' => 'PhabricatorModularTransactionType',
|
'ManiphestTaskTransactionType' => 'PhabricatorModularTransactionType',
|
||||||
'ManiphestTaskUnblockTransaction' => 'ManiphestTaskTransactionType',
|
'ManiphestTaskUnblockTransaction' => 'ManiphestTaskTransactionType',
|
||||||
|
'ManiphestTaskUnlockEngine' => 'PhabricatorUnlockEngine',
|
||||||
'ManiphestTransaction' => 'PhabricatorModularTransaction',
|
'ManiphestTransaction' => 'PhabricatorModularTransaction',
|
||||||
'ManiphestTransactionComment' => 'PhabricatorApplicationTransactionComment',
|
'ManiphestTransactionComment' => 'PhabricatorApplicationTransactionComment',
|
||||||
'ManiphestTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
|
'ManiphestTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||||
|
@ -8842,6 +8886,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorDebugController' => 'PhabricatorController',
|
'PhabricatorDebugController' => 'PhabricatorController',
|
||||||
'PhabricatorDefaultRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
|
'PhabricatorDefaultRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
|
||||||
'PhabricatorDefaultSyntaxStyle' => 'PhabricatorSyntaxStyle',
|
'PhabricatorDefaultSyntaxStyle' => 'PhabricatorSyntaxStyle',
|
||||||
|
'PhabricatorDefaultUnlockEngine' => 'PhabricatorUnlockEngine',
|
||||||
'PhabricatorDestructibleCodex' => 'Phobject',
|
'PhabricatorDestructibleCodex' => 'Phobject',
|
||||||
'PhabricatorDestructionEngine' => 'Phobject',
|
'PhabricatorDestructionEngine' => 'Phobject',
|
||||||
'PhabricatorDestructionEngineExtension' => 'Phobject',
|
'PhabricatorDestructionEngineExtension' => 'Phobject',
|
||||||
|
@ -10852,6 +10897,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorUnitTestContentSource' => 'PhabricatorContentSource',
|
'PhabricatorUnitTestContentSource' => 'PhabricatorContentSource',
|
||||||
'PhabricatorUnitsTestCase' => 'PhabricatorTestCase',
|
'PhabricatorUnitsTestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorUnknownContentSource' => 'PhabricatorContentSource',
|
'PhabricatorUnknownContentSource' => 'PhabricatorContentSource',
|
||||||
|
'PhabricatorUnlockEngine' => 'Phobject',
|
||||||
'PhabricatorUnsubscribedFromObjectEdgeType' => 'PhabricatorEdgeType',
|
'PhabricatorUnsubscribedFromObjectEdgeType' => 'PhabricatorEdgeType',
|
||||||
'PhabricatorUser' => array(
|
'PhabricatorUser' => array(
|
||||||
'PhabricatorUserDAO',
|
'PhabricatorUserDAO',
|
||||||
|
|
|
@ -591,15 +591,11 @@ final class AphrontRequest extends Phobject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRequestURI() {
|
public function getRequestURI() {
|
||||||
$request_uri = idx($_SERVER, 'REQUEST_URI', '/');
|
$uri_path = phutil_escape_uri($this->getPath());
|
||||||
|
$uri_query = idx($_SERVER, 'QUERY_STRING', '');
|
||||||
|
|
||||||
$uri = new PhutilURI($request_uri);
|
return id(new PhutilURI($uri_path.'?'.$uri_query))
|
||||||
$uri->removeQueryParam('__path__');
|
->removeQueryParam('__path__');
|
||||||
|
|
||||||
$path = phutil_escape_uri($this->getPath());
|
|
||||||
$uri->setPath($path);
|
|
||||||
|
|
||||||
return $uri;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAbsoluteRequestURI() {
|
public function getAbsoluteRequestURI() {
|
||||||
|
|
|
@ -776,7 +776,6 @@ final class AphrontApplicationConfiguration
|
||||||
'filler' => str_repeat('Q', 1024 * 16),
|
'filler' => str_repeat('Q', 1024 * 16),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
return id(new AphrontJSONResponse())
|
return id(new AphrontJSONResponse())
|
||||||
->setAddJSONShield(false)
|
->setAddJSONShield(false)
|
||||||
->setContent($result);
|
->setContent($result);
|
||||||
|
|
|
@ -147,7 +147,7 @@ final class PhabricatorAuthInviteEngine extends Phobject {
|
||||||
// no address. Users can use password recovery to access the other
|
// no address. Users can use password recovery to access the other
|
||||||
// account if they really control the address.
|
// account if they really control the address.
|
||||||
throw id(new PhabricatorAuthInviteAccountException(
|
throw id(new PhabricatorAuthInviteAccountException(
|
||||||
pht('Wrong Acount'),
|
pht('Wrong Account'),
|
||||||
pht(
|
pht(
|
||||||
'You are logged in as %s, but the email address you just '.
|
'You are logged in as %s, but the email address you just '.
|
||||||
'clicked a link from is already the primary email address '.
|
'clicked a link from is already the primary email address '.
|
||||||
|
|
|
@ -714,7 +714,14 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||||
if (isset($validation_results[$factor_phid])) {
|
if (isset($validation_results[$factor_phid])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$validation_results[$factor_phid] = new PhabricatorAuthFactorResult();
|
|
||||||
|
$issued_challenges = idx($challenge_map, $factor_phid, array());
|
||||||
|
|
||||||
|
$validation_results[$factor_phid] = $impl->getResultForPrompt(
|
||||||
|
$factor,
|
||||||
|
$viewer,
|
||||||
|
$request,
|
||||||
|
$issued_challenges);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw id(new PhabricatorAuthHighSecurityRequiredException())
|
throw id(new PhabricatorAuthHighSecurityRequiredException())
|
||||||
|
|
|
@ -221,6 +221,40 @@ abstract class PhabricatorAuthFactor extends Phobject {
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final public function getResultForPrompt(
|
||||||
|
PhabricatorAuthFactorConfig $config,
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
AphrontRequest $request,
|
||||||
|
array $challenges) {
|
||||||
|
assert_instances_of($challenges, 'PhabricatorAuthChallenge');
|
||||||
|
|
||||||
|
$result = $this->newResultForPrompt(
|
||||||
|
$config,
|
||||||
|
$viewer,
|
||||||
|
$request,
|
||||||
|
$challenges);
|
||||||
|
|
||||||
|
if (!$this->isAuthResult($result)) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Expected "newResultForPrompt()" to return an object of class "%s", '.
|
||||||
|
'but it returned something else ("%s"; in "%s").',
|
||||||
|
'PhabricatorAuthFactorResult',
|
||||||
|
phutil_describe_type($result),
|
||||||
|
get_class($this)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newResultForPrompt(
|
||||||
|
PhabricatorAuthFactorConfig $config,
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
AphrontRequest $request,
|
||||||
|
array $challenges) {
|
||||||
|
return $this->newResult();
|
||||||
|
}
|
||||||
|
|
||||||
abstract protected function newResultFromIssuedChallenges(
|
abstract protected function newResultFromIssuedChallenges(
|
||||||
PhabricatorAuthFactorConfig $config,
|
PhabricatorAuthFactorConfig $config,
|
||||||
PhabricatorUser $viewer,
|
PhabricatorUser $viewer,
|
||||||
|
|
|
@ -681,6 +681,19 @@ final class PhabricatorDuoAuthFactor
|
||||||
AphrontRequest $request,
|
AphrontRequest $request,
|
||||||
array $challenges) {
|
array $challenges) {
|
||||||
|
|
||||||
|
return $this->getResultForPrompt(
|
||||||
|
$config,
|
||||||
|
$viewer,
|
||||||
|
$request,
|
||||||
|
$challenges);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newResultForPrompt(
|
||||||
|
PhabricatorAuthFactorConfig $config,
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
AphrontRequest $request,
|
||||||
|
array $challenges) {
|
||||||
|
|
||||||
$result = $this->newResult()
|
$result = $this->newResult()
|
||||||
->setIsContinue(true)
|
->setIsContinue(true)
|
||||||
->setErrorMessage(
|
->setErrorMessage(
|
||||||
|
|
|
@ -58,6 +58,10 @@ final class PhabricatorConduitCallManagementWorkflow
|
||||||
'No such user "%s" exists.',
|
'No such user "%s" exists.',
|
||||||
$as));
|
$as));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allow inline generation of user caches for the user we're acting
|
||||||
|
// as, since some calls may read user preferences.
|
||||||
|
$actor->setAllowInlineCacheGeneration(true);
|
||||||
} else {
|
} else {
|
||||||
$actor = $viewer;
|
$actor = $viewer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -409,4 +409,19 @@ abstract class ConduitAPIMethod
|
||||||
$capability);
|
$capability);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final protected function newRemarkupDocumentationView($remarkup) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$view = new PHUIRemarkupView($viewer, $remarkup);
|
||||||
|
|
||||||
|
$view->setRemarkupOptions(
|
||||||
|
array(
|
||||||
|
PHUIRemarkupView::OPTION_PRESERVE_LINEBREAKS => false,
|
||||||
|
));
|
||||||
|
|
||||||
|
return id(new PHUIBoxView())
|
||||||
|
->appendChild($view)
|
||||||
|
->addPadding(PHUI::PADDING_LARGE);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,30 +129,16 @@ final class PhabricatorWebServerSetupCheck extends PhabricatorSetupCheck {
|
||||||
}
|
}
|
||||||
|
|
||||||
$structure = null;
|
$structure = null;
|
||||||
$caught = null;
|
|
||||||
$extra_whitespace = ($body !== trim($body));
|
$extra_whitespace = ($body !== trim($body));
|
||||||
|
|
||||||
if (!$extra_whitespace) {
|
try {
|
||||||
try {
|
$structure = phutil_json_decode(trim($body));
|
||||||
$structure = phutil_json_decode($body);
|
} catch (Exception $ex) {
|
||||||
} catch (Exception $ex) {
|
// Ignore the exception, we only care if the decode worked or not.
|
||||||
$caught = $ex;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$structure) {
|
if (!$structure || $extra_whitespace) {
|
||||||
if ($extra_whitespace) {
|
if (!$structure) {
|
||||||
$message = pht(
|
|
||||||
'Phabricator sent itself a test request and expected to get a bare '.
|
|
||||||
'JSON response back, but the response had extra whitespace at '.
|
|
||||||
'the beginning or end.'.
|
|
||||||
"\n\n".
|
|
||||||
'This usually means you have edited a file and left whitespace '.
|
|
||||||
'characters before the opening %s tag, or after a closing %s tag. '.
|
|
||||||
'Remove any leading whitespace, and prefer to omit closing tags.',
|
|
||||||
phutil_tag('tt', array(), '<?php'),
|
|
||||||
phutil_tag('tt', array(), '?>'));
|
|
||||||
} else {
|
|
||||||
$short = id(new PhutilUTF8StringTruncator())
|
$short = id(new PhutilUTF8StringTruncator())
|
||||||
->setMaximumGlyphs(1024)
|
->setMaximumGlyphs(1024)
|
||||||
->truncateString($body);
|
->truncateString($body);
|
||||||
|
@ -166,6 +152,17 @@ final class PhabricatorWebServerSetupCheck extends PhabricatorSetupCheck {
|
||||||
"\n\n".
|
"\n\n".
|
||||||
'Something is misconfigured or otherwise mangling responses.',
|
'Something is misconfigured or otherwise mangling responses.',
|
||||||
phutil_tag('pre', array(), $short));
|
phutil_tag('pre', array(), $short));
|
||||||
|
} else {
|
||||||
|
$message = pht(
|
||||||
|
'Phabricator sent itself a test request and expected to get a bare '.
|
||||||
|
'JSON response back. It received a JSON response, but the response '.
|
||||||
|
'had extra whitespace at the beginning or end.'.
|
||||||
|
"\n\n".
|
||||||
|
'This usually means you have edited a file and left whitespace '.
|
||||||
|
'characters before the opening %s tag, or after a closing %s tag. '.
|
||||||
|
'Remove any leading whitespace, and prefer to omit closing tags.',
|
||||||
|
phutil_tag('tt', array(), '<?php'),
|
||||||
|
phutil_tag('tt', array(), '?>'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->newIssue('webserver.mangle')
|
$this->newIssue('webserver.mangle')
|
||||||
|
@ -174,7 +171,9 @@ final class PhabricatorWebServerSetupCheck extends PhabricatorSetupCheck {
|
||||||
->setMessage($message);
|
->setMessage($message);
|
||||||
|
|
||||||
// We can't run the other checks if we could not decode the response.
|
// We can't run the other checks if we could not decode the response.
|
||||||
return;
|
if (!$structure) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$actual_user = idx($structure, 'user');
|
$actual_user = idx($structure, 'user');
|
||||||
|
|
|
@ -43,7 +43,7 @@ final class PhabricatorSetConfigType
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($value) {
|
if ($value) {
|
||||||
if (array_keys($value) !== range(0, count($value) - 1)) {
|
if (!phutil_is_natural_list($value)) {
|
||||||
throw $this->newException(
|
throw $this->newException(
|
||||||
pht(
|
pht(
|
||||||
'Option "%s" is of type "%s", and should be specified on the '.
|
'Option "%s" is of type "%s", and should be specified on the '.
|
||||||
|
|
|
@ -204,9 +204,9 @@ final class DifferentialInlineCommentEditController
|
||||||
|
|
||||||
queryfx(
|
queryfx(
|
||||||
$conn_w,
|
$conn_w,
|
||||||
'INSERT IGNORE INTO %T (userPHID, commentID) VALUES %Q',
|
'INSERT IGNORE INTO %T (userPHID, commentID) VALUES %LQ',
|
||||||
$table->getTableName(),
|
$table->getTableName(),
|
||||||
implode(', ', $sql));
|
$sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function showComments(array $ids) {
|
protected function showComments(array $ids) {
|
||||||
|
|
|
@ -208,15 +208,6 @@ final class DifferentialDiffEditor
|
||||||
return $adapter;
|
return $adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function didApplyHeraldRules(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
HeraldAdapter $adapter,
|
|
||||||
HeraldTranscript $transcript) {
|
|
||||||
|
|
||||||
$xactions = array();
|
|
||||||
return $xactions;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function updateDiffFromDict(DifferentialDiff $diff, $dict) {
|
private function updateDiffFromDict(DifferentialDiff $diff, $dict) {
|
||||||
$diff
|
$diff
|
||||||
->setSourcePath(idx($dict, 'sourcePath'))
|
->setSourcePath(idx($dict, 'sourcePath'))
|
||||||
|
|
|
@ -54,6 +54,12 @@ final class DifferentialChangesetEngine extends Phobject {
|
||||||
if (strpos($new_data, '@'.'generated') !== false) {
|
if (strpos($new_data, '@'.'generated') !== false) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See PHI1112. This is the official pattern for marking Go code as
|
||||||
|
// generated.
|
||||||
|
if (preg_match('(^// Code generated .* DO NOT EDIT\.$)m', $new_data)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -285,6 +285,24 @@ final class DifferentialDiffExtractionEngine extends Phobject {
|
||||||
->setNewValue($revision->getModernRevisionStatus());
|
->setNewValue($revision->getModernRevisionStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$concerning_builds = $this->loadConcerningBuilds($revision);
|
||||||
|
if ($concerning_builds) {
|
||||||
|
$build_list = array();
|
||||||
|
foreach ($concerning_builds as $build) {
|
||||||
|
$build_list[] = array(
|
||||||
|
'phid' => $build->getPHID(),
|
||||||
|
'status' => $build->getBuildStatus(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$wrong_builds =
|
||||||
|
DifferentialRevisionWrongBuildsTransaction::TRANSACTIONTYPE;
|
||||||
|
|
||||||
|
$xactions[] = id(new DifferentialTransaction())
|
||||||
|
->setTransactionType($wrong_builds)
|
||||||
|
->setNewValue($build_list);
|
||||||
|
}
|
||||||
|
|
||||||
$type_update = DifferentialRevisionUpdateTransaction::TRANSACTIONTYPE;
|
$type_update = DifferentialRevisionUpdateTransaction::TRANSACTIONTYPE;
|
||||||
|
|
||||||
$xactions[] = id(new DifferentialTransaction())
|
$xactions[] = id(new DifferentialTransaction())
|
||||||
|
@ -322,4 +340,81 @@ final class DifferentialDiffExtractionEngine extends Phobject {
|
||||||
return $result_data;
|
return $result_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function loadConcerningBuilds(DifferentialRevision $revision) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$diff = $revision->getActiveDiff();
|
||||||
|
|
||||||
|
$buildables = id(new HarbormasterBuildableQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withBuildablePHIDs(array($diff->getPHID()))
|
||||||
|
->needBuilds(true)
|
||||||
|
->withManualBuildables(false)
|
||||||
|
->execute();
|
||||||
|
if (!$buildables) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$land_key = HarbormasterBuildPlanBehavior::BEHAVIOR_LANDWARNING;
|
||||||
|
$behavior = HarbormasterBuildPlanBehavior::getBehavior($land_key);
|
||||||
|
|
||||||
|
$key_never = HarbormasterBuildPlanBehavior::LANDWARNING_NEVER;
|
||||||
|
$key_building = HarbormasterBuildPlanBehavior::LANDWARNING_IF_BUILDING;
|
||||||
|
$key_complete = HarbormasterBuildPlanBehavior::LANDWARNING_IF_COMPLETE;
|
||||||
|
|
||||||
|
$concerning_builds = array();
|
||||||
|
foreach ($buildables as $buildable) {
|
||||||
|
$builds = $buildable->getBuilds();
|
||||||
|
foreach ($builds as $build) {
|
||||||
|
$plan = $build->getBuildPlan();
|
||||||
|
$option = $behavior->getPlanOption($plan);
|
||||||
|
$behavior_value = $option->getKey();
|
||||||
|
|
||||||
|
$if_never = ($behavior_value === $key_never);
|
||||||
|
if ($if_never) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$if_building = ($behavior_value === $key_building);
|
||||||
|
if ($if_building && $build->isComplete()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$if_complete = ($behavior_value === $key_complete);
|
||||||
|
if ($if_complete) {
|
||||||
|
if (!$build->isComplete()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: If you "arc land" and a build with "Warn: If Complete"
|
||||||
|
// is still running, you may not see a warning, and push the revision
|
||||||
|
// in good faith. The build may then complete before we get here, so
|
||||||
|
// we now see a completed, failed build.
|
||||||
|
|
||||||
|
// For now, just err on the side of caution and assume these builds
|
||||||
|
// were in a good state when we prompted the user, even if they're in
|
||||||
|
// a bad state now.
|
||||||
|
|
||||||
|
// We could refine this with a rule like "if the build finished
|
||||||
|
// within a couple of minutes before the push happened, assume it was
|
||||||
|
// in good faith", but we don't currently have an especially
|
||||||
|
// convenient way to check when the build finished or when the commit
|
||||||
|
// was pushed or discovered, and this would create some issues in
|
||||||
|
// cases where the repository is observed and the fetch pipeline
|
||||||
|
// stalls for a while.
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($build->isPassed()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$concerning_builds[] = $build;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $concerning_builds;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,7 +162,20 @@ final class DifferentialChangesetTwoUpRenderer
|
||||||
} else if (empty($new_lines[$ii])) {
|
} else if (empty($new_lines[$ii])) {
|
||||||
$o_class = 'old old-full';
|
$o_class = 'old old-full';
|
||||||
} else {
|
} else {
|
||||||
$o_class = 'old';
|
if (isset($depth_only[$ii])) {
|
||||||
|
if ($depth_only[$ii] == '>') {
|
||||||
|
// When a line has depth-only change, we only highlight the
|
||||||
|
// left side of the diff if the depth is decreasing. When the
|
||||||
|
// depth is increasing, the ">>" marker on the right hand side
|
||||||
|
// of the diff generally provides enough visibility on its own.
|
||||||
|
|
||||||
|
$o_class = '';
|
||||||
|
} else {
|
||||||
|
$o_class = 'old';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$o_class = 'old';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$o_classes = $o_class;
|
$o_classes = $o_class;
|
||||||
}
|
}
|
||||||
|
@ -200,13 +213,10 @@ final class DifferentialChangesetTwoUpRenderer
|
||||||
} else if (empty($old_lines[$ii])) {
|
} else if (empty($old_lines[$ii])) {
|
||||||
$n_class = 'new new-full';
|
$n_class = 'new new-full';
|
||||||
} else {
|
} else {
|
||||||
|
// When a line has a depth-only change, never highlight it on
|
||||||
// NOTE: At least for the moment, I'm intentionally clearing the
|
// the right side. The ">>" marker generally provides enough
|
||||||
// line highlighting only on the right side of the diff when a
|
// visibility on its own for indent depth increases, and the left
|
||||||
// line has only depth changes. When a block depth is decreased,
|
// side is still highlighted for indent depth decreases.
|
||||||
// this gives us a large color block on the left (to make it easy
|
|
||||||
// to see the depth change) but a clean diff on the right (to make
|
|
||||||
// it easy to pick out actual code changes).
|
|
||||||
|
|
||||||
if (isset($depth_only[$ii])) {
|
if (isset($depth_only[$ii])) {
|
||||||
$n_class = '';
|
$n_class = '';
|
||||||
|
|
|
@ -877,7 +877,7 @@ final class DifferentialRevision extends DifferentialDAO
|
||||||
PhabricatorUser $viewer,
|
PhabricatorUser $viewer,
|
||||||
array $phids) {
|
array $phids) {
|
||||||
|
|
||||||
return id(new HarbormasterBuildQuery())
|
$builds = id(new HarbormasterBuildQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withBuildablePHIDs($phids)
|
->withBuildablePHIDs($phids)
|
||||||
->withAutobuilds(false)
|
->withAutobuilds(false)
|
||||||
|
@ -893,6 +893,41 @@ final class DifferentialRevision extends DifferentialDAO
|
||||||
HarbormasterBuildStatus::STATUS_DEADLOCKED,
|
HarbormasterBuildStatus::STATUS_DEADLOCKED,
|
||||||
))
|
))
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
|
// Filter builds based on the "Hold Drafts" behavior of their associated
|
||||||
|
// build plans.
|
||||||
|
|
||||||
|
$hold_drafts = HarbormasterBuildPlanBehavior::BEHAVIOR_DRAFTS;
|
||||||
|
$behavior = HarbormasterBuildPlanBehavior::getBehavior($hold_drafts);
|
||||||
|
|
||||||
|
$key_never = HarbormasterBuildPlanBehavior::DRAFTS_NEVER;
|
||||||
|
$key_building = HarbormasterBuildPlanBehavior::DRAFTS_IF_BUILDING;
|
||||||
|
|
||||||
|
foreach ($builds as $key => $build) {
|
||||||
|
$plan = $build->getBuildPlan();
|
||||||
|
$hold_key = $behavior->getPlanOption($plan)->getKey();
|
||||||
|
|
||||||
|
$hold_never = ($hold_key === $key_never);
|
||||||
|
$hold_building = ($hold_key === $key_building);
|
||||||
|
|
||||||
|
// If the build "Never" holds drafts from promoting, we don't care what
|
||||||
|
// the status is.
|
||||||
|
if ($hold_never) {
|
||||||
|
unset($builds[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the build holds drafts from promoting "While Building", we only
|
||||||
|
// care about the status until it completes.
|
||||||
|
if ($hold_building) {
|
||||||
|
if ($build->isComplete()) {
|
||||||
|
unset($builds[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $builds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DifferentialRevisionWrongBuildsTransaction
|
||||||
|
extends DifferentialRevisionTransactionType {
|
||||||
|
|
||||||
|
const TRANSACTIONTYPE = 'differential.builds.wrong';
|
||||||
|
|
||||||
|
public function generateOldValue($object) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateNewValue($object, $value) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIcon() {
|
||||||
|
return 'fa-exclamation';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getColor() {
|
||||||
|
return 'pink';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getActionStrength() {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle() {
|
||||||
|
return pht(
|
||||||
|
'This revision was landed with ongoing or failed builds.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shouldHideForFeed() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -368,9 +368,9 @@ final class DiffusionBrowseQueryConduitAPIMethod
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($commit) {
|
if ($commit) {
|
||||||
$slice_clause = 'AND svnCommit <= '.(int)$commit;
|
$slice_clause = qsprintf($conn_r, 'AND svnCommit <= %d', $commit);
|
||||||
} else {
|
} else {
|
||||||
$slice_clause = '';
|
$slice_clause = qsprintf($conn_r, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
$index = queryfx_all(
|
$index = queryfx_all(
|
||||||
|
@ -439,9 +439,11 @@ final class DiffusionBrowseQueryConduitAPIMethod
|
||||||
|
|
||||||
$sql = array();
|
$sql = array();
|
||||||
foreach ($index as $row) {
|
foreach ($index as $row) {
|
||||||
$sql[] =
|
$sql[] = qsprintf(
|
||||||
'(pathID = '.(int)$row['pathID'].' AND '.
|
$conn_r,
|
||||||
'svnCommit = '.(int)$row['maxCommit'].')';
|
'(pathID = %d AND svnCommit = %d)',
|
||||||
|
$row['pathID'],
|
||||||
|
$row['maxCommit']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$browse = queryfx_all(
|
$browse = queryfx_all(
|
||||||
|
|
|
@ -215,13 +215,17 @@ final class DiffusionHistoryQueryConduitAPIMethod
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$filter_query = '';
|
$filter_query = qsprintf($conn_r, '');
|
||||||
if ($need_direct_changes) {
|
if ($need_direct_changes) {
|
||||||
if ($need_child_changes) {
|
if ($need_child_changes) {
|
||||||
$type = DifferentialChangeType::TYPE_CHILD;
|
$filter_query = qsprintf(
|
||||||
$filter_query = 'AND (isDirect = 1 OR changeType = '.$type.')';
|
$conn_r,
|
||||||
|
'AND (isDirect = 1 OR changeType = %s)',
|
||||||
|
DifferentialChangeType::TYPE_CHILD);
|
||||||
} else {
|
} else {
|
||||||
$filter_query = 'AND (isDirect = 1)';
|
$filter_query = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'AND (isDirect = 1)');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -709,8 +709,17 @@ final class DiffusionBrowseController extends DiffusionController {
|
||||||
'path' => $path,
|
'path' => $path,
|
||||||
));
|
));
|
||||||
|
|
||||||
$before_uri = $before_uri->alter('renamed', $renamed);
|
if ($renamed === null) {
|
||||||
$before_uri = $before_uri->alter('follow', $follow);
|
$before_uri->removeQueryParam('renamed');
|
||||||
|
} else {
|
||||||
|
$before_uri->replaceQueryParam('renamed', $renamed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($follow === null) {
|
||||||
|
$before_uri->removeQueryParam('follow');
|
||||||
|
} else {
|
||||||
|
$before_uri->replaceQueryParam('follow', $follow);
|
||||||
|
}
|
||||||
|
|
||||||
return id(new AphrontRedirectResponse())->setURI($before_uri);
|
return id(new AphrontRedirectResponse())->setURI($before_uri);
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,7 +192,10 @@ final class DiffusionServeController extends DiffusionController {
|
||||||
// Try Git LFS auth first since we can usually reject it without doing
|
// Try Git LFS auth first since we can usually reject it without doing
|
||||||
// any queries, since the username won't match the one we expect or the
|
// any queries, since the username won't match the one we expect or the
|
||||||
// request won't be LFS.
|
// request won't be LFS.
|
||||||
$viewer = $this->authenticateGitLFSUser($username, $password);
|
$viewer = $this->authenticateGitLFSUser(
|
||||||
|
$username,
|
||||||
|
$password,
|
||||||
|
$identifier);
|
||||||
|
|
||||||
// If that failed, try normal auth. Note that we can use normal auth on
|
// If that failed, try normal auth. Note that we can use normal auth on
|
||||||
// LFS requests, so this isn't strictly an alternative to LFS auth.
|
// LFS requests, so this isn't strictly an alternative to LFS auth.
|
||||||
|
@ -655,7 +658,8 @@ final class DiffusionServeController extends DiffusionController {
|
||||||
|
|
||||||
private function authenticateGitLFSUser(
|
private function authenticateGitLFSUser(
|
||||||
$username,
|
$username,
|
||||||
PhutilOpaqueEnvelope $password) {
|
PhutilOpaqueEnvelope $password,
|
||||||
|
$identifier) {
|
||||||
|
|
||||||
// Never accept these credentials for requests which aren't LFS requests.
|
// Never accept these credentials for requests which aren't LFS requests.
|
||||||
if (!$this->getIsGitLFSRequest()) {
|
if (!$this->getIsGitLFSRequest()) {
|
||||||
|
@ -668,11 +672,31 @@ final class DiffusionServeController extends DiffusionController {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See PHI1123. We need to be able to constrain the token query with
|
||||||
|
// "withTokenResources(...)" to take advantage of the key on the table.
|
||||||
|
// In this case, the repository PHID is the "resource" we're after.
|
||||||
|
|
||||||
|
// In normal workflows, we figure out the viewer first, then use the
|
||||||
|
// viewer to load the repository, but that won't work here. Load the
|
||||||
|
// repository as the omnipotent viewer, then use the repository PHID to
|
||||||
|
// look for a token.
|
||||||
|
|
||||||
|
$omnipotent_viewer = PhabricatorUser::getOmnipotentUser();
|
||||||
|
|
||||||
|
$repository = id(new PhabricatorRepositoryQuery())
|
||||||
|
->setViewer($omnipotent_viewer)
|
||||||
|
->withIdentifiers(array($identifier))
|
||||||
|
->executeOne();
|
||||||
|
if (!$repository) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
$lfs_pass = $password->openEnvelope();
|
$lfs_pass = $password->openEnvelope();
|
||||||
$lfs_hash = PhabricatorHash::weakDigest($lfs_pass);
|
$lfs_hash = PhabricatorHash::weakDigest($lfs_pass);
|
||||||
|
|
||||||
$token = id(new PhabricatorAuthTemporaryTokenQuery())
|
$token = id(new PhabricatorAuthTemporaryTokenQuery())
|
||||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
->setViewer($omnipotent_viewer)
|
||||||
|
->withTokenResources(array($repository->getPHID()))
|
||||||
->withTokenTypes(array(DiffusionGitLFSTemporaryTokenType::TOKENTYPE))
|
->withTokenTypes(array(DiffusionGitLFSTemporaryTokenType::TOKENTYPE))
|
||||||
->withTokenCodes(array($lfs_hash))
|
->withTokenCodes(array($lfs_hash))
|
||||||
->withExpired(false)
|
->withExpired(false)
|
||||||
|
@ -682,7 +706,7 @@ final class DiffusionServeController extends DiffusionController {
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = id(new PhabricatorPeopleQuery())
|
$user = id(new PhabricatorPeopleQuery())
|
||||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
->setViewer($omnipotent_viewer)
|
||||||
->withPHIDs(array($token->getUserPHID()))
|
->withPHIDs(array($token->getUserPHID()))
|
||||||
->executeOne();
|
->executeOne();
|
||||||
|
|
||||||
|
|
|
@ -444,13 +444,15 @@ final class DiffusionRepositoryBasicsManagementPanel
|
||||||
id(new PHUIStatusItemView())
|
id(new PHUIStatusItemView())
|
||||||
->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
|
->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')
|
||||||
->setTarget(
|
->setTarget(
|
||||||
pht('Missing Binary %s', phutil_tag('tt', array(), $binary)))
|
pht('Commit Hooks: %s', phutil_tag('tt', array(), $binary)))
|
||||||
->setNote(pht(
|
->setNote(
|
||||||
'Unable to find this binary in `%s`. '.
|
pht(
|
||||||
'You need to configure %s and include %s.',
|
'The directory containing the "svnlook" binary is not '.
|
||||||
'environment.append-paths',
|
'listed in "environment.append-paths", so commit hooks '.
|
||||||
$this->getEnvConfigLink(),
|
'(which execute with an empty "PATH") will not be able to '.
|
||||||
$path)));
|
'find "svnlook". Add `%s` to %s.',
|
||||||
|
$path,
|
||||||
|
$this->getEnvConfigLink())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,9 +127,9 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
|
||||||
// This is suppressing "added <address> to the list of known hosts"
|
// This is suppressing "added <address> to the list of known hosts"
|
||||||
// messages, which are confusing and irrelevant when they arise from
|
// messages, which are confusing and irrelevant when they arise from
|
||||||
// proxied requests. It might also be suppressing lots of useful errors,
|
// proxied requests. It might also be suppressing lots of useful errors,
|
||||||
// of course. Ideally, we would enforce host keys eventually.
|
// of course. Ideally, we would enforce host keys eventually. See T13121.
|
||||||
$options[] = '-o';
|
$options[] = '-o';
|
||||||
$options[] = 'LogLevel=quiet';
|
$options[] = 'LogLevel=ERROR';
|
||||||
|
|
||||||
// NOTE: We prefix the command with "@username", which the far end of the
|
// NOTE: We prefix the command with "@username", which the far end of the
|
||||||
// connection will parse in order to act as the specified user. This
|
// connection will parse in order to act as the specified user. This
|
||||||
|
|
|
@ -30,8 +30,11 @@ final class DrydockSSHCommandInterface extends DrydockCommandInterface {
|
||||||
$full_command = call_user_func_array('csprintf', $argv);
|
$full_command = call_user_func_array('csprintf', $argv);
|
||||||
|
|
||||||
$flags = array();
|
$flags = array();
|
||||||
|
|
||||||
|
// See T13121. Attempt to suppress the "Permanently added X to list of
|
||||||
|
// known hosts" message without suppressing anything important.
|
||||||
$flags[] = '-o';
|
$flags[] = '-o';
|
||||||
$flags[] = 'LogLevel=quiet';
|
$flags[] = 'LogLevel=ERROR';
|
||||||
|
|
||||||
$flags[] = '-o';
|
$flags[] = '-o';
|
||||||
$flags[] = 'StrictHostKeyChecking=no';
|
$flags[] = 'StrictHostKeyChecking=no';
|
||||||
|
|
|
@ -83,6 +83,8 @@ final class PhabricatorHarbormasterApplication extends PhabricatorApplication {
|
||||||
=> 'HarbormasterPlanEditController',
|
=> 'HarbormasterPlanEditController',
|
||||||
'order/(?:(?P<id>\d+)/)?' => 'HarbormasterPlanOrderController',
|
'order/(?:(?P<id>\d+)/)?' => 'HarbormasterPlanOrderController',
|
||||||
'disable/(?P<id>\d+)/' => 'HarbormasterPlanDisableController',
|
'disable/(?P<id>\d+)/' => 'HarbormasterPlanDisableController',
|
||||||
|
'behavior/(?P<id>\d+)/(?P<behaviorKey>[^/]+)/' =>
|
||||||
|
'HarbormasterPlanBehaviorController',
|
||||||
'run/(?P<id>\d+)/' => 'HarbormasterPlanRunController',
|
'run/(?P<id>\d+)/' => 'HarbormasterPlanRunController',
|
||||||
'(?P<id>\d+)/' => 'HarbormasterPlanViewController',
|
'(?P<id>\d+)/' => 'HarbormasterPlanViewController',
|
||||||
),
|
),
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class HarbormasterBuildPlanPolicyCodex
|
||||||
|
extends PhabricatorPolicyCodex {
|
||||||
|
|
||||||
|
public function getPolicySpecialRuleDescriptions() {
|
||||||
|
$object = $this->getObject();
|
||||||
|
$run_with_view = $object->canRunWithoutEditCapability();
|
||||||
|
|
||||||
|
$rules = array();
|
||||||
|
|
||||||
|
$rules[] = $this->newRule()
|
||||||
|
->setCapabilities(
|
||||||
|
array(
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
))
|
||||||
|
->setIsActive(!$run_with_view)
|
||||||
|
->setDescription(
|
||||||
|
pht(
|
||||||
|
'You must have edit permission on this build plan to pause, '.
|
||||||
|
'abort, resume, or restart it.'));
|
||||||
|
|
||||||
|
$rules[] = $this->newRule()
|
||||||
|
->setCapabilities(
|
||||||
|
array(
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
))
|
||||||
|
->setIsActive(!$run_with_view)
|
||||||
|
->setDescription(
|
||||||
|
pht(
|
||||||
|
'You must have edit permission on this build plan to run it '.
|
||||||
|
'manually.'));
|
||||||
|
|
||||||
|
return $rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class HarbormasterBuildPlanEditAPIMethod
|
||||||
|
extends PhabricatorEditEngineAPIMethod {
|
||||||
|
|
||||||
|
public function getAPIMethodName() {
|
||||||
|
return 'harbormaster.buildplan.edit';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newEditEngine() {
|
||||||
|
return new HarbormasterBuildPlanEditEngine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMethodSummary() {
|
||||||
|
return pht(
|
||||||
|
'Apply transactions to create a new build plan or edit an existing '.
|
||||||
|
'one.');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -52,6 +52,10 @@ final class HarbormasterBuildStatus extends Phobject {
|
||||||
return ($this->key === self::STATUS_PASSED);
|
return ($this->key === self::STATUS_PASSED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isFailed() {
|
||||||
|
return ($this->key === self::STATUS_FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a human readable name for a build status constant.
|
* Get a human readable name for a build status constant.
|
||||||
|
|
|
@ -65,13 +65,12 @@ final class HarbormasterBuildActionController
|
||||||
'restart build?');
|
'restart build?');
|
||||||
$submit = pht('Restart Build');
|
$submit = pht('Restart Build');
|
||||||
} else {
|
} else {
|
||||||
$title = pht('Unable to Restart Build');
|
try {
|
||||||
if ($build->isRestarting()) {
|
$build->assertCanRestartBuild();
|
||||||
$body = pht(
|
throw new Exception(pht('Expected to be unable to restart build.'));
|
||||||
'This build is already restarting. You can not reissue a '.
|
} catch (HarbormasterRestartException $ex) {
|
||||||
'restart command to a restarting build.');
|
$title = $ex->getTitle();
|
||||||
} else {
|
$body = $ex->getBody();
|
||||||
$body = pht('You can not restart this build.');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -135,8 +134,7 @@ final class HarbormasterBuildActionController
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$dialog = id(new AphrontDialogView())
|
$dialog = $this->newDialog()
|
||||||
->setUser($viewer)
|
|
||||||
->setTitle($title)
|
->setTitle($title)
|
||||||
->appendChild($body)
|
->appendChild($body)
|
||||||
->addCancelButton($return_uri);
|
->addCancelButton($return_uri);
|
||||||
|
@ -145,7 +143,7 @@ final class HarbormasterBuildActionController
|
||||||
$dialog->addSubmitButton($submit);
|
$dialog->addSubmitButton($submit);
|
||||||
}
|
}
|
||||||
|
|
||||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
return $dialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class HarbormasterPlanBehaviorController
|
||||||
|
extends HarbormasterPlanController {
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$plan = id(new HarbormasterBuildPlanQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(array($request->getURIData('id')))
|
||||||
|
->requireCapabilities(
|
||||||
|
array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
))
|
||||||
|
->executeOne();
|
||||||
|
if (!$plan) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$behavior_key = $request->getURIData('behaviorKey');
|
||||||
|
$metadata_key = HarbormasterBuildPlanBehavior::getTransactionMetadataKey();
|
||||||
|
|
||||||
|
$behaviors = HarbormasterBuildPlanBehavior::newPlanBehaviors();
|
||||||
|
$behavior = idx($behaviors, $behavior_key);
|
||||||
|
if (!$behavior) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$plan_uri = $plan->getURI();
|
||||||
|
|
||||||
|
$v_option = $behavior->getPlanOption($plan)->getKey();
|
||||||
|
if ($request->isFormPost()) {
|
||||||
|
$v_option = $request->getStr('option');
|
||||||
|
|
||||||
|
$xactions = array();
|
||||||
|
|
||||||
|
$xactions[] = id(new HarbormasterBuildPlanTransaction())
|
||||||
|
->setTransactionType(
|
||||||
|
HarbormasterBuildPlanBehaviorTransaction::TRANSACTIONTYPE)
|
||||||
|
->setMetadataValue($metadata_key, $behavior_key)
|
||||||
|
->setNewValue($v_option);
|
||||||
|
|
||||||
|
$editor = id(new HarbormasterBuildPlanEditor())
|
||||||
|
->setActor($viewer)
|
||||||
|
->setContinueOnNoEffect(true)
|
||||||
|
->setContinueOnMissingFields(true)
|
||||||
|
->setContentSourceFromRequest($request);
|
||||||
|
|
||||||
|
$editor->applyTransactions($plan, $xactions);
|
||||||
|
|
||||||
|
return id(new AphrontRedirectResponse())->setURI($plan_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
$select_control = id(new AphrontFormRadioButtonControl())
|
||||||
|
->setName('option')
|
||||||
|
->setValue($v_option)
|
||||||
|
->setLabel(pht('Option'));
|
||||||
|
|
||||||
|
foreach ($behavior->getOptions() as $option) {
|
||||||
|
$icon = id(new PHUIIconView())
|
||||||
|
->setIcon($option->getIcon());
|
||||||
|
|
||||||
|
$select_control->addButton(
|
||||||
|
$option->getKey(),
|
||||||
|
array(
|
||||||
|
$icon,
|
||||||
|
' ',
|
||||||
|
$option->getName(),
|
||||||
|
),
|
||||||
|
$option->getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
$form = id(new AphrontFormView())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->appendInstructions(
|
||||||
|
pht(
|
||||||
|
'Choose a build plan behavior for "%s".',
|
||||||
|
phutil_tag('strong', array(), $behavior->getName())))
|
||||||
|
->appendRemarkupInstructions($behavior->getEditInstructions())
|
||||||
|
->appendControl($select_control);
|
||||||
|
|
||||||
|
return $this->newDialog()
|
||||||
|
->setTitle(pht('Edit Behavior: %s', $behavior->getName()))
|
||||||
|
->appendForm($form)
|
||||||
|
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||||
|
->addSubmitButton(pht('Save Changes'))
|
||||||
|
->addCancelButton($plan_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -19,11 +19,11 @@ final class HarbormasterPlanDisableController
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
$plan_uri = $this->getApplicationURI('plan/'.$plan->getID().'/');
|
$plan_uri = $plan->getURI();
|
||||||
|
|
||||||
if ($request->isFormPost()) {
|
if ($request->isFormPost()) {
|
||||||
|
|
||||||
$type_status = HarbormasterBuildPlanTransaction::TYPE_STATUS;
|
$type_status = HarbormasterBuildPlanStatusTransaction::TRANSACTIONTYPE;
|
||||||
|
|
||||||
$v_status = $plan->isDisabled()
|
$v_status = $plan->isDisabled()
|
||||||
? HarbormasterBuildPlan::STATUS_ACTIVE
|
? HarbormasterBuildPlan::STATUS_ACTIVE
|
||||||
|
|
|
@ -9,16 +9,13 @@ final class HarbormasterPlanRunController extends HarbormasterPlanController {
|
||||||
$plan = id(new HarbormasterBuildPlanQuery())
|
$plan = id(new HarbormasterBuildPlanQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withIDs(array($plan_id))
|
->withIDs(array($plan_id))
|
||||||
->requireCapabilities(
|
|
||||||
array(
|
|
||||||
PhabricatorPolicyCapability::CAN_VIEW,
|
|
||||||
PhabricatorPolicyCapability::CAN_EDIT,
|
|
||||||
))
|
|
||||||
->executeOne();
|
->executeOne();
|
||||||
if (!$plan) {
|
if (!$plan) {
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$plan->assertHasRunCapability($viewer);
|
||||||
|
|
||||||
$cancel_uri = $this->getApplicationURI("plan/{$plan_id}/");
|
$cancel_uri = $this->getApplicationURI("plan/{$plan_id}/");
|
||||||
|
|
||||||
if (!$plan->canRunManually()) {
|
if (!$plan->canRunManually()) {
|
||||||
|
|
|
@ -18,11 +18,6 @@ final class HarbormasterPlanViewController extends HarbormasterPlanController {
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
$timeline = $this->buildTransactionTimeline(
|
|
||||||
$plan,
|
|
||||||
new HarbormasterBuildPlanTransactionQuery());
|
|
||||||
$timeline->setShouldTerminate(true);
|
|
||||||
|
|
||||||
$title = $plan->getName();
|
$title = $plan->getName();
|
||||||
|
|
||||||
$header = id(new PHUIHeaderView())
|
$header = id(new PHUIHeaderView())
|
||||||
|
@ -33,24 +28,30 @@ final class HarbormasterPlanViewController extends HarbormasterPlanController {
|
||||||
|
|
||||||
$curtain = $this->buildCurtainView($plan);
|
$curtain = $this->buildCurtainView($plan);
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
$crumbs = $this->buildApplicationCrumbs()
|
||||||
$crumbs->addTextCrumb(pht('Plan %d', $id));
|
->addTextCrumb($plan->getObjectName())
|
||||||
$crumbs->setBorder(true);
|
->setBorder(true);
|
||||||
|
|
||||||
list($step_list, $has_any_conflicts, $would_deadlock) =
|
list($step_list, $has_any_conflicts, $would_deadlock, $steps) =
|
||||||
$this->buildStepList($plan);
|
$this->buildStepList($plan);
|
||||||
|
|
||||||
$error = null;
|
$error = null;
|
||||||
if ($would_deadlock) {
|
if (!$steps) {
|
||||||
$error = pht('This build plan will deadlock when executed, due to '.
|
$error = pht(
|
||||||
'circular dependencies present in the build plan. '.
|
'This build plan does not have any build steps yet, so it will '.
|
||||||
'Examine the step list and resolve the deadlock.');
|
'not do anything when run.');
|
||||||
|
} else if ($would_deadlock) {
|
||||||
|
$error = pht(
|
||||||
|
'This build plan will deadlock when executed, due to circular '.
|
||||||
|
'dependencies present in the build plan. Examine the step list '.
|
||||||
|
'and resolve the deadlock.');
|
||||||
} else if ($has_any_conflicts) {
|
} else if ($has_any_conflicts) {
|
||||||
// A deadlocking build will also cause all the artifacts to be
|
// A deadlocking build will also cause all the artifacts to be
|
||||||
// invalid, so we just skip showing this message if that's the
|
// invalid, so we just skip showing this message if that's the
|
||||||
// case.
|
// case.
|
||||||
$error = pht('This build plan has conflicts in one or more build steps. '.
|
$error = pht(
|
||||||
'Examine the step list and resolve the listed errors.');
|
'This build plan has conflicts in one or more build steps. '.
|
||||||
|
'Examine the step list and resolve the listed errors.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($error) {
|
if ($error) {
|
||||||
|
@ -59,18 +60,32 @@ final class HarbormasterPlanViewController extends HarbormasterPlanController {
|
||||||
->appendChild($error);
|
->appendChild($error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$builds_view = $this->newBuildsView($plan);
|
||||||
|
$options_view = $this->newOptionsView($plan);
|
||||||
|
$rules_view = $this->newRulesView($plan);
|
||||||
|
|
||||||
|
$timeline = $this->buildTransactionTimeline(
|
||||||
|
$plan,
|
||||||
|
new HarbormasterBuildPlanTransactionQuery());
|
||||||
|
$timeline->setShouldTerminate(true);
|
||||||
|
|
||||||
$view = id(new PHUITwoColumnView())
|
$view = id(new PHUITwoColumnView())
|
||||||
->setHeader($header)
|
->setHeader($header)
|
||||||
->setCurtain($curtain)
|
->setCurtain($curtain)
|
||||||
->setMainColumn(array(
|
->setMainColumn(
|
||||||
$error,
|
array(
|
||||||
$step_list,
|
$error,
|
||||||
$timeline,
|
$step_list,
|
||||||
));
|
$options_view,
|
||||||
|
$rules_view,
|
||||||
|
$builds_view,
|
||||||
|
$timeline,
|
||||||
|
));
|
||||||
|
|
||||||
return $this->newPage()
|
return $this->newPage()
|
||||||
->setTitle($title)
|
->setTitle($title)
|
||||||
->setCrumbs($crumbs)
|
->setCrumbs($crumbs)
|
||||||
|
->setPageObjectPHIDs(array($plan->getPHID()))
|
||||||
->appendChild($view);
|
->appendChild($view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,7 +228,7 @@ final class HarbormasterPlanViewController extends HarbormasterPlanController {
|
||||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||||
->appendChild($step_list);
|
->appendChild($step_list);
|
||||||
|
|
||||||
return array($step_box, $has_any_conflicts, $is_deadlocking);
|
return array($step_box, $has_any_conflicts, $is_deadlocking, $steps);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildCurtainView(HarbormasterBuildPlan $plan) {
|
private function buildCurtainView(HarbormasterBuildPlan $plan) {
|
||||||
|
@ -253,7 +268,7 @@ final class HarbormasterPlanViewController extends HarbormasterPlanController {
|
||||||
->setIcon('fa-ban'));
|
->setIcon('fa-ban'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$can_run = ($can_edit && $plan->canRunManually());
|
$can_run = ($plan->hasRunCapability($viewer) && $plan->canRunManually());
|
||||||
|
|
||||||
$curtain->addAction(
|
$curtain->addAction(
|
||||||
id(new PhabricatorActionView())
|
id(new PhabricatorActionView())
|
||||||
|
@ -263,11 +278,6 @@ final class HarbormasterPlanViewController extends HarbormasterPlanController {
|
||||||
->setDisabled(!$can_run)
|
->setDisabled(!$can_run)
|
||||||
->setIcon('fa-play-circle'));
|
->setIcon('fa-play-circle'));
|
||||||
|
|
||||||
$curtain->addPanel(
|
|
||||||
id(new PHUICurtainPanelView())
|
|
||||||
->setHeaderText(pht('Created'))
|
|
||||||
->appendChild(phabricator_datetime($plan->getDateCreated(), $viewer)));
|
|
||||||
|
|
||||||
return $curtain;
|
return $curtain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,7 +391,7 @@ final class HarbormasterPlanViewController extends HarbormasterPlanController {
|
||||||
array $steps) {
|
array $steps) {
|
||||||
$has_conflicts = false;
|
$has_conflicts = false;
|
||||||
|
|
||||||
if (count($step_phids) === 0) {
|
if (!$step_phids) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,4 +451,149 @@ final class HarbormasterPlanViewController extends HarbormasterPlanController {
|
||||||
|
|
||||||
return array($ui, $has_conflicts);
|
return array($ui, $has_conflicts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function newBuildsView(HarbormasterBuildPlan $plan) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$builds = id(new HarbormasterBuildQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withBuildPlanPHIDs(array($plan->getPHID()))
|
||||||
|
->setLimit(10)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$list = id(new HarbormasterBuildView())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->setBuilds($builds)
|
||||||
|
->newObjectList();
|
||||||
|
|
||||||
|
$list->setNoDataString(pht('No recent builds.'));
|
||||||
|
|
||||||
|
$more_href = new PhutilURI(
|
||||||
|
$this->getApplicationURI('/build/'),
|
||||||
|
array('plan' => $plan->getPHID()));
|
||||||
|
|
||||||
|
$more_link = id(new PHUIButtonView())
|
||||||
|
->setTag('a')
|
||||||
|
->setIcon('fa-list-ul')
|
||||||
|
->setText(pht('View All Builds'))
|
||||||
|
->setHref($more_href);
|
||||||
|
|
||||||
|
$header = id(new PHUIHeaderView())
|
||||||
|
->setHeader(pht('Recent Builds'))
|
||||||
|
->addActionLink($more_link);
|
||||||
|
|
||||||
|
return id(new PHUIObjectBoxView())
|
||||||
|
->setHeader($header)
|
||||||
|
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||||
|
->appendChild($list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newRulesView(HarbormasterBuildPlan $plan) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$rules = id(new HeraldRuleQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withDisabled(false)
|
||||||
|
->withAffectedObjectPHIDs(array($plan->getPHID()))
|
||||||
|
->needValidateAuthors(true)
|
||||||
|
->setLimit(10)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$list = id(new HeraldRuleListView())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->setRules($rules)
|
||||||
|
->newObjectList();
|
||||||
|
|
||||||
|
$list->setNoDataString(pht('No active Herald rules trigger this build.'));
|
||||||
|
|
||||||
|
$more_href = new PhutilURI(
|
||||||
|
'/herald/',
|
||||||
|
array('affectedPHID' => $plan->getPHID()));
|
||||||
|
|
||||||
|
$more_link = id(new PHUIButtonView())
|
||||||
|
->setTag('a')
|
||||||
|
->setIcon('fa-list-ul')
|
||||||
|
->setText(pht('View All Rules'))
|
||||||
|
->setHref($more_href);
|
||||||
|
|
||||||
|
$header = id(new PHUIHeaderView())
|
||||||
|
->setHeader(pht('Run By Herald Rules'))
|
||||||
|
->addActionLink($more_link);
|
||||||
|
|
||||||
|
return id(new PHUIObjectBoxView())
|
||||||
|
->setHeader($header)
|
||||||
|
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||||
|
->appendChild($list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newOptionsView(HarbormasterBuildPlan $plan) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||||
|
$viewer,
|
||||||
|
$plan,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT);
|
||||||
|
|
||||||
|
$behaviors = HarbormasterBuildPlanBehavior::newPlanBehaviors();
|
||||||
|
|
||||||
|
$rows = array();
|
||||||
|
foreach ($behaviors as $behavior) {
|
||||||
|
$option = $behavior->getPlanOption($plan);
|
||||||
|
|
||||||
|
$icon = $option->getIcon();
|
||||||
|
$icon = id(new PHUIIconView())->setIcon($icon);
|
||||||
|
|
||||||
|
$edit_uri = new PhutilURI(
|
||||||
|
$this->getApplicationURI(
|
||||||
|
urisprintf(
|
||||||
|
'plan/behavior/%d/%s/',
|
||||||
|
$plan->getID(),
|
||||||
|
$behavior->getKey())));
|
||||||
|
|
||||||
|
$edit_button = id(new PHUIButtonView())
|
||||||
|
->setTag('a')
|
||||||
|
->setColor(PHUIButtonView::GREY)
|
||||||
|
->setSize(PHUIButtonView::SMALL)
|
||||||
|
->setDisabled(!$can_edit)
|
||||||
|
->setWorkflow(true)
|
||||||
|
->setText(pht('Edit'))
|
||||||
|
->setHref($edit_uri);
|
||||||
|
|
||||||
|
$rows[] = array(
|
||||||
|
$icon,
|
||||||
|
$behavior->getName(),
|
||||||
|
$option->getName(),
|
||||||
|
$option->getDescription(),
|
||||||
|
$edit_button,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = id(new AphrontTableView($rows))
|
||||||
|
->setHeaders(
|
||||||
|
array(
|
||||||
|
null,
|
||||||
|
pht('Name'),
|
||||||
|
pht('Behavior'),
|
||||||
|
pht('Details'),
|
||||||
|
null,
|
||||||
|
))
|
||||||
|
->setColumnClasses(
|
||||||
|
array(
|
||||||
|
null,
|
||||||
|
'pri',
|
||||||
|
null,
|
||||||
|
'wide',
|
||||||
|
null,
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
$header = id(new PHUIHeaderView())
|
||||||
|
->setHeader(pht('Plan Behaviors'));
|
||||||
|
|
||||||
|
return id(new PHUIObjectBoxView())
|
||||||
|
->setHeader($header)
|
||||||
|
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||||
|
->setTable($table);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,17 +77,48 @@ final class HarbormasterBuildPlanEditEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildCustomEditFields($object) {
|
protected function buildCustomEditFields($object) {
|
||||||
return array(
|
$fields = array(
|
||||||
id(new PhabricatorTextEditField())
|
id(new PhabricatorTextEditField())
|
||||||
->setKey('name')
|
->setKey('name')
|
||||||
->setLabel(pht('Name'))
|
->setLabel(pht('Name'))
|
||||||
->setIsRequired(true)
|
->setIsRequired(true)
|
||||||
->setTransactionType(HarbormasterBuildPlanTransaction::TYPE_NAME)
|
->setTransactionType(
|
||||||
|
HarbormasterBuildPlanNameTransaction::TRANSACTIONTYPE)
|
||||||
->setDescription(pht('The build plan name.'))
|
->setDescription(pht('The build plan name.'))
|
||||||
->setConduitDescription(pht('Rename the plan.'))
|
->setConduitDescription(pht('Rename the plan.'))
|
||||||
->setConduitTypeDescription(pht('New plan name.'))
|
->setConduitTypeDescription(pht('New plan name.'))
|
||||||
->setValue($object->getName()),
|
->setValue($object->getName()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
$metadata_key = HarbormasterBuildPlanBehavior::getTransactionMetadataKey();
|
||||||
|
|
||||||
|
$behaviors = HarbormasterBuildPlanBehavior::newPlanBehaviors();
|
||||||
|
foreach ($behaviors as $behavior) {
|
||||||
|
$key = $behavior->getKey();
|
||||||
|
|
||||||
|
// Get the raw key off the object so that we don't reset stuff to
|
||||||
|
// default values by mistake if a behavior goes missing somehow.
|
||||||
|
$storage_key = HarbormasterBuildPlanBehavior::getStorageKeyForBehaviorKey(
|
||||||
|
$key);
|
||||||
|
$behavior_option = $object->getPlanProperty($storage_key);
|
||||||
|
|
||||||
|
if (!strlen($behavior_option)) {
|
||||||
|
$behavior_option = $behavior->getPlanOption($object)->getKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields[] = id(new PhabricatorSelectEditField())
|
||||||
|
->setIsFormField(false)
|
||||||
|
->setKey(sprintf('behavior.%s', $behavior->getKey()))
|
||||||
|
->setMetadataValue($metadata_key, $behavior->getKey())
|
||||||
|
->setLabel(pht('Behavior: %s', $behavior->getName()))
|
||||||
|
->setTransactionType(
|
||||||
|
HarbormasterBuildPlanBehaviorTransaction::TRANSACTIONTYPE)
|
||||||
|
->setValue($behavior_option)
|
||||||
|
->setOptions($behavior->getOptionMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,100 +11,23 @@ final class HarbormasterBuildPlanEditor
|
||||||
return pht('Harbormaster Build Plans');
|
return pht('Harbormaster Build Plans');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getCreateObjectTitle($author, $object) {
|
||||||
|
return pht('%s created this build plan.', $author);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCreateObjectTitleForFeed($author, $object) {
|
||||||
|
return pht('%s created %s.', $author, $object);
|
||||||
|
}
|
||||||
|
|
||||||
protected function supportsSearch() {
|
protected function supportsSearch() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTransactionTypes() {
|
public function getTransactionTypes() {
|
||||||
$types = parent::getTransactionTypes();
|
$types = parent::getTransactionTypes();
|
||||||
$types[] = HarbormasterBuildPlanTransaction::TYPE_NAME;
|
|
||||||
$types[] = HarbormasterBuildPlanTransaction::TYPE_STATUS;
|
|
||||||
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||||
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||||
return $types;
|
return $types;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getCustomTransactionOldValue(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
|
||||||
switch ($xaction->getTransactionType()) {
|
|
||||||
case HarbormasterBuildPlanTransaction::TYPE_NAME:
|
|
||||||
if ($this->getIsNewObject()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return $object->getName();
|
|
||||||
case HarbormasterBuildPlanTransaction::TYPE_STATUS:
|
|
||||||
return $object->getPlanStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::getCustomTransactionOldValue($object, $xaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getCustomTransactionNewValue(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
|
||||||
switch ($xaction->getTransactionType()) {
|
|
||||||
case HarbormasterBuildPlanTransaction::TYPE_NAME:
|
|
||||||
return $xaction->getNewValue();
|
|
||||||
case HarbormasterBuildPlanTransaction::TYPE_STATUS:
|
|
||||||
return $xaction->getNewValue();
|
|
||||||
}
|
|
||||||
return parent::getCustomTransactionNewValue($object, $xaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyCustomInternalTransaction(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
|
||||||
switch ($xaction->getTransactionType()) {
|
|
||||||
case HarbormasterBuildPlanTransaction::TYPE_NAME:
|
|
||||||
$object->setName($xaction->getNewValue());
|
|
||||||
return;
|
|
||||||
case HarbormasterBuildPlanTransaction::TYPE_STATUS:
|
|
||||||
$object->setPlanStatus($xaction->getNewValue());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return parent::applyCustomInternalTransaction($object, $xaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyCustomExternalTransaction(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
|
||||||
switch ($xaction->getTransactionType()) {
|
|
||||||
case HarbormasterBuildPlanTransaction::TYPE_NAME:
|
|
||||||
case HarbormasterBuildPlanTransaction::TYPE_STATUS:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return parent::applyCustomExternalTransaction($object, $xaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function validateTransaction(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
$type,
|
|
||||||
array $xactions) {
|
|
||||||
|
|
||||||
$errors = parent::validateTransaction($object, $type, $xactions);
|
|
||||||
|
|
||||||
switch ($type) {
|
|
||||||
case HarbormasterBuildPlanTransaction::TYPE_NAME:
|
|
||||||
$missing = $this->validateIsEmptyTextField(
|
|
||||||
$object->getName(),
|
|
||||||
$xactions);
|
|
||||||
|
|
||||||
if ($missing) {
|
|
||||||
$error = new PhabricatorApplicationTransactionValidationError(
|
|
||||||
$type,
|
|
||||||
pht('Required'),
|
|
||||||
pht('You must choose a name for your build plan.'),
|
|
||||||
last($xactions));
|
|
||||||
|
|
||||||
$error->setIsMissingFieldError(true);
|
|
||||||
$errors[] = $error;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -497,9 +497,33 @@ final class HarbormasterBuildEngine extends Phobject {
|
||||||
// passed everything it needs to.
|
// passed everything it needs to.
|
||||||
|
|
||||||
if (!$buildable->isPreparing()) {
|
if (!$buildable->isPreparing()) {
|
||||||
|
$behavior_key = HarbormasterBuildPlanBehavior::BEHAVIOR_BUILDABLE;
|
||||||
|
$behavior = HarbormasterBuildPlanBehavior::getBehavior($behavior_key);
|
||||||
|
|
||||||
|
$key_never = HarbormasterBuildPlanBehavior::BUILDABLE_NEVER;
|
||||||
|
$key_building = HarbormasterBuildPlanBehavior::BUILDABLE_IF_BUILDING;
|
||||||
|
|
||||||
$all_pass = true;
|
$all_pass = true;
|
||||||
$any_fail = false;
|
$any_fail = false;
|
||||||
foreach ($buildable->getBuilds() as $build) {
|
foreach ($buildable->getBuilds() as $build) {
|
||||||
|
$plan = $build->getBuildPlan();
|
||||||
|
$option = $behavior->getPlanOption($plan);
|
||||||
|
$option_key = $option->getKey();
|
||||||
|
|
||||||
|
$is_never = ($option_key === $key_never);
|
||||||
|
$is_building = ($option_key === $key_building);
|
||||||
|
|
||||||
|
// If this build "Never" affects the buildable, ignore it.
|
||||||
|
if ($is_never) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this build affects the buildable "If Building", but is already
|
||||||
|
// complete, ignore it.
|
||||||
|
if ($is_building && $build->isComplete()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!$build->isPassed()) {
|
if (!$build->isPassed()) {
|
||||||
$all_pass = false;
|
$all_pass = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class HarbormasterRestartException extends Exception {
|
||||||
|
|
||||||
|
private $title;
|
||||||
|
private $body = array();
|
||||||
|
|
||||||
|
public function __construct($title, $body = null) {
|
||||||
|
$this->setTitle($title);
|
||||||
|
$this->appendParagraph($body);
|
||||||
|
|
||||||
|
parent::__construct($title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTitle($title) {
|
||||||
|
$this->title = $title;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle() {
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function appendParagraph($description) {
|
||||||
|
$this->body[] = $description;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBody() {
|
||||||
|
return $this->body;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -91,4 +91,9 @@ final class HarbormasterRunBuildPlansHeraldAction
|
||||||
'Run build plans: %s.',
|
'Run build plans: %s.',
|
||||||
$this->renderHandleList($value));
|
$this->renderHandleList($value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getPHIDsAffectedByAction(HeraldActionRecord $record) {
|
||||||
|
return $record->getTarget();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,394 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class HarbormasterBuildPlanBehavior
|
||||||
|
extends Phobject {
|
||||||
|
|
||||||
|
private $key;
|
||||||
|
private $name;
|
||||||
|
private $options;
|
||||||
|
private $defaultKey;
|
||||||
|
private $editInstructions;
|
||||||
|
|
||||||
|
const BEHAVIOR_RUNNABLE = 'runnable';
|
||||||
|
const RUNNABLE_IF_VIEWABLE = 'view';
|
||||||
|
const RUNNABLE_IF_EDITABLE = 'edit';
|
||||||
|
|
||||||
|
const BEHAVIOR_RESTARTABLE = 'restartable';
|
||||||
|
const RESTARTABLE_ALWAYS = 'always';
|
||||||
|
const RESTARTABLE_IF_FAILED = 'failed';
|
||||||
|
const RESTARTABLE_NEVER = 'never';
|
||||||
|
|
||||||
|
const BEHAVIOR_DRAFTS = 'hold-drafts';
|
||||||
|
const DRAFTS_ALWAYS = 'always';
|
||||||
|
const DRAFTS_IF_BUILDING = 'building';
|
||||||
|
const DRAFTS_NEVER = 'never';
|
||||||
|
|
||||||
|
const BEHAVIOR_BUILDABLE = 'buildable';
|
||||||
|
const BUILDABLE_ALWAYS = 'always';
|
||||||
|
const BUILDABLE_IF_BUILDING = 'building';
|
||||||
|
const BUILDABLE_NEVER = 'never';
|
||||||
|
|
||||||
|
const BEHAVIOR_LANDWARNING = 'arc-land';
|
||||||
|
const LANDWARNING_ALWAYS = 'always';
|
||||||
|
const LANDWARNING_IF_BUILDING = 'building';
|
||||||
|
const LANDWARNING_IF_COMPLETE = 'complete';
|
||||||
|
const LANDWARNING_NEVER = 'never';
|
||||||
|
|
||||||
|
public function setKey($key) {
|
||||||
|
$this->key = $key;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKey() {
|
||||||
|
return $this->key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setName($name) {
|
||||||
|
$this->name = $name;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName() {
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setEditInstructions($edit_instructions) {
|
||||||
|
$this->editInstructions = $edit_instructions;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEditInstructions() {
|
||||||
|
return $this->editInstructions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOptionMap() {
|
||||||
|
return mpull($this->options, 'getName', 'getKey');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setOptions(array $options) {
|
||||||
|
assert_instances_of($options, 'HarbormasterBuildPlanBehaviorOption');
|
||||||
|
|
||||||
|
$key_map = array();
|
||||||
|
$default = null;
|
||||||
|
|
||||||
|
foreach ($options as $option) {
|
||||||
|
$key = $option->getKey();
|
||||||
|
|
||||||
|
if (isset($key_map[$key])) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Multiple behavior options (for behavior "%s") have the same '.
|
||||||
|
'key ("%s"). Each option must have a unique key.',
|
||||||
|
$this->getKey(),
|
||||||
|
$key));
|
||||||
|
}
|
||||||
|
$key_map[$key] = true;
|
||||||
|
|
||||||
|
if ($option->getIsDefault()) {
|
||||||
|
if ($default === null) {
|
||||||
|
$default = $key;
|
||||||
|
} else {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Multiple behavior options (for behavior "%s") are marked as '.
|
||||||
|
'default options ("%s" and "%s"). Exactly one option must be '.
|
||||||
|
'marked as the default option.',
|
||||||
|
$this->getKey(),
|
||||||
|
$default,
|
||||||
|
$key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($default === null) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'No behavior option is marked as the default option (for '.
|
||||||
|
'behavior "%s"). Exactly one option must be marked as the '.
|
||||||
|
'default option.',
|
||||||
|
$this->getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->options = mpull($options, null, 'getKey');
|
||||||
|
$this->defaultKey = $default;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOptions() {
|
||||||
|
return $this->options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlanOption(HarbormasterBuildPlan $plan) {
|
||||||
|
$behavior_key = $this->getKey();
|
||||||
|
$storage_key = self::getStorageKeyForBehaviorKey($behavior_key);
|
||||||
|
|
||||||
|
$plan_value = $plan->getPlanProperty($storage_key);
|
||||||
|
if (isset($this->options[$plan_value])) {
|
||||||
|
return $this->options[$plan_value];
|
||||||
|
}
|
||||||
|
|
||||||
|
return idx($this->options, $this->defaultKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getTransactionMetadataKey() {
|
||||||
|
return 'behavior-key';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getStorageKeyForBehaviorKey($behavior_key) {
|
||||||
|
return sprintf('behavior.%s', $behavior_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getBehavior($key) {
|
||||||
|
$behaviors = self::newPlanBehaviors();
|
||||||
|
|
||||||
|
if (!isset($behaviors[$key])) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'No build plan behavior with key "%s" exists.',
|
||||||
|
$key));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $behaviors[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function newPlanBehaviors() {
|
||||||
|
$draft_options = array(
|
||||||
|
id(new HarbormasterBuildPlanBehaviorOption())
|
||||||
|
->setKey(self::DRAFTS_ALWAYS)
|
||||||
|
->setIcon('fa-check-circle-o green')
|
||||||
|
->setName(pht('Always'))
|
||||||
|
->setIsDefault(true)
|
||||||
|
->setDescription(
|
||||||
|
pht(
|
||||||
|
'Revisions are not sent for review until the build completes, '.
|
||||||
|
'and are returned to the author for updates if the build fails.')),
|
||||||
|
id(new HarbormasterBuildPlanBehaviorOption())
|
||||||
|
->setKey(self::DRAFTS_IF_BUILDING)
|
||||||
|
->setIcon('fa-pause-circle-o yellow')
|
||||||
|
->setName(pht('If Building'))
|
||||||
|
->setDescription(
|
||||||
|
pht(
|
||||||
|
'Revisions are not sent for review until the build completes, '.
|
||||||
|
'but they will be sent for review even if it fails.')),
|
||||||
|
id(new HarbormasterBuildPlanBehaviorOption())
|
||||||
|
->setKey(self::DRAFTS_NEVER)
|
||||||
|
->setIcon('fa-circle-o red')
|
||||||
|
->setName(pht('Never'))
|
||||||
|
->setDescription(
|
||||||
|
pht(
|
||||||
|
'Revisions are sent for review regardless of the status of the '.
|
||||||
|
'build.')),
|
||||||
|
);
|
||||||
|
|
||||||
|
$land_options = array(
|
||||||
|
id(new HarbormasterBuildPlanBehaviorOption())
|
||||||
|
->setKey(self::LANDWARNING_ALWAYS)
|
||||||
|
->setIcon('fa-check-circle-o green')
|
||||||
|
->setName(pht('Always'))
|
||||||
|
->setIsDefault(true)
|
||||||
|
->setDescription(
|
||||||
|
pht(
|
||||||
|
'"arc land" warns if the build is still running or has '.
|
||||||
|
'failed.')),
|
||||||
|
id(new HarbormasterBuildPlanBehaviorOption())
|
||||||
|
->setKey(self::LANDWARNING_IF_BUILDING)
|
||||||
|
->setIcon('fa-pause-circle-o yellow')
|
||||||
|
->setName(pht('If Building'))
|
||||||
|
->setDescription(
|
||||||
|
pht(
|
||||||
|
'"arc land" warns if the build is still running, but ignores '.
|
||||||
|
'the build if it has failed.')),
|
||||||
|
id(new HarbormasterBuildPlanBehaviorOption())
|
||||||
|
->setKey(self::LANDWARNING_IF_COMPLETE)
|
||||||
|
->setIcon('fa-dot-circle-o yellow')
|
||||||
|
->setName(pht('If Complete'))
|
||||||
|
->setDescription(
|
||||||
|
pht(
|
||||||
|
'"arc land" warns if the build has failed, but ignores the '.
|
||||||
|
'build if it is still running.')),
|
||||||
|
id(new HarbormasterBuildPlanBehaviorOption())
|
||||||
|
->setKey(self::LANDWARNING_NEVER)
|
||||||
|
->setIcon('fa-circle-o red')
|
||||||
|
->setName(pht('Never'))
|
||||||
|
->setDescription(
|
||||||
|
pht(
|
||||||
|
'"arc land" never warns that the build is still running or '.
|
||||||
|
'has failed.')),
|
||||||
|
);
|
||||||
|
|
||||||
|
$aggregate_options = array(
|
||||||
|
id(new HarbormasterBuildPlanBehaviorOption())
|
||||||
|
->setKey(self::BUILDABLE_ALWAYS)
|
||||||
|
->setIcon('fa-check-circle-o green')
|
||||||
|
->setName(pht('Always'))
|
||||||
|
->setIsDefault(true)
|
||||||
|
->setDescription(
|
||||||
|
pht(
|
||||||
|
'The buildable waits for the build, and fails if the '.
|
||||||
|
'build fails.')),
|
||||||
|
id(new HarbormasterBuildPlanBehaviorOption())
|
||||||
|
->setKey(self::BUILDABLE_IF_BUILDING)
|
||||||
|
->setIcon('fa-pause-circle-o yellow')
|
||||||
|
->setName(pht('If Building'))
|
||||||
|
->setDescription(
|
||||||
|
pht(
|
||||||
|
'The buildable waits for the build, but does not fail '.
|
||||||
|
'if the build fails.')),
|
||||||
|
id(new HarbormasterBuildPlanBehaviorOption())
|
||||||
|
->setKey(self::BUILDABLE_NEVER)
|
||||||
|
->setIcon('fa-circle-o red')
|
||||||
|
->setName(pht('Never'))
|
||||||
|
->setDescription(
|
||||||
|
pht(
|
||||||
|
'The buildable does not wait for the build.')),
|
||||||
|
);
|
||||||
|
|
||||||
|
$restart_options = array(
|
||||||
|
id(new HarbormasterBuildPlanBehaviorOption())
|
||||||
|
->setKey(self::RESTARTABLE_ALWAYS)
|
||||||
|
->setIcon('fa-repeat green')
|
||||||
|
->setName(pht('Always'))
|
||||||
|
->setIsDefault(true)
|
||||||
|
->setDescription(
|
||||||
|
pht('The build may be restarted.')),
|
||||||
|
id(new HarbormasterBuildPlanBehaviorOption())
|
||||||
|
->setKey(self::RESTARTABLE_IF_FAILED)
|
||||||
|
->setIcon('fa-times-circle-o yellow')
|
||||||
|
->setName(pht('If Failed'))
|
||||||
|
->setDescription(
|
||||||
|
pht('The build may be restarted if it has failed.')),
|
||||||
|
id(new HarbormasterBuildPlanBehaviorOption())
|
||||||
|
->setKey(self::RESTARTABLE_NEVER)
|
||||||
|
->setIcon('fa-times red')
|
||||||
|
->setName(pht('Never'))
|
||||||
|
->setDescription(
|
||||||
|
pht('The build may not be restarted.')),
|
||||||
|
);
|
||||||
|
|
||||||
|
$run_options = array(
|
||||||
|
id(new HarbormasterBuildPlanBehaviorOption())
|
||||||
|
->setKey(self::RUNNABLE_IF_EDITABLE)
|
||||||
|
->setIcon('fa-pencil green')
|
||||||
|
->setName(pht('If Editable'))
|
||||||
|
->setIsDefault(true)
|
||||||
|
->setDescription(
|
||||||
|
pht('Only users who can edit the plan can run it manually.')),
|
||||||
|
id(new HarbormasterBuildPlanBehaviorOption())
|
||||||
|
->setKey(self::RUNNABLE_IF_VIEWABLE)
|
||||||
|
->setIcon('fa-exclamation-triangle yellow')
|
||||||
|
->setName(pht('If Viewable'))
|
||||||
|
->setDescription(
|
||||||
|
pht(
|
||||||
|
'Any user who can view the plan can run it manually.')),
|
||||||
|
);
|
||||||
|
|
||||||
|
$behaviors = array(
|
||||||
|
id(new self())
|
||||||
|
->setKey(self::BEHAVIOR_DRAFTS)
|
||||||
|
->setName(pht('Hold Drafts'))
|
||||||
|
->setEditInstructions(
|
||||||
|
pht(
|
||||||
|
'When users create revisions in Differential, the default '.
|
||||||
|
'behavior is to hold them in the "Draft" state until all builds '.
|
||||||
|
'pass. Once builds pass, the revisions promote and are sent for '.
|
||||||
|
'review, which notifies reviewers.'.
|
||||||
|
"\n\n".
|
||||||
|
'The general intent of this workflow is to make sure reviewers '.
|
||||||
|
'are only spending time on review once changes survive automated '.
|
||||||
|
'tests. If a change does not pass tests, it usually is not '.
|
||||||
|
'really ready for review.'.
|
||||||
|
"\n\n".
|
||||||
|
'If you want to promote revisions out of "Draft" before builds '.
|
||||||
|
'pass, or promote revisions even when builds fail, you can '.
|
||||||
|
'change the promotion behavior. This may be useful if you have '.
|
||||||
|
'very long-running builds, or some builds which are not very '.
|
||||||
|
'important.'.
|
||||||
|
"\n\n".
|
||||||
|
'Users may always use "Request Review" to promote a "Draft" '.
|
||||||
|
'revision, even if builds have failed or are still in progress.'))
|
||||||
|
->setOptions($draft_options),
|
||||||
|
id(new self())
|
||||||
|
->setKey(self::BEHAVIOR_LANDWARNING)
|
||||||
|
->setName(pht('Warn When Landing'))
|
||||||
|
->setEditInstructions(
|
||||||
|
pht(
|
||||||
|
'When a user attempts to `arc land` a revision and that revision '.
|
||||||
|
'has ongoing or failed builds, the default behavior of `arc` is '.
|
||||||
|
'to warn them about those builds and give them a chance to '.
|
||||||
|
'reconsider: they may want to wait for ongoing builds to '.
|
||||||
|
'complete, or fix failed builds before landing the change.'.
|
||||||
|
"\n\n".
|
||||||
|
'If you do not want to warn users about this build, you can '.
|
||||||
|
'change the warning behavior. This may be useful if the build '.
|
||||||
|
'takes a long time to run (so you do not expect users to wait '.
|
||||||
|
'for it) or the outcome is not important.'.
|
||||||
|
"\n\n".
|
||||||
|
'This warning is only advisory. Users may always elect to ignore '.
|
||||||
|
'this warning and continue, even if builds have failed.'.
|
||||||
|
"\n\n".
|
||||||
|
'This setting also affects the warning that is published to '.
|
||||||
|
'revisions when commits land with ongoing or failed builds.'))
|
||||||
|
->setOptions($land_options),
|
||||||
|
id(new self())
|
||||||
|
->setKey(self::BEHAVIOR_BUILDABLE)
|
||||||
|
->setEditInstructions(
|
||||||
|
pht(
|
||||||
|
'The overall state of a buildable (like a commit or revision) is '.
|
||||||
|
'normally the aggregation of the individual states of all builds '.
|
||||||
|
'that have run against it.'.
|
||||||
|
"\n\n".
|
||||||
|
'Buildables are "building" until all builds pass (which changes '.
|
||||||
|
'them to "pass"), or any build fails (which changes them to '.
|
||||||
|
'"fail").'.
|
||||||
|
"\n\n".
|
||||||
|
'You can change this behavior if you do not want to wait for this '.
|
||||||
|
'build, or do not care if it fails.'))
|
||||||
|
->setName(pht('Affects Buildable'))
|
||||||
|
->setOptions($aggregate_options),
|
||||||
|
id(new self())
|
||||||
|
->setKey(self::BEHAVIOR_RESTARTABLE)
|
||||||
|
->setEditInstructions(
|
||||||
|
pht(
|
||||||
|
'Usually, builds may be restarted. This may be useful if you '.
|
||||||
|
'suspect a build has failed for environmental or circumstantial '.
|
||||||
|
'reasons unrelated to the actual code, and want to give it '.
|
||||||
|
'another chance at glory.'.
|
||||||
|
"\n\n".
|
||||||
|
'If you want to prevent a build from being restarted, you can '.
|
||||||
|
'change the behavior here. This may be useful to prevent '.
|
||||||
|
'accidents where a build with a dangerous side effect (like '.
|
||||||
|
'deployment) is restarted improperly.'))
|
||||||
|
->setName(pht('Restartable'))
|
||||||
|
->setOptions($restart_options),
|
||||||
|
id(new self())
|
||||||
|
->setKey(self::BEHAVIOR_RUNNABLE)
|
||||||
|
->setEditInstructions(
|
||||||
|
pht(
|
||||||
|
'To run a build manually, you normally must have permission to '.
|
||||||
|
'edit the related build plan. If you would prefer that anyone who '.
|
||||||
|
'can see the build plan be able to run and restart the build, you '.
|
||||||
|
'can change the behavior here.'.
|
||||||
|
"\n\n".
|
||||||
|
'Note that this controls access to all build management actions: '.
|
||||||
|
'"Run Plan Manually", "Restart", "Abort", "Pause", and "Resume".'.
|
||||||
|
"\n\n".
|
||||||
|
'WARNING: This may be unsafe, particularly if the build has '.
|
||||||
|
'side effects like deployment.'.
|
||||||
|
"\n\n".
|
||||||
|
'If you weaken this policy, an attacker with control of an '.
|
||||||
|
'account that has "Can View" permission but not "Can Edit" '.
|
||||||
|
'permission can manually run this build against any old version '.
|
||||||
|
'of the code, including versions with known security issues.'.
|
||||||
|
"\n\n".
|
||||||
|
'If running the build has a side effect like deploying code, '.
|
||||||
|
'they can force deployment of a vulnerable version and then '.
|
||||||
|
'escalate into an attack against the deployed service.'))
|
||||||
|
->setName(pht('Runnable'))
|
||||||
|
->setOptions($run_options),
|
||||||
|
);
|
||||||
|
|
||||||
|
return mpull($behaviors, null, 'getKey');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class HarbormasterBuildPlanBehaviorOption
|
||||||
|
extends Phobject {
|
||||||
|
|
||||||
|
private $name;
|
||||||
|
private $key;
|
||||||
|
private $icon;
|
||||||
|
private $description;
|
||||||
|
private $isDefault;
|
||||||
|
|
||||||
|
public function setName($name) {
|
||||||
|
$this->name = $name;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName() {
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setKey($key) {
|
||||||
|
$this->key = $key;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKey() {
|
||||||
|
return $this->key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDescription($description) {
|
||||||
|
$this->description = $description;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription() {
|
||||||
|
return $this->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setIsDefault($is_default) {
|
||||||
|
$this->isDefault = $is_default;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIsDefault() {
|
||||||
|
return $this->isDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setIcon($icon) {
|
||||||
|
$this->icon = $icon;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIcon() {
|
||||||
|
return $this->icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -128,49 +128,14 @@ final class HarbormasterBuildSearchEngine
|
||||||
|
|
||||||
$viewer = $this->requireViewer();
|
$viewer = $this->requireViewer();
|
||||||
|
|
||||||
$buildables = mpull($builds, 'getBuildable');
|
$list = id(new HarbormasterBuildView())
|
||||||
$object_phids = mpull($buildables, 'getBuildablePHID');
|
->setViewer($viewer)
|
||||||
$initiator_phids = mpull($builds, 'getInitiatorPHID');
|
->setBuilds($builds)
|
||||||
$phids = array_mergev(array($initiator_phids, $object_phids));
|
->newObjectList();
|
||||||
$phids = array_unique(array_filter($phids));
|
|
||||||
|
|
||||||
$handles = $viewer->loadHandles($phids);
|
return id(new PhabricatorApplicationSearchResultView())
|
||||||
|
->setObjectList($list)
|
||||||
$list = new PHUIObjectItemListView();
|
->setNoDataString(pht('No builds found.'));
|
||||||
foreach ($builds as $build) {
|
|
||||||
$id = $build->getID();
|
|
||||||
$initiator = $handles[$build->getInitiatorPHID()];
|
|
||||||
$buildable_object = $handles[$build->getBuildable()->getBuildablePHID()];
|
|
||||||
|
|
||||||
$item = id(new PHUIObjectItemView())
|
|
||||||
->setViewer($viewer)
|
|
||||||
->setObject($build)
|
|
||||||
->setObjectName(pht('Build %d', $build->getID()))
|
|
||||||
->setHeader($build->getName())
|
|
||||||
->setHref($build->getURI())
|
|
||||||
->setEpoch($build->getDateCreated())
|
|
||||||
->addAttribute($buildable_object->getName());
|
|
||||||
|
|
||||||
if ($initiator) {
|
|
||||||
$item->addHandleIcon($initiator, $initiator->getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
$status = $build->getBuildStatus();
|
|
||||||
|
|
||||||
$status_icon = HarbormasterBuildStatus::getBuildStatusIcon($status);
|
|
||||||
$status_color = HarbormasterBuildStatus::getBuildStatusColor($status);
|
|
||||||
$status_label = HarbormasterBuildStatus::getBuildStatusName($status);
|
|
||||||
|
|
||||||
$item->setStatusIcon("{$status_icon} {$status_color}", $status_label);
|
|
||||||
|
|
||||||
$list->addItem($item);
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = new PhabricatorApplicationSearchResultView();
|
|
||||||
$result->setObjectList($list);
|
|
||||||
$result->setNoDataString(pht('No builds found.'));
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,6 +183,10 @@ final class HarbormasterBuild extends HarbormasterDAO
|
||||||
return $this->getBuildStatusObject()->isPassed();
|
return $this->getBuildStatusObject()->isPassed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isFailed() {
|
||||||
|
return $this->getBuildStatusObject()->isFailed();
|
||||||
|
}
|
||||||
|
|
||||||
public function getURI() {
|
public function getURI() {
|
||||||
$id = $this->getID();
|
$id = $this->getID();
|
||||||
return "/harbormaster/build/{$id}/";
|
return "/harbormaster/build/{$id}/";
|
||||||
|
@ -193,6 +197,10 @@ final class HarbormasterBuild extends HarbormasterDAO
|
||||||
return HarbormasterBuildStatus::newBuildStatusObject($status_key);
|
return HarbormasterBuildStatus::newBuildStatusObject($status_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getObjectName() {
|
||||||
|
return pht('Build %d', $this->getID());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Build Commands )----------------------------------------------------- */
|
/* -( Build Commands )----------------------------------------------------- */
|
||||||
|
|
||||||
|
@ -207,11 +215,60 @@ final class HarbormasterBuild extends HarbormasterDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canRestartBuild() {
|
public function canRestartBuild() {
|
||||||
if ($this->isAutobuild()) {
|
try {
|
||||||
|
$this->assertCanRestartBuild();
|
||||||
|
return true;
|
||||||
|
} catch (HarbormasterRestartException $ex) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return !$this->isRestarting();
|
public function assertCanRestartBuild() {
|
||||||
|
if ($this->isAutobuild()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Can Not Restart Autobuild'),
|
||||||
|
pht(
|
||||||
|
'This build can not be restarted because it is an automatic '.
|
||||||
|
'build.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$restartable = HarbormasterBuildPlanBehavior::BEHAVIOR_RESTARTABLE;
|
||||||
|
$plan = $this->getBuildPlan();
|
||||||
|
|
||||||
|
$option = HarbormasterBuildPlanBehavior::getBehavior($restartable)
|
||||||
|
->getPlanOption($plan);
|
||||||
|
$option_key = $option->getKey();
|
||||||
|
|
||||||
|
$never_restartable = HarbormasterBuildPlanBehavior::RESTARTABLE_NEVER;
|
||||||
|
$is_never = ($option_key === $never_restartable);
|
||||||
|
if ($is_never) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Build Plan Prevents Restart'),
|
||||||
|
pht(
|
||||||
|
'This build can not be restarted because the build plan is '.
|
||||||
|
'configured to prevent the build from restarting.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$failed_restartable = HarbormasterBuildPlanBehavior::RESTARTABLE_IF_FAILED;
|
||||||
|
$is_failed = ($option_key === $failed_restartable);
|
||||||
|
if ($is_failed) {
|
||||||
|
if (!$this->isFailed()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Only Restartable if Failed'),
|
||||||
|
pht(
|
||||||
|
'This build can not be restarted because the build plan is '.
|
||||||
|
'configured to prevent the build from restarting unless it '.
|
||||||
|
'has failed, and it has not failed.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->isRestarting()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Already Restarting'),
|
||||||
|
pht(
|
||||||
|
'This build is already restarting. You can not reissue a restart '.
|
||||||
|
'command to a restarting build.'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canPauseBuild() {
|
public function canPauseBuild() {
|
||||||
|
@ -330,14 +387,17 @@ final class HarbormasterBuild extends HarbormasterDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
public function assertCanIssueCommand(PhabricatorUser $viewer, $command) {
|
public function assertCanIssueCommand(PhabricatorUser $viewer, $command) {
|
||||||
$need_edit = false;
|
$plan = $this->getBuildPlan();
|
||||||
|
|
||||||
|
$need_edit = true;
|
||||||
switch ($command) {
|
switch ($command) {
|
||||||
case HarbormasterBuildCommand::COMMAND_RESTART:
|
case HarbormasterBuildCommand::COMMAND_RESTART:
|
||||||
break;
|
|
||||||
case HarbormasterBuildCommand::COMMAND_PAUSE:
|
case HarbormasterBuildCommand::COMMAND_PAUSE:
|
||||||
case HarbormasterBuildCommand::COMMAND_RESUME:
|
case HarbormasterBuildCommand::COMMAND_RESUME:
|
||||||
case HarbormasterBuildCommand::COMMAND_ABORT:
|
case HarbormasterBuildCommand::COMMAND_ABORT:
|
||||||
$need_edit = true;
|
if ($plan->canRunWithoutEditCapability()) {
|
||||||
|
$need_edit = false;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
|
@ -351,7 +411,7 @@ final class HarbormasterBuild extends HarbormasterDAO
|
||||||
if ($need_edit) {
|
if ($need_edit) {
|
||||||
PhabricatorPolicyFilter::requireCapability(
|
PhabricatorPolicyFilter::requireCapability(
|
||||||
$viewer,
|
$viewer,
|
||||||
$this->getBuildPlan(),
|
$plan,
|
||||||
PhabricatorPolicyCapability::CAN_EDIT);
|
PhabricatorPolicyCapability::CAN_EDIT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,13 +10,15 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
|
||||||
PhabricatorSubscribableInterface,
|
PhabricatorSubscribableInterface,
|
||||||
PhabricatorNgramsInterface,
|
PhabricatorNgramsInterface,
|
||||||
PhabricatorConduitResultInterface,
|
PhabricatorConduitResultInterface,
|
||||||
PhabricatorProjectInterface {
|
PhabricatorProjectInterface,
|
||||||
|
PhabricatorPolicyCodexInterface {
|
||||||
|
|
||||||
protected $name;
|
protected $name;
|
||||||
protected $planStatus;
|
protected $planStatus;
|
||||||
protected $planAutoKey;
|
protected $planAutoKey;
|
||||||
protected $viewPolicy;
|
protected $viewPolicy;
|
||||||
protected $editPolicy;
|
protected $editPolicy;
|
||||||
|
protected $properties = array();
|
||||||
|
|
||||||
const STATUS_ACTIVE = 'active';
|
const STATUS_ACTIVE = 'active';
|
||||||
const STATUS_DISABLED = 'disabled';
|
const STATUS_DISABLED = 'disabled';
|
||||||
|
@ -45,6 +47,9 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
|
||||||
protected function getConfiguration() {
|
protected function getConfiguration() {
|
||||||
return array(
|
return array(
|
||||||
self::CONFIG_AUX_PHID => true,
|
self::CONFIG_AUX_PHID => true,
|
||||||
|
self::CONFIG_SERIALIZATION => array(
|
||||||
|
'properties' => self::SERIALIZATION_JSON,
|
||||||
|
),
|
||||||
self::CONFIG_COLUMN_SCHEMA => array(
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
'name' => 'sort128',
|
'name' => 'sort128',
|
||||||
'planStatus' => 'text32',
|
'planStatus' => 'text32',
|
||||||
|
@ -84,6 +89,25 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
|
||||||
return ($this->getPlanStatus() == self::STATUS_DISABLED);
|
return ($this->getPlanStatus() == self::STATUS_DISABLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getURI() {
|
||||||
|
return urisprintf(
|
||||||
|
'/harbormaster/plan/%s/',
|
||||||
|
$this->getID());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getObjectName() {
|
||||||
|
return pht('Plan %d', $this->getID());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlanProperty($key, $default = null) {
|
||||||
|
return idx($this->properties, $key, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPlanProperty($key, $value) {
|
||||||
|
$this->properties[$key] = $value;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Autoplans )---------------------------------------------------------- */
|
/* -( Autoplans )---------------------------------------------------------- */
|
||||||
|
|
||||||
|
@ -110,7 +134,6 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function getName() {
|
public function getName() {
|
||||||
$autoplan = $this->getAutoplan();
|
$autoplan = $this->getAutoplan();
|
||||||
if ($autoplan) {
|
if ($autoplan) {
|
||||||
|
@ -120,6 +143,38 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
|
||||||
return parent::getName();
|
return parent::getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function hasRunCapability(PhabricatorUser $viewer) {
|
||||||
|
try {
|
||||||
|
$this->assertHasRunCapability($viewer);
|
||||||
|
return true;
|
||||||
|
} catch (PhabricatorPolicyException $ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canRunWithoutEditCapability() {
|
||||||
|
$runnable = HarbormasterBuildPlanBehavior::BEHAVIOR_RUNNABLE;
|
||||||
|
$if_viewable = HarbormasterBuildPlanBehavior::RUNNABLE_IF_VIEWABLE;
|
||||||
|
|
||||||
|
$option = HarbormasterBuildPlanBehavior::getBehavior($runnable)
|
||||||
|
->getPlanOption($this);
|
||||||
|
|
||||||
|
return ($option->getKey() === $if_viewable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function assertHasRunCapability(PhabricatorUser $viewer) {
|
||||||
|
if ($this->canRunWithoutEditCapability()) {
|
||||||
|
$capability = PhabricatorPolicyCapability::CAN_VIEW;
|
||||||
|
} else {
|
||||||
|
$capability = PhabricatorPolicyCapability::CAN_EDIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
PhabricatorPolicyFilter::requireCapability(
|
||||||
|
$viewer,
|
||||||
|
$this,
|
||||||
|
$capability);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorSubscribableInterface )----------------------------------- */
|
/* -( PhabricatorSubscribableInterface )----------------------------------- */
|
||||||
|
|
||||||
|
@ -210,15 +265,31 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
|
||||||
->setKey('status')
|
->setKey('status')
|
||||||
->setType('map<string, wild>')
|
->setType('map<string, wild>')
|
||||||
->setDescription(pht('The current status of this build plan.')),
|
->setDescription(pht('The current status of this build plan.')),
|
||||||
|
id(new PhabricatorConduitSearchFieldSpecification())
|
||||||
|
->setKey('behaviors')
|
||||||
|
->setType('map<string, string>')
|
||||||
|
->setDescription(pht('Behavior configuration for the build plan.')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFieldValuesForConduit() {
|
public function getFieldValuesForConduit() {
|
||||||
|
$behavior_map = array();
|
||||||
|
|
||||||
|
$behaviors = HarbormasterBuildPlanBehavior::newPlanBehaviors();
|
||||||
|
foreach ($behaviors as $behavior) {
|
||||||
|
$option = $behavior->getPlanOption($this);
|
||||||
|
|
||||||
|
$behavior_map[$behavior->getKey()] = array(
|
||||||
|
'value' => $option->getKey(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'name' => $this->getName(),
|
'name' => $this->getName(),
|
||||||
'status' => array(
|
'status' => array(
|
||||||
'value' => $this->getPlanStatus(),
|
'value' => $this->getPlanStatus(),
|
||||||
),
|
),
|
||||||
|
'behaviors' => $behavior_map,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,4 +297,12 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( PhabricatorPolicyCodexInterface )------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
public function newPolicyCodex() {
|
||||||
|
return new HarbormasterBuildPlanPolicyCodex();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class HarbormasterBuildPlanTransaction
|
final class HarbormasterBuildPlanTransaction
|
||||||
extends PhabricatorApplicationTransaction {
|
extends PhabricatorModularTransaction {
|
||||||
|
|
||||||
const TYPE_NAME = 'harbormaster:name';
|
|
||||||
const TYPE_STATUS = 'harbormaster:status';
|
|
||||||
|
|
||||||
public function getApplicationName() {
|
public function getApplicationName() {
|
||||||
return 'harbormaster';
|
return 'harbormaster';
|
||||||
|
@ -14,67 +11,8 @@ final class HarbormasterBuildPlanTransaction
|
||||||
return HarbormasterBuildPlanPHIDType::TYPECONST;
|
return HarbormasterBuildPlanPHIDType::TYPECONST;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getIcon() {
|
public function getBaseTransactionClass() {
|
||||||
$old = $this->getOldValue();
|
return 'HarbormasterBuildPlanTransactionType';
|
||||||
$new = $this->getNewValue();
|
|
||||||
|
|
||||||
switch ($this->getTransactionType()) {
|
|
||||||
case self::TYPE_NAME:
|
|
||||||
if ($old === null) {
|
|
||||||
return 'fa-plus';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::getIcon();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getColor() {
|
|
||||||
$old = $this->getOldValue();
|
|
||||||
$new = $this->getNewValue();
|
|
||||||
|
|
||||||
switch ($this->getTransactionType()) {
|
|
||||||
case self::TYPE_NAME:
|
|
||||||
if ($old === null) {
|
|
||||||
return 'green';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::getIcon();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTitle() {
|
|
||||||
$old = $this->getOldValue();
|
|
||||||
$new = $this->getNewValue();
|
|
||||||
$author_handle = $this->renderHandleLink($this->getAuthorPHID());
|
|
||||||
|
|
||||||
switch ($this->getTransactionType()) {
|
|
||||||
case self::TYPE_NAME:
|
|
||||||
if ($old === null) {
|
|
||||||
return pht(
|
|
||||||
'%s created this build plan.',
|
|
||||||
$author_handle);
|
|
||||||
} else {
|
|
||||||
return pht(
|
|
||||||
'%s renamed this build plan from "%s" to "%s".',
|
|
||||||
$author_handle,
|
|
||||||
$old,
|
|
||||||
$new);
|
|
||||||
}
|
|
||||||
case self::TYPE_STATUS:
|
|
||||||
if ($new == HarbormasterBuildPlan::STATUS_DISABLED) {
|
|
||||||
return pht(
|
|
||||||
'%s disabled this build plan.',
|
|
||||||
$author_handle);
|
|
||||||
} else {
|
|
||||||
return pht(
|
|
||||||
'%s enabled this build plan.',
|
|
||||||
$author_handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::getTitle();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
67
src/applications/harbormaster/view/HarbormasterBuildView.php
Normal file
67
src/applications/harbormaster/view/HarbormasterBuildView.php
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class HarbormasterBuildView
|
||||||
|
extends AphrontView {
|
||||||
|
|
||||||
|
private $builds = array();
|
||||||
|
|
||||||
|
public function setBuilds(array $builds) {
|
||||||
|
assert_instances_of($builds, 'HarbormasterBuild');
|
||||||
|
$this->builds = $builds;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBuilds() {
|
||||||
|
return $this->builds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render() {
|
||||||
|
return $this->newObjectList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newObjectList() {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$builds = $this->getBuilds();
|
||||||
|
|
||||||
|
$buildables = mpull($builds, 'getBuildable');
|
||||||
|
$object_phids = mpull($buildables, 'getBuildablePHID');
|
||||||
|
$initiator_phids = mpull($builds, 'getInitiatorPHID');
|
||||||
|
$phids = array_mergev(array($initiator_phids, $object_phids));
|
||||||
|
$phids = array_unique(array_filter($phids));
|
||||||
|
|
||||||
|
$handles = $viewer->loadHandles($phids);
|
||||||
|
|
||||||
|
$list = new PHUIObjectItemListView();
|
||||||
|
foreach ($builds as $build) {
|
||||||
|
$id = $build->getID();
|
||||||
|
$initiator = $handles[$build->getInitiatorPHID()];
|
||||||
|
$buildable_object = $handles[$build->getBuildable()->getBuildablePHID()];
|
||||||
|
|
||||||
|
$item = id(new PHUIObjectItemView())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->setObject($build)
|
||||||
|
->setObjectName($build->getObjectName())
|
||||||
|
->setHeader($build->getName())
|
||||||
|
->setHref($build->getURI())
|
||||||
|
->setEpoch($build->getDateCreated())
|
||||||
|
->addAttribute($buildable_object->getName());
|
||||||
|
|
||||||
|
if ($initiator) {
|
||||||
|
$item->addByline($initiator->renderLink());
|
||||||
|
}
|
||||||
|
|
||||||
|
$status = $build->getBuildStatus();
|
||||||
|
|
||||||
|
$status_icon = HarbormasterBuildStatus::getBuildStatusIcon($status);
|
||||||
|
$status_color = HarbormasterBuildStatus::getBuildStatusColor($status);
|
||||||
|
$status_label = HarbormasterBuildStatus::getBuildStatusName($status);
|
||||||
|
|
||||||
|
$item->setStatusIcon("{$status_icon} {$status_color}", $status_label);
|
||||||
|
|
||||||
|
$list->addItem($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class HarbormasterBuildPlanBehaviorTransaction
|
||||||
|
extends HarbormasterBuildPlanTransactionType {
|
||||||
|
|
||||||
|
const TRANSACTIONTYPE = 'behavior';
|
||||||
|
|
||||||
|
public function generateOldValue($object) {
|
||||||
|
$behavior = $this->getBehavior();
|
||||||
|
return $behavior->getPlanOption($object)->getKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyInternalEffects($object, $value) {
|
||||||
|
$key = $this->getStorageKey();
|
||||||
|
return $object->setPlanProperty($key, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle() {
|
||||||
|
$old_value = $this->getOldValue();
|
||||||
|
$new_value = $this->getNewValue();
|
||||||
|
|
||||||
|
$behavior = $this->getBehavior();
|
||||||
|
if ($behavior) {
|
||||||
|
$behavior_name = $behavior->getName();
|
||||||
|
|
||||||
|
$options = $behavior->getOptions();
|
||||||
|
if (isset($options[$old_value])) {
|
||||||
|
$old_value = $options[$old_value]->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($options[$new_value])) {
|
||||||
|
$new_value = $options[$new_value]->getName();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$behavior_name = $this->getBehaviorKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
return pht(
|
||||||
|
'%s changed the %s behavior for this plan from %s to %s.',
|
||||||
|
$this->renderAuthor(),
|
||||||
|
$this->renderValue($behavior_name),
|
||||||
|
$this->renderValue($old_value),
|
||||||
|
$this->renderValue($new_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateTransactions($object, array $xactions) {
|
||||||
|
$errors = array();
|
||||||
|
|
||||||
|
$behaviors = HarbormasterBuildPlanBehavior::newPlanBehaviors();
|
||||||
|
$behaviors = mpull($behaviors, null, 'getKey');
|
||||||
|
|
||||||
|
foreach ($xactions as $xaction) {
|
||||||
|
$key = $this->getBehaviorKeyForTransaction($xaction);
|
||||||
|
|
||||||
|
if (!isset($behaviors[$key])) {
|
||||||
|
$errors[] = $this->newInvalidError(
|
||||||
|
pht(
|
||||||
|
'No behavior with key "%s" exists. Valid keys are: %s.',
|
||||||
|
$key,
|
||||||
|
implode(', ', array_keys($behaviors))),
|
||||||
|
$xaction);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$behavior = $behaviors[$key];
|
||||||
|
$options = $behavior->getOptions();
|
||||||
|
|
||||||
|
$storage_key = HarbormasterBuildPlanBehavior::getStorageKeyForBehaviorKey(
|
||||||
|
$key);
|
||||||
|
$old = $object->getPlanProperty($storage_key);
|
||||||
|
$new = $xaction->getNewValue();
|
||||||
|
|
||||||
|
if ($old === $new) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($options[$new])) {
|
||||||
|
$errors[] = $this->newInvalidError(
|
||||||
|
pht(
|
||||||
|
'Value "%s" is not a valid option for behavior "%s". Valid '.
|
||||||
|
'options are: %s.',
|
||||||
|
$new,
|
||||||
|
$key,
|
||||||
|
implode(', ', array_keys($options))),
|
||||||
|
$xaction);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTransactionTypeForConduit($xaction) {
|
||||||
|
return 'behavior';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFieldValuesForConduit($xaction, $data) {
|
||||||
|
return array(
|
||||||
|
'key' => $this->getBehaviorKeyForTransaction($xaction),
|
||||||
|
'old' => $xaction->getOldValue(),
|
||||||
|
'new' => $xaction->getNewValue(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getBehaviorKeyForTransaction(
|
||||||
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
$metadata_key = HarbormasterBuildPlanBehavior::getTransactionMetadataKey();
|
||||||
|
return $xaction->getMetadataValue($metadata_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getBehaviorKey() {
|
||||||
|
$metadata_key = HarbormasterBuildPlanBehavior::getTransactionMetadataKey();
|
||||||
|
return $this->getMetadataValue($metadata_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getBehavior() {
|
||||||
|
$behavior_key = $this->getBehaviorKey();
|
||||||
|
$behaviors = HarbormasterBuildPlanBehavior::newPlanBehaviors();
|
||||||
|
return idx($behaviors, $behavior_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getStorageKey() {
|
||||||
|
return HarbormasterBuildPlanBehavior::getStorageKeyForBehaviorKey(
|
||||||
|
$this->getBehaviorKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class HarbormasterBuildPlanNameTransaction
|
||||||
|
extends HarbormasterBuildPlanTransactionType {
|
||||||
|
|
||||||
|
const TRANSACTIONTYPE = 'harbormaster:name';
|
||||||
|
|
||||||
|
public function generateOldValue($object) {
|
||||||
|
return $object->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyInternalEffects($object, $value) {
|
||||||
|
$object->setName($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle() {
|
||||||
|
return pht(
|
||||||
|
'%s renamed this build plan from "%s" to "%s".',
|
||||||
|
$this->renderAuthor(),
|
||||||
|
$this->renderOldValue(),
|
||||||
|
$this->renderNewValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateTransactions($object, array $xactions) {
|
||||||
|
$errors = array();
|
||||||
|
|
||||||
|
if ($this->isEmptyTextTransaction($object->getName(), $xactions)) {
|
||||||
|
$errors[] = $this->newRequiredError(
|
||||||
|
pht('You must choose a name for your build plan.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTransactionTypeForConduit($xaction) {
|
||||||
|
return 'name';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFieldValuesForConduit($xaction, $data) {
|
||||||
|
return array(
|
||||||
|
'old' => $xaction->getOldValue(),
|
||||||
|
'new' => $xaction->getNewValue(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class HarbormasterBuildPlanStatusTransaction
|
||||||
|
extends HarbormasterBuildPlanTransactionType {
|
||||||
|
|
||||||
|
const TRANSACTIONTYPE = 'harbormaster:status';
|
||||||
|
|
||||||
|
public function generateOldValue($object) {
|
||||||
|
return $object->getPlanStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyInternalEffects($object, $value) {
|
||||||
|
$object->setPlanStatus($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle() {
|
||||||
|
$new = $this->getNewValue();
|
||||||
|
if ($new === HarbormasterBuildPlan::STATUS_DISABLED) {
|
||||||
|
return pht(
|
||||||
|
'%s disabled this build plan.',
|
||||||
|
$this->renderAuthor());
|
||||||
|
} else {
|
||||||
|
return pht(
|
||||||
|
'%s enabled this build plan.',
|
||||||
|
$this->renderAuthor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateTransactions($object, array $xactions) {
|
||||||
|
$errors = array();
|
||||||
|
|
||||||
|
$options = array(
|
||||||
|
HarbormasterBuildPlan::STATUS_DISABLED,
|
||||||
|
HarbormasterBuildPlan::STATUS_ACTIVE,
|
||||||
|
);
|
||||||
|
$options = array_fuse($options);
|
||||||
|
|
||||||
|
foreach ($xactions as $xaction) {
|
||||||
|
$new = $xaction->getNewValue();
|
||||||
|
|
||||||
|
if (!isset($options[$new])) {
|
||||||
|
$errors[] = $this->newInvalidError(
|
||||||
|
pht(
|
||||||
|
'Status "%s" is not a valid build plan status. Valid '.
|
||||||
|
'statuses are: %s.',
|
||||||
|
$new,
|
||||||
|
implode(', ', $options)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTransactionTypeForConduit($xaction) {
|
||||||
|
return 'status';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFieldValuesForConduit($xaction, $data) {
|
||||||
|
return array(
|
||||||
|
'old' => $xaction->getOldValue(),
|
||||||
|
'new' => $xaction->getNewValue(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class HarbormasterBuildPlanTransactionType
|
||||||
|
extends PhabricatorModularTransactionType {}
|
|
@ -401,4 +401,8 @@ abstract class HeraldAction extends Phobject {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getPHIDsAffectedByAction(HeraldActionRecord $record) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,4 +63,8 @@ final class HeraldCallWebhookAction extends HeraldAction {
|
||||||
return new HeraldWebhookDatasource();
|
return new HeraldWebhookDatasource();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getPHIDsAffectedByAction(HeraldActionRecord $record) {
|
||||||
|
return $record->getTarget();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,15 +186,16 @@ abstract class HeraldAdapter extends Phobject {
|
||||||
return $this->appliedTransactions;
|
return $this->appliedTransactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function queueTransaction($transaction) {
|
final public function queueTransaction(
|
||||||
|
PhabricatorApplicationTransaction $transaction) {
|
||||||
$this->queuedTransactions[] = $transaction;
|
$this->queuedTransactions[] = $transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getQueuedTransactions() {
|
final public function getQueuedTransactions() {
|
||||||
return $this->queuedTransactions;
|
return $this->queuedTransactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function newTransaction() {
|
final public function newTransaction() {
|
||||||
$object = $this->newObject();
|
$object = $this->newObject();
|
||||||
|
|
||||||
if (!($object instanceof PhabricatorApplicationTransactionInterface)) {
|
if (!($object instanceof PhabricatorApplicationTransactionInterface)) {
|
||||||
|
@ -205,7 +206,19 @@ abstract class HeraldAdapter extends Phobject {
|
||||||
'PhabricatorApplicationTransactionInterface'));
|
'PhabricatorApplicationTransactionInterface'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $object->getApplicationTransactionTemplate();
|
$xaction = $object->getApplicationTransactionTemplate();
|
||||||
|
|
||||||
|
if (!($xaction instanceof PhabricatorApplicationTransaction)) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Expected object (of class "%s") to return a transaction template '.
|
||||||
|
'(of class "%s"), but it returned something else ("%s").',
|
||||||
|
get_class($object),
|
||||||
|
'PhabricatorApplicationTransaction',
|
||||||
|
phutil_describe_type($xaction)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $xaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ final class HeraldDisableController extends HeraldController {
|
||||||
|
|
||||||
if ($request->isFormPost()) {
|
if ($request->isFormPost()) {
|
||||||
$xaction = id(new HeraldRuleTransaction())
|
$xaction = id(new HeraldRuleTransaction())
|
||||||
->setTransactionType(HeraldRuleTransaction::TYPE_DISABLE)
|
->setTransactionType(HeraldRuleDisableTransaction::TRANSACTIONTYPE)
|
||||||
->setNewValue($is_disable);
|
->setNewValue($is_disable);
|
||||||
|
|
||||||
id(new HeraldRuleEditor())
|
id(new HeraldRuleEditor())
|
||||||
|
|
|
@ -359,11 +359,21 @@ final class HeraldRuleController extends HeraldController {
|
||||||
$repetition_policy);
|
$repetition_policy);
|
||||||
|
|
||||||
$xactions = array();
|
$xactions = array();
|
||||||
|
|
||||||
|
// Until this moves to EditEngine, manually add a "CREATE" transaction
|
||||||
|
// if we're creating a new rule. This improves rendering of the initial
|
||||||
|
// group of transactions.
|
||||||
|
$is_new = (bool)(!$rule->getID());
|
||||||
|
if ($is_new) {
|
||||||
|
$xactions[] = id(new HeraldRuleTransaction())
|
||||||
|
->setTransactionType(PhabricatorTransactions::TYPE_CREATE);
|
||||||
|
}
|
||||||
|
|
||||||
$xactions[] = id(new HeraldRuleTransaction())
|
$xactions[] = id(new HeraldRuleTransaction())
|
||||||
->setTransactionType(HeraldRuleTransaction::TYPE_EDIT)
|
->setTransactionType(HeraldRuleEditTransaction::TRANSACTIONTYPE)
|
||||||
->setNewValue($new_state);
|
->setNewValue($new_state);
|
||||||
$xactions[] = id(new HeraldRuleTransaction())
|
$xactions[] = id(new HeraldRuleTransaction())
|
||||||
->setTransactionType(HeraldRuleTransaction::TYPE_NAME)
|
->setTransactionType(HeraldRuleNameTransaction::TRANSACTIONTYPE)
|
||||||
->setNewValue($new_name);
|
->setNewValue($new_name);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -73,12 +73,15 @@ final class HeraldWebhookViewController
|
||||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||||
->setTable($requests_table);
|
->setTable($requests_table);
|
||||||
|
|
||||||
|
$rules_view = $this->newRulesView($hook);
|
||||||
|
|
||||||
$hook_view = id(new PHUITwoColumnView())
|
$hook_view = id(new PHUITwoColumnView())
|
||||||
->setHeader($header)
|
->setHeader($header)
|
||||||
->setMainColumn(
|
->setMainColumn(
|
||||||
array(
|
array(
|
||||||
$warnings,
|
$warnings,
|
||||||
$properties_view,
|
$properties_view,
|
||||||
|
$rules_view,
|
||||||
$requests_view,
|
$requests_view,
|
||||||
$timeline,
|
$timeline,
|
||||||
))
|
))
|
||||||
|
@ -194,4 +197,42 @@ final class HeraldWebhookViewController
|
||||||
->appendChild($properties);
|
->appendChild($properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function newRulesView(HeraldWebhook $hook) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$rules = id(new HeraldRuleQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withDisabled(false)
|
||||||
|
->withAffectedObjectPHIDs(array($hook->getPHID()))
|
||||||
|
->needValidateAuthors(true)
|
||||||
|
->setLimit(10)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$list = id(new HeraldRuleListView())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->setRules($rules)
|
||||||
|
->newObjectList();
|
||||||
|
|
||||||
|
$list->setNoDataString(pht('No active Herald rules call this webhook.'));
|
||||||
|
|
||||||
|
$more_href = new PhutilURI(
|
||||||
|
'/herald/',
|
||||||
|
array('affectedPHID' => $hook->getPHID()));
|
||||||
|
|
||||||
|
$more_link = id(new PHUIButtonView())
|
||||||
|
->setTag('a')
|
||||||
|
->setIcon('fa-list-ul')
|
||||||
|
->setText(pht('View All Rules'))
|
||||||
|
->setHref($more_href);
|
||||||
|
|
||||||
|
$header = id(new PHUIHeaderView())
|
||||||
|
->setHeader(pht('Called By Herald Rules'))
|
||||||
|
->addActionLink($more_link);
|
||||||
|
|
||||||
|
return id(new PHUIObjectBoxView())
|
||||||
|
->setHeader($header)
|
||||||
|
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||||
|
->appendChild($list);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class HeraldRuleActionAffectsObjectEdgeType
|
||||||
|
extends PhabricatorEdgeType {
|
||||||
|
|
||||||
|
const EDGECONST = 69;
|
||||||
|
|
||||||
|
}
|
|
@ -11,82 +11,6 @@ final class HeraldRuleEditor
|
||||||
return pht('Herald Rules');
|
return pht('Herald Rules');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTransactionTypes() {
|
|
||||||
$types = parent::getTransactionTypes();
|
|
||||||
|
|
||||||
$types[] = PhabricatorTransactions::TYPE_COMMENT;
|
|
||||||
$types[] = HeraldRuleTransaction::TYPE_EDIT;
|
|
||||||
$types[] = HeraldRuleTransaction::TYPE_NAME;
|
|
||||||
$types[] = HeraldRuleTransaction::TYPE_DISABLE;
|
|
||||||
|
|
||||||
return $types;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getCustomTransactionOldValue(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
|
||||||
|
|
||||||
switch ($xaction->getTransactionType()) {
|
|
||||||
case HeraldRuleTransaction::TYPE_DISABLE:
|
|
||||||
return (int)$object->getIsDisabled();
|
|
||||||
case HeraldRuleTransaction::TYPE_EDIT:
|
|
||||||
return id(new HeraldRuleSerializer())
|
|
||||||
->serializeRule($object);
|
|
||||||
case HeraldRuleTransaction::TYPE_NAME:
|
|
||||||
return $object->getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getCustomTransactionNewValue(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
|
||||||
|
|
||||||
switch ($xaction->getTransactionType()) {
|
|
||||||
case HeraldRuleTransaction::TYPE_DISABLE:
|
|
||||||
return (int)$xaction->getNewValue();
|
|
||||||
case HeraldRuleTransaction::TYPE_EDIT:
|
|
||||||
case HeraldRuleTransaction::TYPE_NAME:
|
|
||||||
return $xaction->getNewValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyCustomInternalTransaction(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
|
||||||
|
|
||||||
switch ($xaction->getTransactionType()) {
|
|
||||||
case HeraldRuleTransaction::TYPE_DISABLE:
|
|
||||||
return $object->setIsDisabled($xaction->getNewValue());
|
|
||||||
case HeraldRuleTransaction::TYPE_NAME:
|
|
||||||
return $object->setName($xaction->getNewValue());
|
|
||||||
case HeraldRuleTransaction::TYPE_EDIT:
|
|
||||||
$new_state = id(new HeraldRuleSerializer())
|
|
||||||
->deserializeRuleComponents($xaction->getNewValue());
|
|
||||||
$object->setMustMatchAll((int)$new_state['match_all']);
|
|
||||||
$object->attachConditions($new_state['conditions']);
|
|
||||||
$object->attachActions($new_state['actions']);
|
|
||||||
|
|
||||||
$new_repetition = $new_state['repetition_policy'];
|
|
||||||
$object->setRepetitionPolicyStringConstant($new_repetition);
|
|
||||||
|
|
||||||
return $object;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyCustomExternalTransaction(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
|
||||||
switch ($xaction->getTransactionType()) {
|
|
||||||
case HeraldRuleTransaction::TYPE_EDIT:
|
|
||||||
$object->saveConditions($object->getConditions());
|
|
||||||
$object->saveActions($object->getActions());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function shouldApplyHeraldRules(
|
protected function shouldApplyHeraldRules(
|
||||||
PhabricatorLiskDAO $object,
|
PhabricatorLiskDAO $object,
|
||||||
array $xactions) {
|
array $xactions) {
|
||||||
|
@ -137,7 +61,6 @@ final class HeraldRuleEditor
|
||||||
return pht('[Herald]');
|
return pht('[Herald]');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected function buildMailBody(
|
protected function buildMailBody(
|
||||||
PhabricatorLiskDAO $object,
|
PhabricatorLiskDAO $object,
|
||||||
array $xactions) {
|
array $xactions) {
|
||||||
|
@ -151,4 +74,8 @@ final class HeraldRuleEditor
|
||||||
return $body;
|
return $body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function supportsSearch() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class HeraldRuleIndexEngineExtension
|
||||||
|
extends PhabricatorIndexEngineExtension {
|
||||||
|
|
||||||
|
const EXTENSIONKEY = 'herald.actions';
|
||||||
|
|
||||||
|
public function getExtensionName() {
|
||||||
|
return pht('Herald Actions');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shouldIndexObject($object) {
|
||||||
|
if (!($object instanceof HeraldRule)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function indexObject(
|
||||||
|
PhabricatorIndexEngine $engine,
|
||||||
|
$object) {
|
||||||
|
|
||||||
|
$edge_type = HeraldRuleActionAffectsObjectEdgeType::EDGECONST;
|
||||||
|
|
||||||
|
$old_edges = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
||||||
|
$object->getPHID(),
|
||||||
|
$edge_type);
|
||||||
|
$old_edges = array_fuse($old_edges);
|
||||||
|
|
||||||
|
$new_edges = $this->getPHIDsAffectedByActions($object);
|
||||||
|
$new_edges = array_fuse($new_edges);
|
||||||
|
|
||||||
|
$add_edges = array_diff_key($new_edges, $old_edges);
|
||||||
|
$rem_edges = array_diff_key($old_edges, $new_edges);
|
||||||
|
|
||||||
|
if (!$add_edges && !$rem_edges) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$editor = new PhabricatorEdgeEditor();
|
||||||
|
|
||||||
|
foreach ($add_edges as $phid) {
|
||||||
|
$editor->addEdge($object->getPHID(), $edge_type, $phid);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($rem_edges as $phid) {
|
||||||
|
$editor->removeEdge($object->getPHID(), $edge_type, $phid);
|
||||||
|
}
|
||||||
|
|
||||||
|
$editor->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIndexVersion($object) {
|
||||||
|
$phids = $this->getPHIDsAffectedByActions($object);
|
||||||
|
sort($phids);
|
||||||
|
$phids = implode(':', $phids);
|
||||||
|
return PhabricatorHash::digestForIndex($phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPHIDsAffectedByActions(HeraldRule $rule) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$rule = id(new HeraldRuleQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(array($rule->getID()))
|
||||||
|
->needConditionsAndActions(true)
|
||||||
|
->executeOne();
|
||||||
|
if (!$rule) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$phids = array();
|
||||||
|
|
||||||
|
$actions = HeraldAction::getAllActions();
|
||||||
|
foreach ($rule->getActions() as $action_record) {
|
||||||
|
$action = idx($actions, $action_record->getAction());
|
||||||
|
|
||||||
|
if (!$action) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($action->getPHIDsAffectedByAction($action_record) as $phid) {
|
||||||
|
$phids[] = $phid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$phids = array_fuse($phids);
|
||||||
|
return array_keys($phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ final class HeraldRuleQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
private $active;
|
private $active;
|
||||||
private $datasourceQuery;
|
private $datasourceQuery;
|
||||||
private $triggerObjectPHIDs;
|
private $triggerObjectPHIDs;
|
||||||
|
private $affectedObjectPHIDs;
|
||||||
|
|
||||||
private $needConditionsAndActions;
|
private $needConditionsAndActions;
|
||||||
private $needAppliedToPHIDs;
|
private $needAppliedToPHIDs;
|
||||||
|
@ -61,6 +62,11 @@ final class HeraldRuleQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withAffectedObjectPHIDs(array $phids) {
|
||||||
|
$this->affectedObjectPHIDs = $phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function needConditionsAndActions($need) {
|
public function needConditionsAndActions($need) {
|
||||||
$this->needConditionsAndActions = $need;
|
$this->needConditionsAndActions = $need;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -261,9 +267,31 @@ final class HeraldRuleQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
$this->triggerObjectPHIDs);
|
$this->triggerObjectPHIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->affectedObjectPHIDs !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'edge_affects.dst IN (%Ls)',
|
||||||
|
$this->affectedObjectPHIDs);
|
||||||
|
}
|
||||||
|
|
||||||
return $where;
|
return $where;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
|
||||||
|
$joins = parent::buildJoinClauseParts($conn);
|
||||||
|
|
||||||
|
if ($this->affectedObjectPHIDs !== null) {
|
||||||
|
$joins[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'JOIN %T edge_affects ON rule.phid = edge_affects.src
|
||||||
|
AND edge_affects.type = %d',
|
||||||
|
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
|
||||||
|
HeraldRuleActionAffectsObjectEdgeType::EDGECONST);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $joins;
|
||||||
|
}
|
||||||
|
|
||||||
private function validateRuleAuthors(array $rules) {
|
private function validateRuleAuthors(array $rules) {
|
||||||
// "Global" and "Object" rules always have valid authors.
|
// "Global" and "Object" rules always have valid authors.
|
||||||
foreach ($rules as $key => $rule) {
|
foreach ($rules as $key => $rule) {
|
||||||
|
|
|
@ -55,6 +55,10 @@ final class HeraldRuleSearchEngine extends PhabricatorApplicationSearchEngine {
|
||||||
pht('(Show All)'),
|
pht('(Show All)'),
|
||||||
pht('Show Only Disabled Rules'),
|
pht('Show Only Disabled Rules'),
|
||||||
pht('Show Only Enabled Rules')),
|
pht('Show Only Enabled Rules')),
|
||||||
|
id(new PhabricatorPHIDsSearchField())
|
||||||
|
->setLabel(pht('Affected Objects'))
|
||||||
|
->setKey('affectedPHIDs')
|
||||||
|
->setAliases(array('affectedPHID')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +85,10 @@ final class HeraldRuleSearchEngine extends PhabricatorApplicationSearchEngine {
|
||||||
$query->withActive($map['active']);
|
$query->withActive($map['active']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($map['affectedPHIDs']) {
|
||||||
|
$query->withAffectedObjectPHIDs($map['affectedPHIDs']);
|
||||||
|
}
|
||||||
|
|
||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,54 +135,18 @@ final class HeraldRuleSearchEngine extends PhabricatorApplicationSearchEngine {
|
||||||
PhabricatorSavedQuery $query,
|
PhabricatorSavedQuery $query,
|
||||||
array $handles) {
|
array $handles) {
|
||||||
assert_instances_of($rules, 'HeraldRule');
|
assert_instances_of($rules, 'HeraldRule');
|
||||||
|
|
||||||
$viewer = $this->requireViewer();
|
$viewer = $this->requireViewer();
|
||||||
$handles = $viewer->loadHandles(mpull($rules, 'getAuthorPHID'));
|
|
||||||
|
|
||||||
$content_type_map = HeraldAdapter::getEnabledAdapterMap($viewer);
|
$list = id(new HeraldRuleListView())
|
||||||
|
->setViewer($viewer)
|
||||||
$list = id(new PHUIObjectItemListView())
|
->setRules($rules)
|
||||||
->setUser($viewer);
|
->newObjectList();
|
||||||
foreach ($rules as $rule) {
|
|
||||||
$monogram = $rule->getMonogram();
|
|
||||||
|
|
||||||
$item = id(new PHUIObjectItemView())
|
|
||||||
->setObjectName($monogram)
|
|
||||||
->setHeader($rule->getName())
|
|
||||||
->setHref("/{$monogram}");
|
|
||||||
|
|
||||||
if ($rule->isPersonalRule()) {
|
|
||||||
$item->addIcon('fa-user', pht('Personal Rule'));
|
|
||||||
$item->addByline(
|
|
||||||
pht(
|
|
||||||
'Authored by %s',
|
|
||||||
$handles[$rule->getAuthorPHID()]->renderLink()));
|
|
||||||
} else if ($rule->isObjectRule()) {
|
|
||||||
$item->addIcon('fa-briefcase', pht('Object Rule'));
|
|
||||||
} else {
|
|
||||||
$item->addIcon('fa-globe', pht('Global Rule'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($rule->getIsDisabled()) {
|
|
||||||
$item->setDisabled(true);
|
|
||||||
$item->addIcon('fa-lock grey', pht('Disabled'));
|
|
||||||
} else if (!$rule->hasValidAuthor()) {
|
|
||||||
$item->setDisabled(true);
|
|
||||||
$item->addIcon('fa-user grey', pht('Author Not Active'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$content_type_name = idx($content_type_map, $rule->getContentType());
|
|
||||||
$item->addAttribute(pht('Affects: %s', $content_type_name));
|
|
||||||
|
|
||||||
$list->addItem($item);
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = new PhabricatorApplicationSearchResultView();
|
$result = new PhabricatorApplicationSearchResultView();
|
||||||
$result->setObjectList($list);
|
$result->setObjectList($list);
|
||||||
$result->setNoDataString(pht('No rules found.'));
|
$result->setNoDataString(pht('No rules found.'));
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getNewUserBody() {
|
protected function getNewUserBody() {
|
||||||
|
|
|
@ -6,6 +6,7 @@ final class HeraldRule extends HeraldDAO
|
||||||
PhabricatorFlaggableInterface,
|
PhabricatorFlaggableInterface,
|
||||||
PhabricatorPolicyInterface,
|
PhabricatorPolicyInterface,
|
||||||
PhabricatorDestructibleInterface,
|
PhabricatorDestructibleInterface,
|
||||||
|
PhabricatorIndexableInterface,
|
||||||
PhabricatorSubscribableInterface {
|
PhabricatorSubscribableInterface {
|
||||||
|
|
||||||
const TABLE_RULE_APPLIED = 'herald_ruleapplied';
|
const TABLE_RULE_APPLIED = 'herald_ruleapplied';
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class HeraldRuleTransaction
|
final class HeraldRuleTransaction
|
||||||
extends PhabricatorApplicationTransaction {
|
extends PhabricatorModularTransaction {
|
||||||
|
|
||||||
const TYPE_EDIT = 'herald:edit';
|
const TYPE_EDIT = 'herald:edit';
|
||||||
const TYPE_NAME = 'herald:name';
|
|
||||||
const TYPE_DISABLE = 'herald:disable';
|
|
||||||
|
|
||||||
public function getApplicationName() {
|
public function getApplicationName() {
|
||||||
return 'herald';
|
return 'herald';
|
||||||
|
@ -15,121 +13,8 @@ final class HeraldRuleTransaction
|
||||||
return HeraldRulePHIDType::TYPECONST;
|
return HeraldRulePHIDType::TYPECONST;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getApplicationTransactionCommentObject() {
|
public function getBaseTransactionClass() {
|
||||||
return new HeraldRuleTransactionComment();
|
return 'HeraldRuleTransactionType';
|
||||||
}
|
|
||||||
|
|
||||||
public function getColor() {
|
|
||||||
$old = $this->getOldValue();
|
|
||||||
$new = $this->getNewValue();
|
|
||||||
|
|
||||||
switch ($this->getTransactionType()) {
|
|
||||||
case self::TYPE_DISABLE:
|
|
||||||
if ($new) {
|
|
||||||
return 'red';
|
|
||||||
} else {
|
|
||||||
return 'green';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::getColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getActionName() {
|
|
||||||
$old = $this->getOldValue();
|
|
||||||
$new = $this->getNewValue();
|
|
||||||
|
|
||||||
switch ($this->getTransactionType()) {
|
|
||||||
case self::TYPE_DISABLE:
|
|
||||||
if ($new) {
|
|
||||||
return pht('Disabled');
|
|
||||||
} else {
|
|
||||||
return pht('Enabled');
|
|
||||||
}
|
|
||||||
case self::TYPE_NAME:
|
|
||||||
return pht('Renamed');
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::getActionName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getIcon() {
|
|
||||||
$old = $this->getOldValue();
|
|
||||||
$new = $this->getNewValue();
|
|
||||||
|
|
||||||
switch ($this->getTransactionType()) {
|
|
||||||
case self::TYPE_DISABLE:
|
|
||||||
if ($new) {
|
|
||||||
return 'fa-ban';
|
|
||||||
} else {
|
|
||||||
return 'fa-check';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::getIcon();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function getTitle() {
|
|
||||||
$author_phid = $this->getAuthorPHID();
|
|
||||||
|
|
||||||
$old = $this->getOldValue();
|
|
||||||
$new = $this->getNewValue();
|
|
||||||
|
|
||||||
switch ($this->getTransactionType()) {
|
|
||||||
case self::TYPE_DISABLE:
|
|
||||||
if ($new) {
|
|
||||||
return pht(
|
|
||||||
'%s disabled this rule.',
|
|
||||||
$this->renderHandleLink($author_phid));
|
|
||||||
} else {
|
|
||||||
return pht(
|
|
||||||
'%s enabled this rule.',
|
|
||||||
$this->renderHandleLink($author_phid));
|
|
||||||
}
|
|
||||||
case self::TYPE_NAME:
|
|
||||||
if ($old == null) {
|
|
||||||
return pht(
|
|
||||||
'%s created this rule.',
|
|
||||||
$this->renderHandleLink($author_phid));
|
|
||||||
} else {
|
|
||||||
return pht(
|
|
||||||
'%s renamed this rule from "%s" to "%s".',
|
|
||||||
$this->renderHandleLink($author_phid),
|
|
||||||
$old,
|
|
||||||
$new);
|
|
||||||
}
|
|
||||||
case self::TYPE_EDIT:
|
|
||||||
return pht(
|
|
||||||
'%s edited this rule.',
|
|
||||||
$this->renderHandleLink($author_phid));
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::getTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hasChangeDetails() {
|
|
||||||
switch ($this->getTransactionType()) {
|
|
||||||
case self::TYPE_EDIT:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return parent::hasChangeDetails();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderChangeDetails(PhabricatorUser $viewer) {
|
|
||||||
$json = new PhutilJSON();
|
|
||||||
switch ($this->getTransactionType()) {
|
|
||||||
case self::TYPE_EDIT:
|
|
||||||
return $this->renderTextCorpusChangeDetails(
|
|
||||||
$viewer,
|
|
||||||
$json->encodeFormatted($this->getOldValue()),
|
|
||||||
$json->encodeFormatted($this->getNewValue()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->renderTextCorpusChangeDetails(
|
|
||||||
$viewer,
|
|
||||||
$this->getOldValue(),
|
|
||||||
$this->getNewValue());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class HeraldRuleTransactionComment
|
|
||||||
extends PhabricatorApplicationTransactionComment {
|
|
||||||
|
|
||||||
public function getApplicationTransactionObject() {
|
|
||||||
return new HeraldRuleTransaction();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
65
src/applications/herald/view/HeraldRuleListView.php
Normal file
65
src/applications/herald/view/HeraldRuleListView.php
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class HeraldRuleListView
|
||||||
|
extends AphrontView {
|
||||||
|
|
||||||
|
private $rules;
|
||||||
|
|
||||||
|
public function setRules(array $rules) {
|
||||||
|
assert_instances_of($rules, 'HeraldRule');
|
||||||
|
$this->rules = $rules;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render() {
|
||||||
|
return $this->newObjectList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newObjectList() {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$rules = $this->rules;
|
||||||
|
|
||||||
|
$handles = $viewer->loadHandles(mpull($rules, 'getAuthorPHID'));
|
||||||
|
|
||||||
|
$content_type_map = HeraldAdapter::getEnabledAdapterMap($viewer);
|
||||||
|
|
||||||
|
$list = id(new PHUIObjectItemListView())
|
||||||
|
->setViewer($viewer);
|
||||||
|
foreach ($rules as $rule) {
|
||||||
|
$monogram = $rule->getMonogram();
|
||||||
|
|
||||||
|
$item = id(new PHUIObjectItemView())
|
||||||
|
->setObjectName($monogram)
|
||||||
|
->setHeader($rule->getName())
|
||||||
|
->setHref($rule->getURI());
|
||||||
|
|
||||||
|
if ($rule->isPersonalRule()) {
|
||||||
|
$item->addIcon('fa-user', pht('Personal Rule'));
|
||||||
|
$item->addByline(
|
||||||
|
pht(
|
||||||
|
'Authored by %s',
|
||||||
|
$handles[$rule->getAuthorPHID()]->renderLink()));
|
||||||
|
} else if ($rule->isObjectRule()) {
|
||||||
|
$item->addIcon('fa-briefcase', pht('Object Rule'));
|
||||||
|
} else {
|
||||||
|
$item->addIcon('fa-globe', pht('Global Rule'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($rule->getIsDisabled()) {
|
||||||
|
$item->setDisabled(true);
|
||||||
|
$item->addIcon('fa-lock grey', pht('Disabled'));
|
||||||
|
} else if (!$rule->hasValidAuthor()) {
|
||||||
|
$item->setDisabled(true);
|
||||||
|
$item->addIcon('fa-user grey', pht('Author Not Active'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$content_type_name = idx($content_type_map, $rule->getContentType());
|
||||||
|
$item->addAttribute(pht('Affects: %s', $content_type_name));
|
||||||
|
|
||||||
|
$list->addItem($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class HeraldRuleDisableTransaction
|
||||||
|
extends HeraldRuleTransactionType {
|
||||||
|
|
||||||
|
const TRANSACTIONTYPE = 'herald:disable';
|
||||||
|
|
||||||
|
public function generateOldValue($object) {
|
||||||
|
return (bool)$object->getIsDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateNewValue($object, $value) {
|
||||||
|
return (bool)$value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyInternalEffects($object, $value) {
|
||||||
|
$object->setIsDisabled((int)$value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle() {
|
||||||
|
if ($this->getNewValue()) {
|
||||||
|
return pht(
|
||||||
|
'%s disabled this rule.',
|
||||||
|
$this->renderAuthor());
|
||||||
|
} else {
|
||||||
|
return pht(
|
||||||
|
'%s enabled this rule.',
|
||||||
|
$this->renderAuthor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class HeraldRuleEditTransaction
|
||||||
|
extends HeraldRuleTransactionType {
|
||||||
|
|
||||||
|
const TRANSACTIONTYPE = 'herald:edit';
|
||||||
|
|
||||||
|
public function generateOldValue($object) {
|
||||||
|
return id(new HeraldRuleSerializer())
|
||||||
|
->serializeRule($object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyInternalEffects($object, $value) {
|
||||||
|
$new_state = id(new HeraldRuleSerializer())
|
||||||
|
->deserializeRuleComponents($value);
|
||||||
|
|
||||||
|
$object->setMustMatchAll((int)$new_state['match_all']);
|
||||||
|
$object->attachConditions($new_state['conditions']);
|
||||||
|
$object->attachActions($new_state['actions']);
|
||||||
|
|
||||||
|
$new_repetition = $new_state['repetition_policy'];
|
||||||
|
$object->setRepetitionPolicyStringConstant($new_repetition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyExternalEffects($object, $value) {
|
||||||
|
$object->saveConditions($object->getConditions());
|
||||||
|
$object->saveActions($object->getActions());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle() {
|
||||||
|
return pht(
|
||||||
|
'%s edited this rule.',
|
||||||
|
$this->renderAuthor());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasChangeDetailView() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->setOldText($old_json)
|
||||||
|
->setNewText($new_json);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class HeraldRuleNameTransaction
|
||||||
|
extends HeraldRuleTransactionType {
|
||||||
|
|
||||||
|
const TRANSACTIONTYPE = 'herald:name';
|
||||||
|
|
||||||
|
public function generateOldValue($object) {
|
||||||
|
return $object->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyInternalEffects($object, $value) {
|
||||||
|
$object->setName($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle() {
|
||||||
|
return pht(
|
||||||
|
'%s renamed this rule from %s to %s.',
|
||||||
|
$this->renderAuthor(),
|
||||||
|
$this->renderOldValue(),
|
||||||
|
$this->renderNewValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateTransactions($object, array $xactions) {
|
||||||
|
$errors = array();
|
||||||
|
|
||||||
|
if ($this->isEmptyTextTransaction($object->getName(), $xactions)) {
|
||||||
|
$errors[] = $this->newRequiredError(
|
||||||
|
pht('Rules must have a name.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$max_length = $object->getColumnMaximumByteLength('name');
|
||||||
|
foreach ($xactions as $xaction) {
|
||||||
|
$new_value = $xaction->getNewValue();
|
||||||
|
|
||||||
|
$new_length = strlen($new_value);
|
||||||
|
if ($new_length > $max_length) {
|
||||||
|
$errors[] = $this->newInvalidError(
|
||||||
|
pht(
|
||||||
|
'Rule names can be no longer than %s characters.',
|
||||||
|
new PhutilNumber($max_length)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class HeraldRuleTransactionType
|
||||||
|
extends PhabricatorModularTransactionType {}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ManiphestTaskUnlockEngine
|
||||||
|
extends PhabricatorUnlockEngine {
|
||||||
|
|
||||||
|
public function newUnlockOwnerTransactions($object, $user) {
|
||||||
|
return array(
|
||||||
|
$this->newTransaction($object)
|
||||||
|
->setTransactionType(ManiphestTaskOwnerTransaction::TRANSACTIONTYPE)
|
||||||
|
->setNewValue($user->getPHID()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -21,7 +21,8 @@ final class ManiphestTask extends ManiphestDAO
|
||||||
PhabricatorEditEngineSubtypeInterface,
|
PhabricatorEditEngineSubtypeInterface,
|
||||||
PhabricatorEditEngineLockableInterface,
|
PhabricatorEditEngineLockableInterface,
|
||||||
PhabricatorEditEngineMFAInterface,
|
PhabricatorEditEngineMFAInterface,
|
||||||
PhabricatorPolicyCodexInterface {
|
PhabricatorPolicyCodexInterface,
|
||||||
|
PhabricatorUnlockableInterface {
|
||||||
|
|
||||||
const MARKUP_FIELD_DESCRIPTION = 'markup:desc';
|
const MARKUP_FIELD_DESCRIPTION = 'markup:desc';
|
||||||
|
|
||||||
|
@ -649,4 +650,12 @@ final class ManiphestTask extends ManiphestDAO
|
||||||
return new ManiphestTaskPolicyCodex();
|
return new ManiphestTaskPolicyCodex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( PhabricatorUnlockableInterface )------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
public function newUnlockEngine() {
|
||||||
|
return new ManiphestTaskUnlockEngine();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,4 +123,14 @@ final class ManiphestTaskUnblockTransaction
|
||||||
return parent::shouldHideForFeed();
|
return parent::shouldHideForFeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRequiredCapabilities(
|
||||||
|
$object,
|
||||||
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
|
||||||
|
// When you close a task, we want to apply this transaction to its parents
|
||||||
|
// even if you can not edit (or even see) those parents, so don't require
|
||||||
|
// any capabilities. See PHI1059.
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,40 +8,72 @@ final class PhabricatorPolicyManagementUnlockWorkflow
|
||||||
->setName('unlock')
|
->setName('unlock')
|
||||||
->setSynopsis(
|
->setSynopsis(
|
||||||
pht(
|
pht(
|
||||||
'Unlock an object by setting its policies to allow anyone to view '.
|
'Unlock an object which has policies that prevent it from being '.
|
||||||
'and edit it.'))
|
'viewed or edited.'))
|
||||||
->setExamples('**unlock** D123')
|
->setExamples('**unlock** --view __user__ __object__')
|
||||||
->setArguments(
|
->setArguments(
|
||||||
array(
|
array(
|
||||||
array(
|
array(
|
||||||
'name' => 'objects',
|
'name' => 'view',
|
||||||
'wildcard' => true,
|
'param' => 'username',
|
||||||
|
'help' => pht(
|
||||||
|
'Change the view policy of an object so that the specified '.
|
||||||
|
'user may view it.'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'edit',
|
||||||
|
'param' => 'username',
|
||||||
|
'help' => pht(
|
||||||
|
'Change the edit policy of an object so that the specified '.
|
||||||
|
'user may edit it.'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'owner',
|
||||||
|
'param' => 'username',
|
||||||
|
'help' => pht(
|
||||||
|
'Change the owner of an object to the specified user.'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'objects',
|
||||||
|
'wildcard' => true,
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(PhutilArgumentParser $args) {
|
public function execute(PhutilArgumentParser $args) {
|
||||||
$console = PhutilConsole::getConsole();
|
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
$obj_names = $args->getArg('objects');
|
$object_names = $args->getArg('objects');
|
||||||
if (!$obj_names) {
|
if (!$object_names) {
|
||||||
throw new PhutilArgumentUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
pht('Specify the name of an object to unlock.'));
|
pht('Specify the name of an object to unlock.'));
|
||||||
} else if (count($obj_names) > 1) {
|
} else if (count($object_names) > 1) {
|
||||||
throw new PhutilArgumentUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
pht('Specify the name of exactly one object to unlock.'));
|
pht('Specify the name of exactly one object to unlock.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$object_name = head($object_names);
|
||||||
|
|
||||||
$object = id(new PhabricatorObjectQuery())
|
$object = id(new PhabricatorObjectQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withNames($obj_names)
|
->withNames(array($object_name))
|
||||||
->executeOne();
|
->executeOne();
|
||||||
|
|
||||||
if (!$object) {
|
if (!$object) {
|
||||||
$name = head($obj_names);
|
|
||||||
throw new PhutilArgumentUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
pht("No such object '%s'!", $name));
|
pht(
|
||||||
|
'Unable to find any object with the specified name ("%s").',
|
||||||
|
$object_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
$view_user = $this->loadUser($args->getArg('view'));
|
||||||
|
$edit_user = $this->loadUser($args->getArg('edit'));
|
||||||
|
$owner_user = $this->loadUser($args->getArg('owner'));
|
||||||
|
|
||||||
|
if (!$view_user && !$edit_user && !$owner_user) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Choose which capabilities to unlock with "--view", "--edit", '.
|
||||||
|
'or "--owner".'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$handle = id(new PhabricatorHandleQuery())
|
$handle = id(new PhabricatorHandleQuery())
|
||||||
|
@ -49,84 +81,73 @@ final class PhabricatorPolicyManagementUnlockWorkflow
|
||||||
->withPHIDs(array($object->getPHID()))
|
->withPHIDs(array($object->getPHID()))
|
||||||
->executeOne();
|
->executeOne();
|
||||||
|
|
||||||
if ($object instanceof PhabricatorApplication) {
|
echo tsprintf(
|
||||||
$application = $object;
|
"<bg:blue>** %s **</bg> %s\n",
|
||||||
|
pht('UNLOCKING'),
|
||||||
|
pht('Unlocking: %s', $handle->getFullName()));
|
||||||
|
|
||||||
$console->writeOut(
|
$engine = PhabricatorUnlockEngine::newUnlockEngineForObject($object);
|
||||||
"%s\n",
|
|
||||||
pht('Unlocking Application: %s', $handle->getFullName()));
|
|
||||||
|
|
||||||
// For applications, we can't unlock them in a normal way and don't want
|
$xactions = array();
|
||||||
// to unlock every capability, just view and edit.
|
if ($view_user) {
|
||||||
$capabilities = array(
|
$xactions[] = $engine->newUnlockViewTransactions($object, $view_user);
|
||||||
PhabricatorPolicyCapability::CAN_VIEW,
|
}
|
||||||
PhabricatorPolicyCapability::CAN_EDIT,
|
if ($edit_user) {
|
||||||
);
|
$xactions[] = $engine->newUnlockEditTransactions($object, $edit_user);
|
||||||
|
}
|
||||||
|
if ($owner_user) {
|
||||||
|
$xactions[] = $engine->newUnlockOwnerTransactions($object, $owner_user);
|
||||||
|
}
|
||||||
|
$xactions = array_mergev($xactions);
|
||||||
|
|
||||||
$key = 'phabricator.application-settings';
|
$policy_application = new PhabricatorPolicyApplication();
|
||||||
$config_entry = PhabricatorConfigEntry::loadConfigEntry($key);
|
$content_source = $this->newContentSource();
|
||||||
$value = $config_entry->getValue();
|
|
||||||
|
|
||||||
foreach ($capabilities as $capability) {
|
$editor = $object->getApplicationTransactionEditor()
|
||||||
if ($application->isCapabilityEditable($capability)) {
|
->setActor($viewer)
|
||||||
unset($value[$application->getPHID()]['policy'][$capability]);
|
->setActingAsPHID($policy_application->getPHID())
|
||||||
}
|
->setContinueOnMissingFields(true)
|
||||||
}
|
->setContinueOnNoEffect(true)
|
||||||
|
->setContentSource($content_source);
|
||||||
|
|
||||||
$config_entry->setValue($value);
|
$editor->applyTransactions($object, $xactions);
|
||||||
$config_entry->save();
|
|
||||||
|
|
||||||
$console->writeOut("%s\n", pht('Saved application.'));
|
echo tsprintf(
|
||||||
|
"<bg:green>** %s **</bg> %s\n",
|
||||||
|
pht('UNLOCKED'),
|
||||||
|
pht('Modified object policies.'));
|
||||||
|
|
||||||
return 0;
|
$uri = $handle->getURI();
|
||||||
|
if (strlen($uri)) {
|
||||||
|
echo tsprintf(
|
||||||
|
"\n **%s**: __%s__\n\n",
|
||||||
|
pht('Object URI'),
|
||||||
|
PhabricatorEnv::getURI($uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
$console->writeOut("%s\n", pht('Unlocking: %s', $handle->getFullName()));
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
$updated = false;
|
private function loadUser($username) {
|
||||||
foreach ($object->getCapabilities() as $capability) {
|
$viewer = $this->getViewer();
|
||||||
switch ($capability) {
|
|
||||||
case PhabricatorPolicyCapability::CAN_VIEW:
|
if ($username === null) {
|
||||||
try {
|
return null;
|
||||||
$object->setViewPolicy(PhabricatorPolicies::POLICY_USER);
|
|
||||||
$console->writeOut("%s\n", pht('Unlocked view policy.'));
|
|
||||||
$updated = true;
|
|
||||||
} catch (Exception $ex) {
|
|
||||||
$console->writeOut("%s\n", pht('View policy is not mutable.'));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case PhabricatorPolicyCapability::CAN_EDIT:
|
|
||||||
try {
|
|
||||||
$object->setEditPolicy(PhabricatorPolicies::POLICY_USER);
|
|
||||||
$console->writeOut("%s\n", pht('Unlocked edit policy.'));
|
|
||||||
$updated = true;
|
|
||||||
} catch (Exception $ex) {
|
|
||||||
$console->writeOut("%s\n", pht('Edit policy is not mutable.'));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case PhabricatorPolicyCapability::CAN_JOIN:
|
|
||||||
try {
|
|
||||||
$object->setJoinPolicy(PhabricatorPolicies::POLICY_USER);
|
|
||||||
$console->writeOut("%s\n", pht('Unlocked join policy.'));
|
|
||||||
$updated = true;
|
|
||||||
} catch (Exception $ex) {
|
|
||||||
$console->writeOut("%s\n", pht('Join policy is not mutable.'));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($updated) {
|
$user = id(new PhabricatorPeopleQuery())
|
||||||
$object->save();
|
->setViewer($viewer)
|
||||||
$console->writeOut("%s\n", pht('Saved object.'));
|
->withUsernames(array($username))
|
||||||
} else {
|
->executeOne();
|
||||||
$console->writeOut(
|
|
||||||
"%s\n",
|
if (!$user) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
pht(
|
pht(
|
||||||
'Object has no mutable policies. Try unlocking parent/container '.
|
'No user with username "%s" exists.',
|
||||||
'object instead. For example, to gain access to a commit, unlock '.
|
$username));
|
||||||
'the repository it belongs to.'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $user;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,29 +124,6 @@ final class PhabricatorRepositoryIdentityQuery
|
||||||
return $where;
|
return $where;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function didFilterPage(array $identities) {
|
|
||||||
$user_ids = array_filter(
|
|
||||||
mpull($identities, 'getCurrentEffectiveUserPHID', 'getID'));
|
|
||||||
if (!$user_ids) {
|
|
||||||
return $identities;
|
|
||||||
}
|
|
||||||
|
|
||||||
$users = id(new PhabricatorPeopleQuery())
|
|
||||||
->withPHIDs($user_ids)
|
|
||||||
->setViewer($this->getViewer())
|
|
||||||
->execute();
|
|
||||||
$users = mpull($users, null, 'getPHID');
|
|
||||||
|
|
||||||
foreach ($identities as $identity) {
|
|
||||||
if ($identity->hasEffectiveUser()) {
|
|
||||||
$user = idx($users, $identity->getCurrentEffectiveUserPHID());
|
|
||||||
$identity->attachEffectiveUser($user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $identities;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getQueryApplicationClass() {
|
public function getQueryApplicationClass() {
|
||||||
return 'PhabricatorDiffusionApplication';
|
return 'PhabricatorDiffusionApplication';
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,17 +14,6 @@ final class PhabricatorRepositoryIdentity
|
||||||
protected $manuallySetUserPHID;
|
protected $manuallySetUserPHID;
|
||||||
protected $currentEffectiveUserPHID;
|
protected $currentEffectiveUserPHID;
|
||||||
|
|
||||||
private $effectiveUser = self::ATTACHABLE;
|
|
||||||
|
|
||||||
public function attachEffectiveUser(PhabricatorUser $user) {
|
|
||||||
$this->effectiveUser = $user;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getEffectiveUser() {
|
|
||||||
return $this->assertAttached($this->effectiveUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getConfiguration() {
|
protected function getConfiguration() {
|
||||||
return array(
|
return array(
|
||||||
self::CONFIG_AUX_PHID => true,
|
self::CONFIG_AUX_PHID => true,
|
||||||
|
|
|
@ -144,7 +144,7 @@ EOTEXT
|
||||||
->setHeaderText(pht('Builtin and Saved Queries'))
|
->setHeaderText(pht('Builtin and Saved Queries'))
|
||||||
->setCollapsed(true)
|
->setCollapsed(true)
|
||||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||||
->appendChild($this->buildRemarkup($info))
|
->appendChild($this->newRemarkupDocumentationView($info))
|
||||||
->appendChild($table);
|
->appendChild($table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,7 +223,7 @@ EOTEXT
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($constants) {
|
if ($constants) {
|
||||||
$constant_lists[] = $this->buildRemarkup(
|
$constant_lists[] = $this->newRemarkupDocumentationView(
|
||||||
pht(
|
pht(
|
||||||
'Constants supported by the `%s` constraint:',
|
'Constants supported by the `%s` constraint:',
|
||||||
'statuses'));
|
'statuses'));
|
||||||
|
@ -283,7 +283,7 @@ EOTEXT
|
||||||
->setHeaderText(pht('Custom Query Constraints'))
|
->setHeaderText(pht('Custom Query Constraints'))
|
||||||
->setCollapsed(true)
|
->setCollapsed(true)
|
||||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||||
->appendChild($this->buildRemarkup($info))
|
->appendChild($this->newRemarkupDocumentationView($info))
|
||||||
->appendChild($table)
|
->appendChild($table)
|
||||||
->appendChild($constant_lists);
|
->appendChild($constant_lists);
|
||||||
}
|
}
|
||||||
|
@ -391,9 +391,9 @@ EOTEXT
|
||||||
->setHeaderText(pht('Result Ordering'))
|
->setHeaderText(pht('Result Ordering'))
|
||||||
->setCollapsed(true)
|
->setCollapsed(true)
|
||||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||||
->appendChild($this->buildRemarkup($orders_info))
|
->appendChild($this->newRemarkupDocumentationView($orders_info))
|
||||||
->appendChild($orders_table)
|
->appendChild($orders_table)
|
||||||
->appendChild($this->buildRemarkup($columns_info))
|
->appendChild($this->newRemarkupDocumentationView($columns_info))
|
||||||
->appendChild($columns_table);
|
->appendChild($columns_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -472,7 +472,7 @@ EOTEXT
|
||||||
->setHeaderText(pht('Object Fields'))
|
->setHeaderText(pht('Object Fields'))
|
||||||
->setCollapsed(true)
|
->setCollapsed(true)
|
||||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||||
->appendChild($this->buildRemarkup($info))
|
->appendChild($this->newRemarkupDocumentationView($info))
|
||||||
->appendChild($table);
|
->appendChild($table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,7 +562,7 @@ EOTEXT
|
||||||
->setHeaderText(pht('Attachments'))
|
->setHeaderText(pht('Attachments'))
|
||||||
->setCollapsed(true)
|
->setCollapsed(true)
|
||||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||||
->appendChild($this->buildRemarkup($info))
|
->appendChild($this->newRemarkupDocumentationView($info))
|
||||||
->appendChild($table);
|
->appendChild($table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,21 +633,7 @@ EOTEXT
|
||||||
->setHeaderText(pht('Paging and Limits'))
|
->setHeaderText(pht('Paging and Limits'))
|
||||||
->setCollapsed(true)
|
->setCollapsed(true)
|
||||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||||
->appendChild($this->buildRemarkup($info));
|
->appendChild($this->newRemarkupDocumentationView($info));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildRemarkup($remarkup) {
|
|
||||||
$viewer = $this->getViewer();
|
|
||||||
|
|
||||||
$view = new PHUIRemarkupView($viewer, $remarkup);
|
|
||||||
|
|
||||||
$view->setRemarkupOptions(
|
|
||||||
array(
|
|
||||||
PHUIRemarkupView::OPTION_PRESERVE_LINEBREAKS => false,
|
|
||||||
));
|
|
||||||
|
|
||||||
return id(new PHUIBoxView())
|
|
||||||
->appendChild($view)
|
|
||||||
->addPadding(PHUI::PADDING_LARGE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorDefaultUnlockEngine
|
||||||
|
extends PhabricatorUnlockEngine {}
|
81
src/applications/system/engine/PhabricatorUnlockEngine.php
Normal file
81
src/applications/system/engine/PhabricatorUnlockEngine.php
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class PhabricatorUnlockEngine
|
||||||
|
extends Phobject {
|
||||||
|
|
||||||
|
final public static function newUnlockEngineForObject($object) {
|
||||||
|
if (!($object instanceof PhabricatorApplicationTransactionInterface)) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Object ("%s") does not implement interface "%s", so this type '.
|
||||||
|
'of object can not be unlocked.',
|
||||||
|
phutil_describe_type($object),
|
||||||
|
'PhabricatorApplicationTransactionInterface'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($object instanceof PhabricatorUnlockableInterface) {
|
||||||
|
$engine = $object->newUnlockEngine();
|
||||||
|
} else {
|
||||||
|
$engine = new PhabricatorDefaultUnlockEngine();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newUnlockViewTransactions($object, $user) {
|
||||||
|
$type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||||
|
|
||||||
|
if (!$this->canApplyTransactionType($object, $type_view)) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Object view policy can not be unlocked because this object '.
|
||||||
|
'does not have a mutable view policy.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
$this->newTransaction($object)
|
||||||
|
->setTransactionType($type_view)
|
||||||
|
->setNewValue($user->getPHID()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newUnlockEditTransactions($object, $user) {
|
||||||
|
$type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||||
|
|
||||||
|
if (!$this->canApplyTransactionType($object, $type_edit)) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Object edit policy can not be unlocked because this object '.
|
||||||
|
'does not have a mutable edit policy.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
$this->newTransaction($object)
|
||||||
|
->setTransactionType($type_edit)
|
||||||
|
->setNewValue($user->getPHID()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newUnlockOwnerTransactions($object, $user) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Object owner can not be unlocked: the unlocking engine ("%s") for '.
|
||||||
|
'this object does not implement an owner unlocking mechanism.',
|
||||||
|
get_class($this)));
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected function canApplyTransactionType($object, $type) {
|
||||||
|
$xaction_types = $object->getApplicationTransactionEditor()
|
||||||
|
->getTransactionTypesForObject($object);
|
||||||
|
|
||||||
|
$xaction_types = array_fuse($xaction_types);
|
||||||
|
|
||||||
|
return isset($xaction_types[$type]);
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected function newTransaction($object) {
|
||||||
|
return $object->getApplicationTransactionTemplate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
interface PhabricatorUnlockableInterface {
|
||||||
|
|
||||||
|
public function newUnlockEngine();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEMPLATE IMPLEMENTATION /////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/* -( PhabricatorUnlockableInterface )------------------------------------- */
|
||||||
|
/*
|
||||||
|
|
||||||
|
public function newUnlockEngine() {
|
||||||
|
return new <<<...>>>UnlockEngine();
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
|
@ -8,15 +8,58 @@ final class TransactionSearchConduitAPIMethod
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMethodDescription() {
|
public function getMethodDescription() {
|
||||||
return pht('Read transactions for an object.');
|
return pht('Read transactions and comments for an object.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMethodStatus() {
|
public function getMethodDocumentation() {
|
||||||
return self::METHOD_STATUS_UNSTABLE;
|
$markup = pht(<<<EOREMARKUP
|
||||||
}
|
When an object (like a task) is edited, Phabricator creates a "transaction"
|
||||||
|
and applies it. This list of transactions on each object is the basis for
|
||||||
|
essentially all edits and comments in Phabricator. Reviewing the transaction
|
||||||
|
record allows you to see who edited an object, when, and how their edit changed
|
||||||
|
things.
|
||||||
|
|
||||||
public function getMethodStatusDescription() {
|
One common reason to call this method is that you're implmenting a webhook and
|
||||||
return pht('This method is new and experimental.');
|
just received a notification that an object has changed. See the Webhooks
|
||||||
|
documentation for more detailed discussion of this use case.
|
||||||
|
|
||||||
|
Constraints
|
||||||
|
===========
|
||||||
|
|
||||||
|
These constraints are supported:
|
||||||
|
|
||||||
|
- `phids` //Optional list<phid>.// Find specific transactions by PHID. This
|
||||||
|
is most likely to be useful if you're responding to a webhook notification
|
||||||
|
and want to inspect only the related events.
|
||||||
|
- `authorPHIDs` //Optional list<phid>.// Find transactions with particular
|
||||||
|
authors.
|
||||||
|
|
||||||
|
Transaction Format
|
||||||
|
==================
|
||||||
|
|
||||||
|
Each transaction has custom data describing what the transaction did. The
|
||||||
|
format varies from transaction to transaction. The easiest way to figure out
|
||||||
|
exactly what a particular transaction looks like is to make the associated kind
|
||||||
|
of edit to a test object, then query that object.
|
||||||
|
|
||||||
|
Not all transactions have data: by default, transactions have a `null` "type"
|
||||||
|
and no additional data. This API does not expose raw transaction data because
|
||||||
|
some of it is internal, oddly named, misspelled, confusing, not useful, or
|
||||||
|
could create security or policy problems to expose directly.
|
||||||
|
|
||||||
|
New transactions are exposed (with correctly spelled, comprehensible types and
|
||||||
|
useful, reasonable fields) as we become aware of use cases for them.
|
||||||
|
|
||||||
|
EOREMARKUP
|
||||||
|
);
|
||||||
|
|
||||||
|
$markup = $this->newRemarkupDocumentationView($markup);
|
||||||
|
|
||||||
|
return id(new PHUIObjectBoxView())
|
||||||
|
->setCollapsed(true)
|
||||||
|
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||||
|
->setHeaderText(pht('Method Details'))
|
||||||
|
->appendChild($markup);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function defineParamTypes() {
|
protected function defineParamTypes() {
|
||||||
|
@ -73,24 +116,8 @@ final class TransactionSearchConduitAPIMethod
|
||||||
->setViewer($viewer);
|
->setViewer($viewer);
|
||||||
|
|
||||||
$constraints = $request->getValue('constraints', array());
|
$constraints = $request->getValue('constraints', array());
|
||||||
PhutilTypeSpec::checkMap(
|
|
||||||
$constraints,
|
|
||||||
array(
|
|
||||||
'phids' => 'optional list<string>',
|
|
||||||
));
|
|
||||||
|
|
||||||
$with_phids = idx($constraints, 'phids');
|
$xaction_query = $this->applyConstraints($constraints, $xaction_query);
|
||||||
|
|
||||||
if ($with_phids === array()) {
|
|
||||||
throw new Exception(
|
|
||||||
pht(
|
|
||||||
'Constraint "phids" to "transaction.search" requires nonempty list, '.
|
|
||||||
'empty list provided.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($with_phids) {
|
|
||||||
$xaction_query->withPHIDs($with_phids);
|
|
||||||
}
|
|
||||||
|
|
||||||
$xactions = $xaction_query->executeWithCursorPager($pager);
|
$xactions = $xaction_query->executeWithCursorPager($pager);
|
||||||
|
|
||||||
|
@ -218,6 +245,14 @@ final class TransactionSearchConduitAPIMethod
|
||||||
case PhabricatorTransactions::TYPE_CREATE:
|
case PhabricatorTransactions::TYPE_CREATE:
|
||||||
$type = 'create';
|
$type = 'create';
|
||||||
break;
|
break;
|
||||||
|
case PhabricatorTransactions::TYPE_EDGE:
|
||||||
|
switch ($xaction->getMetadataValue('edge:type')) {
|
||||||
|
case PhabricatorProjectObjectHasProjectEdgeType::EDGECONST:
|
||||||
|
$type = 'projects';
|
||||||
|
$fields = $this->newEdgeTransactionFields($xaction);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,4 +275,69 @@ final class TransactionSearchConduitAPIMethod
|
||||||
|
|
||||||
return $this->addPagerResults($results, $pager);
|
return $this->addPagerResults($results, $pager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function applyConstraints(
|
||||||
|
array $constraints,
|
||||||
|
PhabricatorApplicationTransactionQuery $query) {
|
||||||
|
|
||||||
|
PhutilTypeSpec::checkMap(
|
||||||
|
$constraints,
|
||||||
|
array(
|
||||||
|
'phids' => 'optional list<string>',
|
||||||
|
'authorPHIDs' => 'optional list<string>',
|
||||||
|
));
|
||||||
|
|
||||||
|
$with_phids = idx($constraints, 'phids');
|
||||||
|
|
||||||
|
if ($with_phids === array()) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Constraint "phids" to "transaction.search" requires nonempty list, '.
|
||||||
|
'empty list provided.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($with_phids) {
|
||||||
|
$query->withPHIDs($with_phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
$with_authors = idx($constraints, 'authorPHIDs');
|
||||||
|
if ($with_authors === array()) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Constraint "authorPHIDs" to "transaction.search" requires '.
|
||||||
|
'nonempty list, empty list provided.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($with_authors) {
|
||||||
|
$query->withAuthorPHIDs($with_authors);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newEdgeTransactionFields(
|
||||||
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
|
||||||
|
$record = PhabricatorEdgeChangeRecord::newFromTransaction($xaction);
|
||||||
|
|
||||||
|
$operations = array();
|
||||||
|
foreach ($record->getAddedPHIDs() as $phid) {
|
||||||
|
$operations[] = array(
|
||||||
|
'operation' => 'add',
|
||||||
|
'phid' => $phid,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($record->getRemovedPHIDs() as $phid) {
|
||||||
|
$operations[] = array(
|
||||||
|
'operation' => 'remove',
|
||||||
|
'phid' => $phid,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'operations' => $operations,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3779,9 +3779,14 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
|
|
||||||
$this->mustEncrypt = $adapter->getMustEncryptReasons();
|
$this->mustEncrypt = $adapter->getMustEncryptReasons();
|
||||||
|
|
||||||
|
$apply_xactions = $this->didApplyHeraldRules($object, $adapter, $xscript);
|
||||||
|
assert_instances_of($apply_xactions, 'PhabricatorApplicationTransaction');
|
||||||
|
|
||||||
|
$queue_xactions = $adapter->getQueuedTransactions();
|
||||||
|
|
||||||
return array_merge(
|
return array_merge(
|
||||||
$this->didApplyHeraldRules($object, $adapter, $xscript),
|
array_values($apply_xactions),
|
||||||
$adapter->getQueuedTransactions());
|
array_values($queue_xactions));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function didApplyHeraldRules(
|
protected function didApplyHeraldRules(
|
||||||
|
|
|
@ -15,6 +15,7 @@ final class PhabricatorWorkerBulkJobEditor
|
||||||
$types = parent::getTransactionTypes();
|
$types = parent::getTransactionTypes();
|
||||||
|
|
||||||
$types[] = PhabricatorWorkerBulkJobTransaction::TYPE_STATUS;
|
$types[] = PhabricatorWorkerBulkJobTransaction::TYPE_STATUS;
|
||||||
|
$types[] = PhabricatorTransactions::TYPE_EDGE;
|
||||||
|
|
||||||
return $types;
|
return $types;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,23 +11,64 @@ final class PhabricatorWorkerManagementExecuteWorkflow
|
||||||
pht(
|
pht(
|
||||||
'Execute a task explicitly. This command ignores leases, is '.
|
'Execute a task explicitly. This command ignores leases, is '.
|
||||||
'dangerous, and may cause work to be performed twice.'))
|
'dangerous, and may cause work to be performed twice.'))
|
||||||
->setArguments($this->getTaskSelectionArguments());
|
->setArguments(
|
||||||
|
array_merge(
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'name' => 'retry',
|
||||||
|
'help' => pht('Retry archived tasks.'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'repeat',
|
||||||
|
'help' => pht('Repeat archived, successful tasks.'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
$this->getTaskSelectionArguments()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(PhutilArgumentParser $args) {
|
public function execute(PhutilArgumentParser $args) {
|
||||||
$console = PhutilConsole::getConsole();
|
$console = PhutilConsole::getConsole();
|
||||||
$tasks = $this->loadTasks($args);
|
$tasks = $this->loadTasks($args);
|
||||||
|
|
||||||
|
$is_retry = $args->getArg('retry');
|
||||||
|
$is_repeat = $args->getArg('repeat');
|
||||||
|
|
||||||
foreach ($tasks as $task) {
|
foreach ($tasks as $task) {
|
||||||
$can_execute = !$task->isArchived();
|
$can_execute = !$task->isArchived();
|
||||||
if (!$can_execute) {
|
if (!$can_execute) {
|
||||||
$console->writeOut(
|
if (!$is_retry) {
|
||||||
|
$console->writeOut(
|
||||||
|
"**<bg:yellow> %s </bg>** %s\n",
|
||||||
|
pht('ARCHIVED'),
|
||||||
|
pht(
|
||||||
|
'%s is already archived, and will not be executed. '.
|
||||||
|
'Use "--retry" to execute archived tasks.',
|
||||||
|
$this->describeTask($task)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result_success = PhabricatorWorkerArchiveTask::RESULT_SUCCESS;
|
||||||
|
if ($task->getResult() == $result_success) {
|
||||||
|
if (!$is_repeat) {
|
||||||
|
$console->writeOut(
|
||||||
|
"**<bg:yellow> %s </bg>** %s\n",
|
||||||
|
pht('SUCCEEDED'),
|
||||||
|
pht(
|
||||||
|
'%s has already succeeded, and will not be retried. '.
|
||||||
|
'Use "--repeat" to repeat successful tasks.',
|
||||||
|
$this->describeTask($task)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo tsprintf(
|
||||||
"**<bg:yellow> %s </bg>** %s\n",
|
"**<bg:yellow> %s </bg>** %s\n",
|
||||||
pht('ARCHIVED'),
|
pht('ARCHIVED'),
|
||||||
pht(
|
pht(
|
||||||
'%s is already archived, and can not be executed.',
|
'Unarchiving %s.',
|
||||||
$this->describeTask($task)));
|
$this->describeTask($task)));
|
||||||
continue;
|
|
||||||
|
$task = $task->unarchiveTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: This ignores leases, maybe it should respect them without
|
// NOTE: This ignores leases, maybe it should respect them without
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue