@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'master' into redesign-2015

+1891 -489
+14
resources/sql/autopatches/20150617.harbor.1.lint.sql
··· 1 + CREATE TABLE {$NAMESPACE}_harbormaster.harbormaster_buildlintmessage ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + buildTargetPHID VARBINARY(64) NOT NULL, 4 + path LONGTEXT NOT NULL, 5 + line INT UNSIGNED, 6 + characterOffset INT UNSIGNED, 7 + code VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL, 8 + severity VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL, 9 + name VARCHAR(255) COLLATE {$COLLATE_TEXT} NOT NULL, 10 + properties LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 11 + dateCreated INT UNSIGNED NOT NULL, 12 + dateModified INT UNSIGNED NOT NULL, 13 + KEY `key_target` (buildTargetPHID) 14 + ) ENGINE=INNODB, COLLATE {$COLLATE_TEXT};
+13
resources/sql/autopatches/20150617.harbor.2.unit.sql
··· 1 + CREATE TABLE {$NAMESPACE}_harbormaster.harbormaster_buildunitmessage ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + buildTargetPHID VARBINARY(64) NOT NULL, 4 + engine VARCHAR(255) COLLATE {$COLLATE_TEXT} NOT NULL, 5 + namespace VARCHAR(255) COLLATE {$COLLATE_TEXT} NOT NULL, 6 + name VARCHAR(255) COLLATE {$COLLATE_TEXT} NOT NULL, 7 + result VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL, 8 + duration DOUBLE, 9 + properties LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 10 + dateCreated INT UNSIGNED NOT NULL, 11 + dateModified INT UNSIGNED NOT NULL, 12 + KEY `key_target` (buildTargetPHID) 13 + ) ENGINE=INNODB, COLLATE {$COLLATE_TEXT};
+2
resources/sql/autopatches/20150618.harbor.1.planauto.sql
··· 1 + ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_buildplan 2 + ADD planAutoKey VARCHAR(32) COLLATE {$COLLATE_TEXT};
+2
resources/sql/autopatches/20150618.harbor.2.stepauto.sql
··· 1 + ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_buildstep 2 + ADD stepAutoKey VARCHAR(32) COLLATE {$COLLATE_TEXT};
+2
resources/sql/autopatches/20150618.harbor.3.buildauto.sql
··· 1 + ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_build 2 + ADD planAutoKey VARCHAR(32) COLLATE {$COLLATE_TEXT};
+2
resources/sql/autopatches/20150621.phrase.1.sql
··· 1 + ALTER TABLE {$NAMESPACE}_passphrase.passphrase_credential 2 + ADD authorPHID VARBINARY(64) NOT NULL;
+2
resources/sql/autopatches/20150621.phrase.2.sql
··· 1 + ALTER TABLE {$NAMESPACE}_passphrase.passphrase_credential 2 + ADD spacePHID VARBINARY(64);
+27 -6
src/__phutil_library_map__.php
··· 360 360 'DifferentialDiffTableOfContentsView' => 'applications/differential/view/DifferentialDiffTableOfContentsView.php', 361 361 'DifferentialDiffTestCase' => 'applications/differential/storage/__tests__/DifferentialDiffTestCase.php', 362 362 'DifferentialDiffTransaction' => 'applications/differential/storage/DifferentialDiffTransaction.php', 363 + 'DifferentialDiffTransactionQuery' => 'applications/differential/query/DifferentialDiffTransactionQuery.php', 363 364 'DifferentialDiffViewController' => 'applications/differential/controller/DifferentialDiffViewController.php', 364 365 'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'applications/differential/doorkeeper/DifferentialDoorkeeperRevisionFeedStoryPublisher.php', 365 366 'DifferentialDraft' => 'applications/differential/storage/DifferentialDraft.php', ··· 826 827 'FundInitiativeTransactionQuery' => 'applications/fund/query/FundInitiativeTransactionQuery.php', 827 828 'FundInitiativeViewController' => 'applications/fund/controller/FundInitiativeViewController.php', 828 829 'FundSchemaSpec' => 'applications/fund/storage/FundSchemaSpec.php', 830 + 'HarbormasterArcLintBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterArcLintBuildStepImplementation.php', 831 + 'HarbormasterArcUnitBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterArcUnitBuildStepImplementation.php', 832 + 'HarbormasterAutotargetsTestCase' => 'applications/harbormaster/__tests__/HarbormasterAutotargetsTestCase.php', 829 833 'HarbormasterBuild' => 'applications/harbormaster/storage/build/HarbormasterBuild.php', 830 834 'HarbormasterBuildAbortedException' => 'applications/harbormaster/exception/HarbormasterBuildAbortedException.php', 831 835 'HarbormasterBuildActionController' => 'applications/harbormaster/controller/HarbormasterBuildActionController.php', 836 + 'HarbormasterBuildArcanistAutoplan' => 'applications/harbormaster/autoplan/HarbormasterBuildArcanistAutoplan.php', 832 837 'HarbormasterBuildArtifact' => 'applications/harbormaster/storage/build/HarbormasterBuildArtifact.php', 833 838 'HarbormasterBuildArtifactQuery' => 'applications/harbormaster/query/HarbormasterBuildArtifactQuery.php', 839 + 'HarbormasterBuildAutoplan' => 'applications/harbormaster/autoplan/HarbormasterBuildAutoplan.php', 834 840 'HarbormasterBuildCommand' => 'applications/harbormaster/storage/HarbormasterBuildCommand.php', 835 841 'HarbormasterBuildDependencyDatasource' => 'applications/harbormaster/typeahead/HarbormasterBuildDependencyDatasource.php', 836 842 'HarbormasterBuildEngine' => 'applications/harbormaster/engine/HarbormasterBuildEngine.php', 837 843 'HarbormasterBuildFailureException' => 'applications/harbormaster/exception/HarbormasterBuildFailureException.php', 838 844 'HarbormasterBuildGraph' => 'applications/harbormaster/engine/HarbormasterBuildGraph.php', 839 - 'HarbormasterBuildItem' => 'applications/harbormaster/storage/build/HarbormasterBuildItem.php', 840 - 'HarbormasterBuildItemPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildItemPHIDType.php', 841 - 'HarbormasterBuildItemQuery' => 'applications/harbormaster/query/HarbormasterBuildItemQuery.php', 845 + 'HarbormasterBuildLintMessage' => 'applications/harbormaster/storage/build/HarbormasterBuildLintMessage.php', 842 846 'HarbormasterBuildLog' => 'applications/harbormaster/storage/build/HarbormasterBuildLog.php', 843 847 'HarbormasterBuildLogPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildLogPHIDType.php', 844 848 'HarbormasterBuildLogQuery' => 'applications/harbormaster/query/HarbormasterBuildLogQuery.php', ··· 870 874 'HarbormasterBuildTransaction' => 'applications/harbormaster/storage/HarbormasterBuildTransaction.php', 871 875 'HarbormasterBuildTransactionEditor' => 'applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php', 872 876 'HarbormasterBuildTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildTransactionQuery.php', 877 + 'HarbormasterBuildUnitMessage' => 'applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php', 873 878 'HarbormasterBuildViewController' => 'applications/harbormaster/controller/HarbormasterBuildViewController.php', 874 879 'HarbormasterBuildWorker' => 'applications/harbormaster/worker/HarbormasterBuildWorker.php', 875 880 'HarbormasterBuildable' => 'applications/harbormaster/storage/HarbormasterBuildable.php', ··· 901 906 'HarbormasterPlanRunController' => 'applications/harbormaster/controller/HarbormasterPlanRunController.php', 902 907 'HarbormasterPlanViewController' => 'applications/harbormaster/controller/HarbormasterPlanViewController.php', 903 908 'HarbormasterPublishFragmentBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterPublishFragmentBuildStepImplementation.php', 909 + 'HarbormasterQueryAutotargetsConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterQueryAutotargetsConduitAPIMethod.php', 904 910 'HarbormasterQueryBuildablesConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterQueryBuildablesConduitAPIMethod.php', 905 911 'HarbormasterQueryBuildsConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterQueryBuildsConduitAPIMethod.php', 906 912 'HarbormasterRemarkupRule' => 'applications/harbormaster/remarkup/HarbormasterRemarkupRule.php', ··· 911 917 'HarbormasterStepAddController' => 'applications/harbormaster/controller/HarbormasterStepAddController.php', 912 918 'HarbormasterStepDeleteController' => 'applications/harbormaster/controller/HarbormasterStepDeleteController.php', 913 919 'HarbormasterStepEditController' => 'applications/harbormaster/controller/HarbormasterStepEditController.php', 920 + 'HarbormasterTargetEngine' => 'applications/harbormaster/engine/HarbormasterTargetEngine.php', 914 921 'HarbormasterTargetWorker' => 'applications/harbormaster/worker/HarbormasterTargetWorker.php', 915 922 'HarbormasterThrowExceptionBuildStep' => 'applications/harbormaster/step/HarbormasterThrowExceptionBuildStep.php', 916 923 'HarbormasterUIEventListener' => 'applications/harbormaster/event/HarbormasterUIEventListener.php', ··· 1262 1269 'PassphraseConduitAPIMethod' => 'applications/passphrase/conduit/PassphraseConduitAPIMethod.php', 1263 1270 'PassphraseController' => 'applications/passphrase/controller/PassphraseController.php', 1264 1271 'PassphraseCredential' => 'applications/passphrase/storage/PassphraseCredential.php', 1272 + 'PassphraseCredentialAuthorPolicyRule' => 'applications/passphrase/policyrule/PassphraseCredentialAuthorPolicyRule.php', 1265 1273 'PassphraseCredentialConduitController' => 'applications/passphrase/controller/PassphraseCredentialConduitController.php', 1266 1274 'PassphraseCredentialControl' => 'applications/passphrase/view/PassphraseCredentialControl.php', 1267 1275 'PassphraseCredentialCreateController' => 'applications/passphrase/controller/PassphraseCredentialCreateController.php', ··· 1281 1289 'PassphraseCredentialTypeTestCase' => 'applications/passphrase/credentialtype/__tests__/PassphraseCredentialTypeTestCase.php', 1282 1290 'PassphraseCredentialViewController' => 'applications/passphrase/controller/PassphraseCredentialViewController.php', 1283 1291 'PassphraseDAO' => 'applications/passphrase/storage/PassphraseDAO.php', 1292 + 'PassphraseDefaultEditCapability' => 'applications/passphrase/capability/PassphraseDefaultEditCapability.php', 1293 + 'PassphraseDefaultViewCapability' => 'applications/passphrase/capability/PassphraseDefaultViewCapability.php', 1284 1294 'PassphraseNoteCredentialType' => 'applications/passphrase/credentialtype/PassphraseNoteCredentialType.php', 1285 1295 'PassphrasePasswordCredentialType' => 'applications/passphrase/credentialtype/PassphrasePasswordCredentialType.php', 1286 1296 'PassphrasePasswordKey' => 'applications/passphrase/keys/PassphrasePasswordKey.php', ··· 3710 3720 'DifferentialDiffTableOfContentsView' => 'AphrontView', 3711 3721 'DifferentialDiffTestCase' => 'PhutilTestCase', 3712 3722 'DifferentialDiffTransaction' => 'PhabricatorApplicationTransaction', 3723 + 'DifferentialDiffTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 3713 3724 'DifferentialDiffViewController' => 'DifferentialController', 3714 3725 'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'DoorkeeperFeedStoryPublisher', 3715 3726 'DifferentialDraft' => 'DifferentialDAO', ··· 4241 4252 'FundInitiativeTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 4242 4253 'FundInitiativeViewController' => 'FundController', 4243 4254 'FundSchemaSpec' => 'PhabricatorConfigSchemaSpec', 4255 + 'HarbormasterArcLintBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 4256 + 'HarbormasterArcUnitBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 4257 + 'HarbormasterAutotargetsTestCase' => 'PhabricatorTestCase', 4244 4258 'HarbormasterBuild' => array( 4245 4259 'HarbormasterDAO', 4246 4260 'PhabricatorApplicationTransactionInterface', ··· 4248 4262 ), 4249 4263 'HarbormasterBuildAbortedException' => 'Exception', 4250 4264 'HarbormasterBuildActionController' => 'HarbormasterController', 4265 + 'HarbormasterBuildArcanistAutoplan' => 'HarbormasterBuildAutoplan', 4251 4266 'HarbormasterBuildArtifact' => array( 4252 4267 'HarbormasterDAO', 4253 4268 'PhabricatorPolicyInterface', 4254 4269 ), 4255 4270 'HarbormasterBuildArtifactQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 4271 + 'HarbormasterBuildAutoplan' => 'Phobject', 4256 4272 'HarbormasterBuildCommand' => 'HarbormasterDAO', 4257 4273 'HarbormasterBuildDependencyDatasource' => 'PhabricatorTypeaheadDatasource', 4258 4274 'HarbormasterBuildEngine' => 'Phobject', 4259 4275 'HarbormasterBuildFailureException' => 'Exception', 4260 4276 'HarbormasterBuildGraph' => 'AbstractDirectedGraph', 4261 - 'HarbormasterBuildItem' => 'HarbormasterDAO', 4262 - 'HarbormasterBuildItemPHIDType' => 'PhabricatorPHIDType', 4263 - 'HarbormasterBuildItemQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 4277 + 'HarbormasterBuildLintMessage' => 'HarbormasterDAO', 4264 4278 'HarbormasterBuildLog' => array( 4265 4279 'HarbormasterDAO', 4266 4280 'PhabricatorPolicyInterface', ··· 4314 4328 'HarbormasterBuildTransaction' => 'PhabricatorApplicationTransaction', 4315 4329 'HarbormasterBuildTransactionEditor' => 'PhabricatorApplicationTransactionEditor', 4316 4330 'HarbormasterBuildTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 4331 + 'HarbormasterBuildUnitMessage' => 'HarbormasterDAO', 4317 4332 'HarbormasterBuildViewController' => 'HarbormasterController', 4318 4333 'HarbormasterBuildWorker' => 'HarbormasterWorker', 4319 4334 'HarbormasterBuildable' => array( ··· 4349 4364 'HarbormasterPlanRunController' => 'HarbormasterController', 4350 4365 'HarbormasterPlanViewController' => 'HarbormasterPlanController', 4351 4366 'HarbormasterPublishFragmentBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 4367 + 'HarbormasterQueryAutotargetsConduitAPIMethod' => 'HarbormasterConduitAPIMethod', 4352 4368 'HarbormasterQueryBuildablesConduitAPIMethod' => 'HarbormasterConduitAPIMethod', 4353 4369 'HarbormasterQueryBuildsConduitAPIMethod' => 'HarbormasterConduitAPIMethod', 4354 4370 'HarbormasterRemarkupRule' => 'PhabricatorObjectRemarkupRule', ··· 4359 4375 'HarbormasterStepAddController' => 'HarbormasterController', 4360 4376 'HarbormasterStepDeleteController' => 'HarbormasterController', 4361 4377 'HarbormasterStepEditController' => 'HarbormasterController', 4378 + 'HarbormasterTargetEngine' => 'Phobject', 4362 4379 'HarbormasterTargetWorker' => 'HarbormasterWorker', 4363 4380 'HarbormasterThrowExceptionBuildStep' => 'HarbormasterBuildStepImplementation', 4364 4381 'HarbormasterUIEventListener' => 'PhabricatorEventListener', ··· 4768 4785 'PhabricatorApplicationTransactionInterface', 4769 4786 'PhabricatorPolicyInterface', 4770 4787 'PhabricatorDestructibleInterface', 4788 + 'PhabricatorSpacesInterface', 4771 4789 ), 4790 + 'PassphraseCredentialAuthorPolicyRule' => 'PhabricatorPolicyRule', 4772 4791 'PassphraseCredentialConduitController' => 'PassphraseController', 4773 4792 'PassphraseCredentialControl' => 'AphrontFormControl', 4774 4793 'PassphraseCredentialCreateController' => 'PassphraseController', ··· 4788 4807 'PassphraseCredentialTypeTestCase' => 'PhabricatorTestCase', 4789 4808 'PassphraseCredentialViewController' => 'PassphraseController', 4790 4809 'PassphraseDAO' => 'PhabricatorLiskDAO', 4810 + 'PassphraseDefaultEditCapability' => 'PhabricatorPolicyCapability', 4811 + 'PassphraseDefaultViewCapability' => 'PhabricatorPolicyCapability', 4791 4812 'PassphraseNoteCredentialType' => 'PassphraseCredentialType', 4792 4813 'PassphrasePasswordCredentialType' => 'PassphraseCredentialType', 4793 4814 'PassphrasePasswordKey' => 'PassphraseAbstractKey',
+17
src/applications/base/PhabricatorApplication.php
··· 606 606 return idx($spec, 'template'); 607 607 } 608 608 609 + final public function getDefaultObjectTypePolicyMap() { 610 + $map = array(); 611 + 612 + foreach ($this->getCustomCapabilities() as $capability => $spec) { 613 + if (empty($spec['template'])) { 614 + continue; 615 + } 616 + if (empty($spec['capability'])) { 617 + continue; 618 + } 619 + $default = $this->getPolicy($capability); 620 + $map[$spec['template']][$spec['capability']] = $default; 621 + } 622 + 623 + return $map; 624 + } 625 + 609 626 public function getApplicationSearchDocumentTypes() { 610 627 return array(); 611 628 }
+1
src/applications/countdown/application/PhabricatorCountdownApplication.php
··· 53 53 PhabricatorCountdownDefaultViewCapability::CAPABILITY => array( 54 54 'caption' => pht('Default view policy for new countdowns.'), 55 55 'template' => PhabricatorCountdownCountdownPHIDType::TYPECONST, 56 + 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 56 57 ), 57 58 ); 58 59 }
+1
src/applications/differential/application/PhabricatorDifferentialApplication.php
··· 187 187 DifferentialDefaultViewCapability::CAPABILITY => array( 188 188 'caption' => pht('Default view policy for newly created revisions.'), 189 189 'template' => DifferentialRevisionPHIDType::TYPECONST, 190 + 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 190 191 ), 191 192 ); 192 193 }
+10
src/applications/differential/query/DifferentialDiffTransactionQuery.php
··· 1 + <?php 2 + 3 + final class DifferentialDiffTransactionQuery 4 + extends PhabricatorApplicationTransactionQuery { 5 + 6 + public function getTemplateApplicationTransaction() { 7 + return new DifferentialDiffTransaction(); 8 + } 9 + 10 + }
+9 -7
src/applications/differential/storage/DifferentialDiff.php
··· 381 381 $results = array(); 382 382 383 383 $results['buildable.diff'] = $this->getID(); 384 - $revision = $this->getRevision(); 385 - $results['buildable.revision'] = $revision->getID(); 386 - $repo = $revision->getRepository(); 384 + if ($this->revisionID) { 385 + $revision = $this->getRevision(); 386 + $results['buildable.revision'] = $revision->getID(); 387 + $repo = $revision->getRepository(); 387 388 388 - if ($repo) { 389 - $results['repository.callsign'] = $repo->getCallsign(); 390 - $results['repository.vcs'] = $repo->getVersionControlSystem(); 391 - $results['repository.uri'] = $repo->getPublicCloneURI(); 389 + if ($repo) { 390 + $results['repository.callsign'] = $repo->getCallsign(); 391 + $results['repository.vcs'] = $repo->getVersionControlSystem(); 392 + $results['repository.uri'] = $repo->getPublicCloneURI(); 393 + } 392 394 } 393 395 394 396 return $results;
+2
src/applications/diffusion/application/PhabricatorDiffusionApplication.php
··· 142 142 return array( 143 143 DiffusionDefaultViewCapability::CAPABILITY => array( 144 144 'template' => PhabricatorRepositoryRepositoryPHIDType::TYPECONST, 145 + 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 145 146 ), 146 147 DiffusionDefaultEditCapability::CAPABILITY => array( 147 148 'default' => PhabricatorPolicies::POLICY_ADMIN, 148 149 'template' => PhabricatorRepositoryRepositoryPHIDType::TYPECONST, 150 + 'capability' => PhabricatorPolicyCapability::CAN_EDIT, 149 151 ), 150 152 DiffusionDefaultPushCapability::CAPABILITY => array( 151 153 'template' => PhabricatorRepositoryRepositoryPHIDType::TYPECONST,
+2
src/applications/diviner/application/PhabricatorDivinerApplication.php
··· 57 57 return array( 58 58 DivinerDefaultViewCapability::CAPABILITY => array( 59 59 'template' => DivinerBookPHIDType::TYPECONST, 60 + 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 60 61 ), 61 62 DivinerDefaultEditCapability::CAPABILITY => array( 62 63 'default' => PhabricatorPolicies::POLICY_ADMIN, 63 64 'template' => DivinerBookPHIDType::TYPECONST, 65 + 'capability' => PhabricatorPolicyCapability::CAN_EDIT, 64 66 ), 65 67 ); 66 68 }
+2
src/applications/drydock/application/PhabricatorDrydockApplication.php
··· 74 74 return array( 75 75 DrydockDefaultViewCapability::CAPABILITY => array( 76 76 'template' => DrydockBlueprintPHIDType::TYPECONST, 77 + 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 77 78 ), 78 79 DrydockDefaultEditCapability::CAPABILITY => array( 79 80 'default' => PhabricatorPolicies::POLICY_ADMIN, 80 81 'template' => DrydockBlueprintPHIDType::TYPECONST, 82 + 'capability' => PhabricatorPolicyCapability::CAN_EDIT, 81 83 ), 82 84 DrydockCreateBlueprintsCapability::CAPABILITY => array( 83 85 'default' => PhabricatorPolicies::POLICY_ADMIN,
+1
src/applications/files/application/PhabricatorFilesApplication.php
··· 61 61 FilesDefaultViewCapability::CAPABILITY => array( 62 62 'caption' => pht('Default view policy for newly created files.'), 63 63 'template' => PhabricatorFileFilePHIDType::TYPECONST, 64 + 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 64 65 ), 65 66 ); 66 67 }
+61
src/applications/harbormaster/__tests__/HarbormasterAutotargetsTestCase.php
··· 1 + <?php 2 + 3 + final class HarbormasterAutotargetsTestCase extends PhabricatorTestCase { 4 + 5 + protected function getPhabricatorTestCaseConfiguration() { 6 + return array( 7 + self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true, 8 + ); 9 + } 10 + 11 + public function testGenerateHarbormasterAutotargets() { 12 + $viewer = $this->generateNewTestUser(); 13 + 14 + $raw_diff = <<<EODIFF 15 + diff --git a/fruit b/fruit 16 + new file mode 100644 17 + index 0000000..1c0f49d 18 + --- /dev/null 19 + +++ b/fruit 20 + @@ -0,0 +1,2 @@ 21 + +apal 22 + +banan 23 + EODIFF; 24 + 25 + $parser = new ArcanistDiffParser(); 26 + $changes = $parser->parseDiff($raw_diff); 27 + 28 + $diff = DifferentialDiff::newFromRawChanges($viewer, $changes) 29 + ->setLintStatus(DifferentialLintStatus::LINT_AUTO_SKIP) 30 + ->setUnitStatus(DifferentialUnitStatus::UNIT_AUTO_SKIP) 31 + ->attachRevision(null) 32 + ->save(); 33 + 34 + $params = array( 35 + 'objectPHID' => $diff->getPHID(), 36 + 'targetKeys' => array( 37 + HarbormasterArcLintBuildStepImplementation::STEPKEY, 38 + HarbormasterArcUnitBuildStepImplementation::STEPKEY, 39 + ), 40 + ); 41 + 42 + // Creation of autotargets should work from an empty state. 43 + $result = id(new ConduitCall('harbormaster.queryautotargets', $params)) 44 + ->setUser($viewer) 45 + ->execute(); 46 + 47 + $targets = idx($result, 'targetMap'); 48 + foreach ($params['targetKeys'] as $target_key) { 49 + $this->assertTrue((bool)$result['targetMap'][$target_key]); 50 + } 51 + 52 + // Querying the same autotargets again should produce the same results, 53 + // not make new ones. 54 + $retry = id(new ConduitCall('harbormaster.queryautotargets', $params)) 55 + ->setUser($viewer) 56 + ->execute(); 57 + 58 + $this->assertEqual($result, $retry); 59 + } 60 + 61 + }
+16
src/applications/harbormaster/autoplan/HarbormasterBuildArcanistAutoplan.php
··· 1 + <?php 2 + 3 + final class HarbormasterBuildArcanistAutoplan 4 + extends HarbormasterBuildAutoplan { 5 + 6 + const PLANKEY = 'arcanist'; 7 + 8 + public function getAutoplanPlanKey() { 9 + return self::PLANKEY; 10 + } 11 + 12 + public function getAutoplanName() { 13 + return pht('Arcanist Client Results'); 14 + } 15 + 16 + }
+44
src/applications/harbormaster/autoplan/HarbormasterBuildAutoplan.php
··· 1 + <?php 2 + 3 + abstract class HarbormasterBuildAutoplan extends Phobject { 4 + 5 + abstract public function getAutoplanPlanKey(); 6 + abstract public function getAutoplanName(); 7 + 8 + public static function getAutoplan($key) { 9 + return idx(self::getAllAutoplans(), $key); 10 + } 11 + 12 + public static function getAllAutoplans() { 13 + static $plans; 14 + 15 + if ($plans === null) { 16 + $objects = id(new PhutilSymbolLoader()) 17 + ->setAncestorClass(__CLASS__) 18 + ->loadObjects(); 19 + 20 + $map = array(); 21 + foreach ($objects as $object) { 22 + $key = $object->getAutoplanPlanKey(); 23 + if (!empty($map[$key])) { 24 + $other = $map[$key]; 25 + throw new Exception( 26 + pht( 27 + 'Two build autoplans (of classes "%s" and "%s") define the same '. 28 + 'key ("%s"). Each autoplan must have a unique key.', 29 + get_class($other), 30 + get_class($object), 31 + $key)); 32 + } 33 + $map[$key] = $object; 34 + } 35 + 36 + ksort($map); 37 + 38 + $plans = $map; 39 + } 40 + 41 + return $plans; 42 + } 43 + 44 + }
+76
src/applications/harbormaster/conduit/HarbormasterQueryAutotargetsConduitAPIMethod.php
··· 1 + <?php 2 + 3 + final class HarbormasterQueryAutotargetsConduitAPIMethod 4 + extends HarbormasterConduitAPIMethod { 5 + 6 + public function getAPIMethodName() { 7 + return 'harbormaster.queryautotargets'; 8 + } 9 + 10 + public function getMethodDescription() { 11 + return pht('Load or create build autotargets.'); 12 + } 13 + 14 + protected function defineParamTypes() { 15 + return array( 16 + 'objectPHID' => 'phid', 17 + 'targetKeys' => 'list<string>', 18 + ); 19 + } 20 + 21 + protected function defineReturnType() { 22 + return 'map<string, phid>'; 23 + } 24 + 25 + protected function execute(ConduitAPIRequest $request) { 26 + $viewer = $request->getUser(); 27 + 28 + $phid = $request->getValue('objectPHID'); 29 + 30 + // NOTE: We use withNames() to let monograms like "D123" work, which makes 31 + // this a little easier to test. Real PHIDs will still work as expected. 32 + 33 + $object = id(new PhabricatorObjectQuery()) 34 + ->setViewer($viewer) 35 + ->withNames(array($phid)) 36 + ->executeOne(); 37 + if (!$object) { 38 + throw new Exception( 39 + pht( 40 + 'No such object "%s" exists.', 41 + $phid)); 42 + } 43 + 44 + if (!($object instanceof HarbormasterBuildableInterface)) { 45 + throw new Exception( 46 + pht( 47 + 'Object "%s" does not implement interface "%s". Autotargets may '. 48 + 'only be queried for buildable objects.', 49 + $phid, 50 + 'HarbormasterBuildableInterface')); 51 + } 52 + 53 + $autotargets = $request->getValue('targetKeys', array()); 54 + 55 + if ($autotargets) { 56 + $targets = id(new HarbormasterTargetEngine()) 57 + ->setViewer($viewer) 58 + ->setObject($object) 59 + ->setAutoTargetKeys($autotargets) 60 + ->buildTargets(); 61 + } else { 62 + $targets = array(); 63 + } 64 + 65 + // Reorder the results according to the request order so we can make test 66 + // assertions that subsequent calls return the same results. 67 + 68 + $map = mpull($targets, 'getPHID'); 69 + $map = array_select_keys($map, $autotargets); 70 + 71 + return array( 72 + 'targetMap' => $map, 73 + ); 74 + } 75 + 76 + }
-17
src/applications/harbormaster/controller/HarbormasterController.php
··· 2 2 3 3 abstract class HarbormasterController extends PhabricatorController { 4 4 5 - protected function buildApplicationCrumbs() { 6 - $crumbs = parent::buildApplicationCrumbs(); 7 - 8 - $can_create = $this->hasApplicationCapability( 9 - HarbormasterManagePlansCapability::CAPABILITY); 10 - 11 - $crumbs->addAction( 12 - id(new PHUIListItemView()) 13 - ->setName(pht('New Build Plan')) 14 - ->setHref($this->getApplicationURI('plan/edit/')) 15 - ->setDisabled(!$can_create) 16 - ->setWorkflow(!$can_create) 17 - ->setIcon('fa-plus-square')); 18 - 19 - return $crumbs; 20 - } 21 - 22 5 }
+9 -14
src/applications/harbormaster/controller/HarbormasterPlanDisableController.php
··· 3 3 final class HarbormasterPlanDisableController 4 4 extends HarbormasterPlanController { 5 5 6 - private $id; 7 - 8 - public function willProcessRequest(array $data) { 9 - $this->id = $data['id']; 10 - } 11 - 12 - public function processRequest() { 13 - $request = $this->getRequest(); 14 - $viewer = $request->getUser(); 6 + public function handleRequest(AphrontRequest $request) { 7 + $viewer = $this->getViewer(); 15 8 16 9 $this->requireApplicationCapability( 17 10 HarbormasterManagePlansCapability::CAPABILITY); 18 11 19 12 $plan = id(new HarbormasterBuildPlanQuery()) 20 13 ->setViewer($viewer) 21 - ->withIDs(array($this->id)) 14 + ->withIDs(array($request->getURIData('id'))) 15 + ->requireCapabilities( 16 + array( 17 + PhabricatorPolicyCapability::CAN_VIEW, 18 + PhabricatorPolicyCapability::CAN_EDIT, 19 + )) 22 20 ->executeOne(); 23 21 if (!$plan) { 24 22 return new Aphront404Response(); ··· 63 61 $button = pht('Disable Plan'); 64 62 } 65 63 66 - $dialog = id(new AphrontDialogView()) 67 - ->setUser($viewer) 64 + return $this->newDialog() 68 65 ->setTitle($title) 69 66 ->appendChild($body) 70 67 ->addSubmitButton($button) 71 68 ->addCancelButton($plan_uri); 72 - 73 - return id(new AphrontDialogResponse())->setDialog($dialog); 74 69 } 75 70 76 71 }
+11 -11
src/applications/harbormaster/controller/HarbormasterPlanEditController.php
··· 2 2 3 3 final class HarbormasterPlanEditController extends HarbormasterPlanController { 4 4 5 - private $id; 6 - 7 - public function willProcessRequest(array $data) { 8 - $this->id = idx($data, 'id'); 9 - } 10 - 11 - public function processRequest() { 12 - $request = $this->getRequest(); 13 - $viewer = $request->getUser(); 5 + public function handleRequest(AphrontRequest $request) { 6 + $viewer = $this->getViewer(); 14 7 15 8 $this->requireApplicationCapability( 16 9 HarbormasterManagePlansCapability::CAPABILITY); 17 10 18 - if ($this->id) { 11 + $id = $request->getURIData('id'); 12 + if ($id) { 19 13 $plan = id(new HarbormasterBuildPlanQuery()) 20 14 ->setViewer($viewer) 21 - ->withIDs(array($this->id)) 15 + ->withIDs(array($id)) 16 + ->requireCapabilities( 17 + array( 18 + PhabricatorPolicyCapability::CAN_VIEW, 19 + PhabricatorPolicyCapability::CAN_EDIT, 20 + )) 22 21 ->executeOne(); 23 22 if (!$plan) { 24 23 return new Aphront404Response(); ··· 43 42 44 43 $editor = id(new HarbormasterBuildPlanEditor()) 45 44 ->setActor($viewer) 45 + ->setContinueOnNoEffect(true) 46 46 ->setContentSourceFromRequest($request); 47 47 48 48 try {
+20 -8
src/applications/harbormaster/controller/HarbormasterPlanListController.php
··· 2 2 3 3 final class HarbormasterPlanListController extends HarbormasterPlanController { 4 4 5 - private $queryKey; 6 - 7 5 public function shouldAllowPublic() { 8 6 return true; 9 7 } 10 8 11 - public function willProcessRequest(array $data) { 12 - $this->queryKey = idx($data, 'queryKey'); 13 - } 14 - 15 - public function processRequest() { 9 + public function handleRequest(AphrontRequest $request) { 16 10 $controller = id(new PhabricatorApplicationSearchController()) 17 - ->setQueryKey($this->queryKey) 11 + ->setQueryKey($request->getURIData('queryKey')) 18 12 ->setSearchEngine(new HarbormasterBuildPlanSearchEngine()) 19 13 ->setNavigation($this->buildSideNavView()); 20 14 ··· 43 37 public function buildApplicationMenu() { 44 38 return $this->buildSideNavView(true)->getMenu(); 45 39 } 40 + 41 + protected function buildApplicationCrumbs() { 42 + $crumbs = parent::buildApplicationCrumbs(); 43 + 44 + $can_create = $this->hasApplicationCapability( 45 + HarbormasterManagePlansCapability::CAPABILITY); 46 + 47 + $crumbs->addAction( 48 + id(new PHUIListItemView()) 49 + ->setName(pht('New Build Plan')) 50 + ->setHref($this->getApplicationURI('plan/edit/')) 51 + ->setDisabled(!$can_create) 52 + ->setWorkflow(!$can_create) 53 + ->setIcon('fa-plus-square')); 54 + 55 + return $crumbs; 56 + } 57 + 46 58 47 59 }
+9 -14
src/applications/harbormaster/controller/HarbormasterPlanRunController.php
··· 2 2 3 3 final class HarbormasterPlanRunController extends HarbormasterController { 4 4 5 - private $id; 6 - 7 - public function willProcessRequest(array $data) { 8 - $this->id = $data['id']; 9 - } 10 - 11 - public function processRequest() { 12 - $request = $this->getRequest(); 13 - $viewer = $request->getUser(); 5 + public function handleRequest(AphrontRequest $request) { 6 + $viewer = $this->getViewer(); 14 7 15 8 $this->requireApplicationCapability( 16 9 HarbormasterManagePlansCapability::CAPABILITY); 17 10 18 - $plan_id = $this->id; 11 + $plan_id = $request->getURIData('id'); 12 + 13 + // NOTE: At least for now, this only requires the "Can Manage Plans" 14 + // capability, not the "Can Edit" capability. Possibly it should have 15 + // a more stringent requirement, though. 16 + 19 17 $plan = id(new HarbormasterBuildPlanQuery()) 20 18 ->setViewer($viewer) 21 19 ->withIDs(array($plan_id)) ··· 87 85 ->setError($e_name) 88 86 ->setValue($v_name)); 89 87 90 - $dialog = id(new AphrontDialogView()) 88 + return $this->newDialog() 91 89 ->setWidth(AphrontDialogView::WIDTH_FULL) 92 - ->setUser($viewer) 93 90 ->setTitle($title) 94 91 ->appendChild($form) 95 92 ->addCancelButton($cancel_uri) 96 93 ->addSubmitButton($save_button); 97 - 98 - return id(new AphrontDialogResponse())->setDialog($dialog); 99 94 } 100 95 101 96 }
+45 -28
src/applications/harbormaster/controller/HarbormasterPlanViewController.php
··· 2 2 3 3 final class HarbormasterPlanViewController extends HarbormasterPlanController { 4 4 5 - private $id; 5 + public function handleRequest(AphrontRequest $request) { 6 + $viewer = $this->getviewer(); 6 7 7 - public function willProcessRequest(array $data) { 8 - $this->id = $data['id']; 9 - } 10 - 11 - public function processRequest() { 12 - $request = $this->getRequest(); 13 - $viewer = $request->getUser(); 14 - 15 - $id = $this->id; 8 + $id = $request->getURIData('id'); 16 9 17 10 $plan = id(new HarbormasterBuildPlanQuery()) 18 11 ->setViewer($viewer) ··· 79 72 } 80 73 81 74 private function buildStepList(HarbormasterBuildPlan $plan) { 82 - $request = $this->getRequest(); 83 - $viewer = $request->getUser(); 75 + $viewer = $this->getViewer(); 84 76 85 - $run_order = 86 - HarbormasterBuildGraph::determineDependencyExecution($plan); 77 + $run_order = HarbormasterBuildGraph::determineDependencyExecution($plan); 87 78 88 79 $steps = id(new HarbormasterBuildStepQuery()) 89 80 ->setViewer($viewer) ··· 91 82 ->execute(); 92 83 $steps = mpull($steps, null, 'getPHID'); 93 84 94 - $can_edit = $this->hasApplicationCapability( 85 + $has_manage = $this->hasApplicationCapability( 95 86 HarbormasterManagePlansCapability::CAPABILITY); 87 + 88 + $has_edit = PhabricatorPolicyFilter::hasCapability( 89 + $viewer, 90 + $plan, 91 + PhabricatorPolicyCapability::CAN_EDIT); 92 + 93 + $can_edit = ($has_manage && $has_edit); 96 94 97 95 $step_list = id(new PHUIObjectItemListView()) 98 96 ->setUser($viewer) ··· 222 220 $step_list->addItem($item); 223 221 } 224 222 225 - return array($step_list, $has_any_conflicts, $is_deadlocking); 223 + $step_list->setFlush(true); 224 + 225 + $plan_id = $plan->getID(); 226 + 227 + $header = id(new PHUIHeaderView()) 228 + ->setHeader(pht('Build Steps')) 229 + ->addActionLink( 230 + id(new PHUIButtonView()) 231 + ->setText(pht('Add Build Step')) 232 + ->setHref($this->getApplicationURI("step/add/{$plan_id}/")) 233 + ->setTag('a') 234 + ->setIcon( 235 + id(new PHUIIconView()) 236 + ->setIconFont('fa-plus')) 237 + ->setDisabled(!$can_edit) 238 + ->setWorkflow(true)); 239 + 240 + $step_box = id(new PHUIObjectBoxView()) 241 + ->setHeader($header) 242 + ->appendChild($step_list); 243 + 244 + return array($step_box, $has_any_conflicts, $is_deadlocking); 226 245 } 227 246 228 247 private function buildActionList(HarbormasterBuildPlan $plan) { 229 - $request = $this->getRequest(); 230 - $viewer = $request->getUser(); 248 + $viewer = $this->getViewer(); 231 249 $id = $plan->getID(); 232 250 233 251 $list = id(new PhabricatorActionListView()) ··· 235 253 ->setObject($plan) 236 254 ->setObjectURI($this->getApplicationURI("plan/{$id}/")); 237 255 238 - $can_edit = $this->hasApplicationCapability( 256 + $has_manage = $this->hasApplicationCapability( 239 257 HarbormasterManagePlansCapability::CAPABILITY); 258 + 259 + $has_edit = PhabricatorPolicyFilter::hasCapability( 260 + $viewer, 261 + $plan, 262 + PhabricatorPolicyCapability::CAN_EDIT); 263 + 264 + $can_edit = ($has_manage && $has_edit); 240 265 241 266 $list->addAction( 242 267 id(new PhabricatorActionView()) ··· 266 291 267 292 $list->addAction( 268 293 id(new PhabricatorActionView()) 269 - ->setName(pht('Add Build Step')) 270 - ->setHref($this->getApplicationURI("step/add/{$id}/")) 271 - ->setWorkflow(true) 272 - ->setDisabled(!$can_edit) 273 - ->setIcon('fa-plus')); 274 - 275 - $list->addAction( 276 - id(new PhabricatorActionView()) 277 294 ->setName(pht('Run Plan Manually')) 278 295 ->setHref($this->getApplicationURI("plan/run/{$id}/")) 279 296 ->setWorkflow(true) 280 - ->setDisabled(!$can_edit) 297 + ->setDisabled(!$has_manage) 281 298 ->setIcon('fa-play-circle')); 282 299 283 300 return $list;
+16 -12
src/applications/harbormaster/controller/HarbormasterStepAddController.php
··· 2 2 3 3 final class HarbormasterStepAddController extends HarbormasterController { 4 4 5 - private $id; 6 - 7 - public function willProcessRequest(array $data) { 8 - $this->id = $data['id']; 9 - } 10 - 11 - public function processRequest() { 12 - $request = $this->getRequest(); 13 - $viewer = $request->getUser(); 5 + public function handleRequest(AphrontRequest $request) { 6 + $viewer = $this->getViewer(); 14 7 15 8 $this->requireApplicationCapability( 16 9 HarbormasterManagePlansCapability::CAPABILITY); 17 10 18 11 $plan = id(new HarbormasterBuildPlanQuery()) 19 12 ->setViewer($viewer) 20 - ->withIDs(array($this->id)) 13 + ->withIDs(array($request->getURIData('id'))) 14 + ->requireCapabilities( 15 + array( 16 + PhabricatorPolicyCapability::CAN_VIEW, 17 + PhabricatorPolicyCapability::CAN_EDIT, 18 + )) 21 19 ->executeOne(); 22 20 if (!$plan) { 23 21 return new Aphront404Response(); ··· 26 24 $plan_id = $plan->getID(); 27 25 $cancel_uri = $this->getApplicationURI("plan/{$plan_id}/"); 28 26 27 + $all = HarbormasterBuildStepImplementation::getImplementations(); 28 + foreach ($all as $key => $impl) { 29 + if ($impl->shouldRequireAutotargeting()) { 30 + unset($all[$key]); 31 + } 32 + } 33 + 29 34 $errors = array(); 30 35 if ($request->isFormPost()) { 31 36 $class = $request->getStr('class'); 32 - if (!HarbormasterBuildStepImplementation::getImplementation($class)) { 37 + if (empty($all[$class])) { 33 38 $errors[] = pht('Choose the type of build step you want to add.'); 34 39 } 35 40 if (!$errors) { ··· 41 46 $control = id(new AphrontFormRadioButtonControl()) 42 47 ->setName('class'); 43 48 44 - $all = HarbormasterBuildStepImplementation::getImplementations(); 45 49 foreach ($all as $class => $implementation) { 46 50 $control->addButton( 47 51 $class,
+16 -23
src/applications/harbormaster/controller/HarbormasterStepDeleteController.php
··· 2 2 3 3 final class HarbormasterStepDeleteController extends HarbormasterController { 4 4 5 - private $id; 6 - 7 - public function willProcessRequest(array $data) { 8 - $this->id = $data['id']; 9 - } 10 - 11 - public function processRequest() { 12 - $request = $this->getRequest(); 13 - $viewer = $request->getUser(); 5 + public function handleRequest(AphrontRequest $request) { 6 + $viewer = $this->getViewer(); 14 7 15 8 $this->requireApplicationCapability( 16 9 HarbormasterManagePlansCapability::CAPABILITY); 17 10 18 - $id = $this->id; 11 + $id = $request->getURIData('id'); 19 12 20 13 $step = id(new HarbormasterBuildStepQuery()) 21 14 ->setViewer($viewer) 22 15 ->withIDs(array($id)) 16 + ->requireCapabilities( 17 + array( 18 + PhabricatorPolicyCapability::CAN_VIEW, 19 + PhabricatorPolicyCapability::CAN_EDIT, 20 + )) 23 21 ->executeOne(); 24 - if ($step === null) { 25 - throw new Exception(pht('Build step not found!')); 22 + if (!$step) { 23 + return new Aphront404Response(); 26 24 } 27 25 28 26 $plan_id = $step->getBuildPlan()->getID(); ··· 33 31 return id(new AphrontRedirectResponse())->setURI($done_uri); 34 32 } 35 33 36 - $dialog = new AphrontDialogView(); 37 - $dialog->setTitle(pht('Really Delete Step?')) 38 - ->setUser($viewer) 39 - ->addSubmitButton(pht('Delete Build Step')) 40 - ->addCancelButton($done_uri); 41 - $dialog->appendChild( 42 - phutil_tag( 43 - 'p', 44 - array(), 34 + return $this->newDialog() 35 + ->setTitle(pht('Really Delete Step?')) 36 + ->appendParagraph( 45 37 pht( 46 38 "Are you sure you want to delete this step? ". 47 - "This can't be undone!"))); 48 - return id(new AphrontDialogResponse())->setDialog($dialog); 39 + "This can't be undone!")) 40 + ->addCancelButton($done_uri) 41 + ->addSubmitButton(pht('Delete Build Step')); 49 42 } 50 43 51 44 }
+28 -21
src/applications/harbormaster/controller/HarbormasterStepEditController.php
··· 2 2 3 3 final class HarbormasterStepEditController extends HarbormasterController { 4 4 5 - private $id; 6 - private $planID; 7 - private $className; 8 - 9 - public function willProcessRequest(array $data) { 10 - $this->id = idx($data, 'id'); 11 - $this->planID = idx($data, 'plan'); 12 - $this->className = idx($data, 'class'); 13 - } 14 - 15 - public function processRequest() { 16 - $request = $this->getRequest(); 17 - $viewer = $request->getUser(); 5 + public function handleRequest(AphrontRequest $request) { 6 + $viewer = $this->getViewer(); 18 7 19 8 $this->requireApplicationCapability( 20 9 HarbormasterManagePlansCapability::CAPABILITY); 21 10 22 - if ($this->id) { 11 + $id = $request->getURIData('id'); 12 + if ($id) { 23 13 $step = id(new HarbormasterBuildStepQuery()) 24 14 ->setViewer($viewer) 25 - ->withIDs(array($this->id)) 15 + ->withIDs(array($id)) 16 + ->requireCapabilities( 17 + array( 18 + PhabricatorPolicyCapability::CAN_VIEW, 19 + PhabricatorPolicyCapability::CAN_EDIT, 20 + )) 26 21 ->executeOne(); 27 22 if (!$step) { 28 23 return new Aphront404Response(); ··· 31 26 32 27 $is_new = false; 33 28 } else { 29 + $plan_id = $request->getURIData('plan'); 30 + $class = $request->getURIData('class'); 31 + 34 32 $plan = id(new HarbormasterBuildPlanQuery()) 35 - ->setViewer($viewer) 36 - ->withIDs(array($this->planID)) 37 - ->executeOne(); 33 + ->setViewer($viewer) 34 + ->withIDs(array($plan_id)) 35 + ->requireCapabilities( 36 + array( 37 + PhabricatorPolicyCapability::CAN_VIEW, 38 + PhabricatorPolicyCapability::CAN_EDIT, 39 + )) 40 + ->executeOne(); 38 41 if (!$plan) { 39 42 return new Aphront404Response(); 40 43 } 41 44 42 - $impl = HarbormasterBuildStepImplementation::getImplementation( 43 - $this->className); 45 + $impl = HarbormasterBuildStepImplementation::getImplementation($class); 44 46 if (!$impl) { 45 47 return new Aphront404Response(); 46 48 } 47 49 50 + if ($impl->shouldRequireAutotargeting()) { 51 + // No manual creation of autotarget steps. 52 + return new Aphront404Response(); 53 + } 54 + 48 55 $step = HarbormasterBuildStep::initializeNewStep($viewer) 49 56 ->setBuildPlanPHID($plan->getPHID()) 50 - ->setClassName($this->className); 57 + ->setClassName($class); 51 58 52 59 $is_new = true; 53 60 }
+251
src/applications/harbormaster/engine/HarbormasterTargetEngine.php
··· 1 + <?php 2 + 3 + final class HarbormasterTargetEngine extends Phobject { 4 + 5 + private $viewer; 6 + private $object; 7 + private $autoTargetKeys; 8 + 9 + public function setViewer($viewer) { 10 + $this->viewer = $viewer; 11 + return $this; 12 + } 13 + 14 + public function getViewer() { 15 + return $this->viewer; 16 + } 17 + 18 + public function setObject(HarbormasterBuildableInterface $object) { 19 + $this->object = $object; 20 + return $this; 21 + } 22 + 23 + public function getObject() { 24 + return $this->object; 25 + } 26 + 27 + public function setAutoTargetKeys(array $auto_keys) { 28 + $this->autoTargetKeys = $auto_keys; 29 + return $this; 30 + } 31 + 32 + public function getAutoTargetKeys() { 33 + return $this->autoTargetKeys; 34 + } 35 + 36 + public function buildTargets() { 37 + $object = $this->getObject(); 38 + $viewer = $this->getViewer(); 39 + 40 + $step_map = $this->generateBuildStepMap($this->getAutoTargetKeys()); 41 + 42 + $buildable = HarbormasterBuildable::createOrLoadExisting( 43 + $viewer, 44 + $object->getHarbormasterBuildablePHID(), 45 + $object->getHarbormasterContainerPHID()); 46 + 47 + $target_map = $this->generateBuildTargetMap($buildable, $step_map); 48 + 49 + return $target_map; 50 + } 51 + 52 + 53 + /** 54 + * Get a map of the @{class:HarbormasterBuildStep} objects for a list of 55 + * autotarget keys. 56 + * 57 + * This method creates the steps if they do not yet exist. 58 + * 59 + * @param list<string> Autotarget keys, like `"core.arc.lint"`. 60 + * @return map<string, object> Map of keys to step objects. 61 + */ 62 + private function generateBuildStepMap(array $autotargets) { 63 + $viewer = $this->getViewer(); 64 + 65 + $autosteps = $this->getAutosteps($autotargets); 66 + $autosteps = mgroup($autosteps, 'getBuildStepAutotargetPlanKey'); 67 + 68 + $plans = id(new HarbormasterBuildPlanQuery()) 69 + ->setViewer($viewer) 70 + ->withPlanAutoKeys(array_keys($autosteps)) 71 + ->needBuildSteps(true) 72 + ->execute(); 73 + $plans = mpull($plans, null, 'getPlanAutoKey'); 74 + 75 + // NOTE: When creating the plan and steps, we save the autokeys as the 76 + // names. These won't actually be shown in the UI, but make the data more 77 + // consistent for secondary consumers like typeaheads. 78 + 79 + $step_map = array(); 80 + foreach ($autosteps as $plan_key => $steps) { 81 + $plan = idx($plans, $plan_key); 82 + if (!$plan) { 83 + $plan = HarbormasterBuildPlan::initializeNewBuildPlan($viewer) 84 + ->setName($plan_key) 85 + ->setPlanAutoKey($plan_key); 86 + } 87 + 88 + $current = $plan->getBuildSteps(); 89 + $current = mpull($current, null, 'getStepAutoKey'); 90 + $new_steps = array(); 91 + 92 + foreach ($steps as $step_key => $step) { 93 + if (isset($current[$step_key])) { 94 + $step_map[$step_key] = $current[$step_key]; 95 + continue; 96 + } 97 + 98 + $new_step = HarbormasterBuildStep::initializeNewStep($viewer) 99 + ->setName($step_key) 100 + ->setClassName(get_class($step)) 101 + ->setStepAutoKey($step_key); 102 + 103 + $new_steps[$step_key] = $new_step; 104 + } 105 + 106 + if ($new_steps) { 107 + $plan->openTransaction(); 108 + if (!$plan->getPHID()) { 109 + $plan->save(); 110 + } 111 + foreach ($new_steps as $step_key => $step) { 112 + $step->setBuildPlanPHID($plan->getPHID()); 113 + $step->save(); 114 + 115 + $step->attachBuildPlan($plan); 116 + $step_map[$step_key] = $step; 117 + } 118 + $plan->saveTransaction(); 119 + } 120 + } 121 + 122 + return $step_map; 123 + } 124 + 125 + 126 + /** 127 + * Get all of the @{class:HarbormasterBuildStepImplementation} objects for 128 + * a list of autotarget keys. 129 + * 130 + * @param list<string> Autotarget keys, like `"core.arc.lint"`. 131 + * @return map<string, object> Map of keys to implementations. 132 + */ 133 + private function getAutosteps(array $autotargets) { 134 + $all_steps = HarbormasterBuildStepImplementation::getImplementations(); 135 + $all_steps = mpull($all_steps, null, 'getBuildStepAutotargetStepKey'); 136 + 137 + // Make sure all the targets really exist. 138 + foreach ($autotargets as $autotarget) { 139 + if (empty($all_steps[$autotarget])) { 140 + throw new Exception( 141 + pht( 142 + 'No build step provides autotarget "%s"!', 143 + $autotarget)); 144 + } 145 + } 146 + 147 + return array_select_keys($all_steps, $autotargets); 148 + } 149 + 150 + 151 + /** 152 + * Get a list of @{class:HarbormasterBuildTarget} objects for a list of 153 + * autotarget keys. 154 + * 155 + * If some targets or builds do not exist, they are created. 156 + * 157 + * @param HarbormasterBuildable A buildable. 158 + * @param map<string, object> Map of keys to steps. 159 + * @return map<string, object> Map of keys to targets. 160 + */ 161 + private function generateBuildTargetMap( 162 + HarbormasterBuildable $buildable, 163 + array $step_map) { 164 + 165 + $viewer = $this->getViewer(); 166 + $plan_map = mgroup($step_map, 'getBuildPlanPHID'); 167 + 168 + $builds = id(new HarbormasterBuildQuery()) 169 + ->setViewer($viewer) 170 + ->withBuildablePHIDs(array($buildable->getPHID())) 171 + ->withBuildPlanPHIDs(array_keys($plan_map)) 172 + ->needBuildTargets(true) 173 + ->execute(); 174 + 175 + $autobuilds = array(); 176 + foreach ($builds as $build) { 177 + $plan_key = $build->getBuildPlan()->getPlanAutoKey(); 178 + $autobuilds[$plan_key] = $build; 179 + } 180 + 181 + $new_builds = array(); 182 + foreach ($plan_map as $plan_phid => $steps) { 183 + $plan = head($steps)->getBuildPlan(); 184 + $plan_key = $plan->getPlanAutoKey(); 185 + 186 + $build = idx($autobuilds, $plan_key); 187 + if ($build) { 188 + // We already have a build for this set of targets, so we don't need 189 + // to do any work. (It's possible the build is an older build that 190 + // doesn't have all of the right targets if new autotargets were 191 + // recently introduced, but we don't currently try to construct them.) 192 + continue; 193 + } 194 + 195 + // NOTE: Normally, `applyPlan()` does not actually generate targets. 196 + // We need to apply the plan in-process to perform target generation. 197 + // This is fine as long as autotargets are empty containers that don't 198 + // do any work, which they always should be. 199 + 200 + PhabricatorWorker::setRunAllTasksInProcess(true); 201 + try { 202 + 203 + // NOTE: We might race another process here to create the same build 204 + // with the same `planAutoKey`. The database will prevent this and 205 + // using autotargets only currently makes sense if you just created the 206 + // resource and "own" it, so we don't try to handle this, but may need 207 + // to be more careful here if use of autotargets expands. 208 + 209 + $build = $buildable->applyPlan($plan); 210 + PhabricatorWorker::setRunAllTasksInProcess(false); 211 + } catch (Exception $ex) { 212 + PhabricatorWorker::setRunAllTasksInProcess(false); 213 + throw $ex; 214 + } 215 + 216 + $new_builds[] = $build; 217 + } 218 + 219 + if ($new_builds) { 220 + $all_targets = id(new HarbormasterBuildTargetQuery()) 221 + ->setViewer($viewer) 222 + ->withBuildPHIDs(mpull($new_builds, 'getPHID')) 223 + ->execute(); 224 + } else { 225 + $all_targets = array(); 226 + } 227 + 228 + foreach ($builds as $build) { 229 + foreach ($build->getBuildTargets() as $target) { 230 + $all_targets[] = $target; 231 + } 232 + } 233 + 234 + $target_map = array(); 235 + foreach ($all_targets as $target) { 236 + $target_key = $target 237 + ->getImplementation() 238 + ->getBuildStepAutotargetStepKey(); 239 + if (!$target_key) { 240 + continue; 241 + } 242 + $target_map[$target_key] = $target; 243 + } 244 + 245 + $target_map = array_select_keys($target_map, array_keys($step_map)); 246 + 247 + return $target_map; 248 + } 249 + 250 + 251 + }
-33
src/applications/harbormaster/phid/HarbormasterBuildItemPHIDType.php
··· 1 - <?php 2 - 3 - final class HarbormasterBuildItemPHIDType extends PhabricatorPHIDType { 4 - 5 - const TYPECONST = 'HMBI'; 6 - 7 - public function getTypeName() { 8 - return pht('Build Item'); 9 - } 10 - 11 - public function newObject() { 12 - return new HarbormasterBuildItem(); 13 - } 14 - 15 - protected function buildQueryForObjects( 16 - PhabricatorObjectQuery $query, 17 - array $phids) { 18 - 19 - return id(new HarbormasterBuildItemQuery()) 20 - ->withPHIDs($phids); 21 - } 22 - 23 - public function loadHandles( 24 - PhabricatorHandleQuery $query, 25 - array $handles, 26 - array $objects) { 27 - 28 - foreach ($handles as $phid => $handle) { 29 - $build_item = $objects[$phid]; 30 - } 31 - } 32 - 33 - }
-60
src/applications/harbormaster/query/HarbormasterBuildItemQuery.php
··· 1 - <?php 2 - 3 - final class HarbormasterBuildItemQuery 4 - extends PhabricatorCursorPagedPolicyAwareQuery { 5 - 6 - private $ids; 7 - private $phids; 8 - 9 - public function withIDs(array $ids) { 10 - $this->ids = $ids; 11 - return $this; 12 - } 13 - 14 - public function withPHIDs(array $phids) { 15 - $this->phids = $phids; 16 - return $this; 17 - } 18 - 19 - protected function loadPage() { 20 - $table = new HarbormasterBuildItem(); 21 - $conn_r = $table->establishConnection('r'); 22 - 23 - $data = queryfx_all( 24 - $conn_r, 25 - 'SELECT * FROM %T %Q %Q %Q', 26 - $table->getTableName(), 27 - $this->buildWhereClause($conn_r), 28 - $this->buildOrderClause($conn_r), 29 - $this->buildLimitClause($conn_r)); 30 - 31 - return $table->loadAllFromArray($data); 32 - } 33 - 34 - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { 35 - $where = array(); 36 - 37 - if ($this->ids) { 38 - $where[] = qsprintf( 39 - $conn_r, 40 - 'id IN (%Ld)', 41 - $this->ids); 42 - } 43 - 44 - if ($this->phids) { 45 - $where[] = qsprintf( 46 - $conn_r, 47 - 'phid in (%Ls)', 48 - $this->phids); 49 - } 50 - 51 - $where[] = $this->buildPagingClause($conn_r); 52 - 53 - return $this->formatWhereClause($where); 54 - } 55 - 56 - public function getQueryApplicationClass() { 57 - return 'PhabricatorHarbormasterApplication'; 58 - } 59 - 60 - }
+39
src/applications/harbormaster/query/HarbormasterBuildPlanQuery.php
··· 7 7 private $phids; 8 8 private $statuses; 9 9 private $datasourceQuery; 10 + private $planAutoKeys; 11 + private $needBuildSteps; 10 12 11 13 public function withIDs(array $ids) { 12 14 $this->ids = $ids; ··· 28 30 return $this; 29 31 } 30 32 33 + public function withPlanAutoKeys(array $keys) { 34 + $this->planAutoKeys = $keys; 35 + return $this; 36 + } 37 + 38 + public function needBuildSteps($need) { 39 + $this->needBuildSteps = $need; 40 + return $this; 41 + } 42 + 31 43 public function newResultObject() { 32 44 return new HarbormasterBuildPlan(); 33 45 } ··· 36 48 return $this->loadStandardPage($this->newResultObject()); 37 49 } 38 50 51 + protected function didFilterPage(array $page) { 52 + if ($this->needBuildSteps) { 53 + $plan_phids = mpull($page, 'getPHID'); 54 + 55 + $steps = id(new HarbormasterBuildStepQuery()) 56 + ->setParentQuery($this) 57 + ->setViewer($this->getViewer()) 58 + ->withBuildPlanPHIDs($plan_phids) 59 + ->execute(); 60 + $steps = mgroup($steps, 'getBuildPlanPHID'); 61 + 62 + foreach ($page as $plan) { 63 + $plan_steps = idx($steps, $plan->getPHID(), array()); 64 + $plan->attachBuildSteps($plan_steps); 65 + } 66 + } 67 + 68 + return $page; 69 + } 70 + 39 71 protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 40 72 $where = parent::buildWhereClauseParts($conn); 41 73 ··· 65 97 $conn, 66 98 'name LIKE %>', 67 99 $this->datasourceQuery); 100 + } 101 + 102 + if ($this->planAutoKeys !== null) { 103 + $where[] = qsprintf( 104 + $conn, 105 + 'planAutoKey IN (%Ls)', 106 + $this->planAutoKeys); 68 107 } 69 108 70 109 return $where;
+4
src/applications/harbormaster/query/HarbormasterBuildPlanSearchEngine.php
··· 88 88 $item->setDisabled(true); 89 89 } 90 90 91 + if ($plan->isAutoplan()) { 92 + $item->addIcon('fa-lock grey', pht('Autoplan')); 93 + } 94 + 91 95 $item->setHref($this->getApplicationURI("plan/{$id}/")); 92 96 93 97 $list->addItem($item);
+38
src/applications/harbormaster/step/HarbormasterArcLintBuildStepImplementation.php
··· 1 + <?php 2 + 3 + final class HarbormasterArcLintBuildStepImplementation 4 + extends HarbormasterBuildStepImplementation { 5 + 6 + const STEPKEY = 'arcanist.lint'; 7 + 8 + public function getBuildStepAutotargetPlanKey() { 9 + return HarbormasterBuildArcanistAutoplan::PLANKEY; 10 + } 11 + 12 + public function getBuildStepAutotargetStepKey() { 13 + return self::STEPKEY; 14 + } 15 + 16 + public function shouldRequireAutotargeting() { 17 + return true; 18 + } 19 + 20 + public function getName() { 21 + return pht('Arcanist Lint Results'); 22 + } 23 + 24 + public function getGenericDescription() { 25 + return pht('Automatic `arc lint` step.'); 26 + } 27 + 28 + public function execute( 29 + HarbormasterBuild $build, 30 + HarbormasterBuildTarget $build_target) { 31 + return; 32 + } 33 + 34 + public function shouldWaitForMessage(HarbormasterBuildTarget $target) { 35 + return true; 36 + } 37 + 38 + }
+38
src/applications/harbormaster/step/HarbormasterArcUnitBuildStepImplementation.php
··· 1 + <?php 2 + 3 + final class HarbormasterArcUnitBuildStepImplementation 4 + extends HarbormasterBuildStepImplementation { 5 + 6 + const STEPKEY = 'arcanist.unit'; 7 + 8 + public function getBuildStepAutotargetPlanKey() { 9 + return HarbormasterBuildArcanistAutoplan::PLANKEY; 10 + } 11 + 12 + public function getBuildStepAutotargetStepKey() { 13 + return self::STEPKEY; 14 + } 15 + 16 + public function shouldRequireAutotargeting() { 17 + return true; 18 + } 19 + 20 + public function getName() { 21 + return pht('Arcanist Unit Results'); 22 + } 23 + 24 + public function getGenericDescription() { 25 + return pht('Automatic `arc unit` step.'); 26 + } 27 + 28 + public function execute( 29 + HarbormasterBuild $build, 30 + HarbormasterBuildTarget $build_target) { 31 + return; 32 + } 33 + 34 + public function shouldWaitForMessage(HarbormasterBuildTarget $target) { 35 + return true; 36 + } 37 + 38 + }
+19
src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php
··· 1 1 <?php 2 2 3 + /** 4 + * @task autotarget Automatic Targets 5 + */ 3 6 abstract class HarbormasterBuildStepImplementation extends Phobject { 4 7 5 8 private $settings; ··· 247 250 return $future->resolve(); 248 251 } 249 252 } 253 + } 254 + 255 + 256 + /* -( Automatic Targets )-------------------------------------------------- */ 257 + 258 + 259 + public function getBuildStepAutotargetStepKey() { 260 + return null; 261 + } 262 + 263 + public function getBuildStepAutotargetPlanKey() { 264 + throw new PhutilMethodNotImplementedException(); 265 + } 266 + 267 + public function shouldRequireAutotargeting() { 268 + return false; 250 269 } 251 270 252 271 }
+8 -2
src/applications/harbormaster/storage/HarbormasterBuildable.php
··· 141 141 $build = HarbormasterBuild::initializeNewBuild($viewer) 142 142 ->setBuildablePHID($this->getPHID()) 143 143 ->setBuildPlanPHID($plan->getPHID()) 144 - ->setBuildStatus(HarbormasterBuild::STATUS_PENDING) 145 - ->save(); 144 + ->setBuildStatus(HarbormasterBuild::STATUS_PENDING); 145 + 146 + $auto_key = $plan->getPlanAutoKey(); 147 + if ($auto_key) { 148 + $build->setPlanAutoKey($auto_key); 149 + } 150 + 151 + $build->save(); 146 152 147 153 PhabricatorWorker::scheduleTask( 148 154 'HarbormasterBuildWorker',
+6
src/applications/harbormaster/storage/build/HarbormasterBuild.php
··· 9 9 protected $buildPlanPHID; 10 10 protected $buildStatus; 11 11 protected $buildGeneration; 12 + protected $planAutoKey; 12 13 13 14 private $buildable = self::ATTACHABLE; 14 15 private $buildPlan = self::ATTACHABLE; ··· 148 149 self::CONFIG_COLUMN_SCHEMA => array( 149 150 'buildStatus' => 'text32', 150 151 'buildGeneration' => 'uint32', 152 + 'planAutoKey' => 'text32?', 151 153 ), 152 154 self::CONFIG_KEY_SCHEMA => array( 153 155 'key_buildable' => array( ··· 158 160 ), 159 161 'key_status' => array( 160 162 'columns' => array('buildStatus'), 163 + ), 164 + 'key_planautokey' => array( 165 + 'columns' => array('buildablePHID', 'planAutoKey'), 166 + 'unique' => true, 161 167 ), 162 168 ), 163 169 ) + parent::getConfiguration();
-19
src/applications/harbormaster/storage/build/HarbormasterBuildItem.php
··· 1 - <?php 2 - 3 - final class HarbormasterBuildItem extends HarbormasterDAO { 4 - 5 - protected $name; 6 - 7 - protected function getConfiguration() { 8 - return array( 9 - self::CONFIG_AUX_PHID => true, 10 - self::CONFIG_NO_TABLE => true, 11 - ) + parent::getConfiguration(); 12 - } 13 - 14 - public function generatePHID() { 15 - return PhabricatorPHID::generateNewPHID( 16 - HarbormasterBuildItemPHIDType::TYPECONST); 17 - } 18 - 19 - }
+98
src/applications/harbormaster/storage/build/HarbormasterBuildLintMessage.php
··· 1 + <?php 2 + 3 + final class HarbormasterBuildLintMessage 4 + extends HarbormasterDAO { 5 + 6 + protected $buildTargetPHID; 7 + protected $path; 8 + protected $line; 9 + protected $characterOffset; 10 + protected $code; 11 + protected $severity; 12 + protected $name; 13 + protected $properties = array(); 14 + 15 + private $buildTarget = self::ATTACHABLE; 16 + 17 + public static function initializeNewLintMessage( 18 + HarbormasterBuildTarget $build_target) { 19 + return id(new HarbormasterBuildLintMessage()) 20 + ->setBuildTargetPHID($build_target->getPHID()); 21 + } 22 + 23 + public static function newFromDictionary( 24 + HarbormasterBuildTarget $build_target, 25 + array $dict) { 26 + 27 + $obj = self::initializeNewLintMessage($build_target); 28 + 29 + $spec = array( 30 + 'path' => 'string', 31 + 'line' => 'optional int', 32 + 'char' => 'optional int', 33 + 'code' => 'string', 34 + 'severity' => 'string', 35 + 'name' => 'string', 36 + 'description' => 'optional string', 37 + ); 38 + 39 + // We're just going to ignore extra keys for now, to make it easier to 40 + // add stuff here later on. 41 + $dict = array_select_keys($dict, array_keys($spec)); 42 + PhutilTypeSpec::checkMap($dict, $spec); 43 + 44 + $obj->setPath($dict['path']); 45 + $obj->setLine(idx($dict, 'line')); 46 + $obj->setCharacterOffset(idx($dict, 'char')); 47 + $obj->setCode($dict['code']); 48 + $obj->setSeverity($dict['severity']); 49 + $obj->setName($dict['name']); 50 + 51 + $description = idx($dict, 'description'); 52 + if (strlen($description)) { 53 + $obj->setProperty('description', $description); 54 + } 55 + 56 + return $obj; 57 + } 58 + 59 + protected function getConfiguration() { 60 + return array( 61 + self::CONFIG_SERIALIZATION => array( 62 + 'properties' => self::SERIALIZATION_JSON, 63 + ), 64 + self::CONFIG_COLUMN_SCHEMA => array( 65 + 'path' => 'text', 66 + 'line' => 'uint32?', 67 + 'characterOffset' => 'uint32?', 68 + 'code' => 'text32', 69 + 'severity' => 'text32', 70 + 'name' => 'text255', 71 + ), 72 + self::CONFIG_KEY_SCHEMA => array( 73 + 'key_target' => array( 74 + 'columns' => array('buildTargetPHID'), 75 + ), 76 + ), 77 + ) + parent::getConfiguration(); 78 + } 79 + 80 + public function attachBuildTarget(HarbormasterBuildTarget $build_target) { 81 + $this->buildTarget = $build_target; 82 + return $this; 83 + } 84 + 85 + public function getBuildTarget() { 86 + return $this->assertAttached($this->buildTarget); 87 + } 88 + 89 + public function getProperty($key, $default = null) { 90 + return idx($this->properties, $key, $default); 91 + } 92 + 93 + public function setProperty($key, $value) { 94 + $this->properties[$key] = $value; 95 + return $this; 96 + } 97 + 98 + }
+9 -1
src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php
··· 175 175 return $this->implementation; 176 176 } 177 177 178 + public function isAutotarget() { 179 + try { 180 + return (bool)$this->getImplementation()->getBuildStepAutotargetPlanKey(); 181 + } catch (Exception $e) { 182 + return false; 183 + } 184 + } 185 + 178 186 public function getName() { 179 - if (strlen($this->name)) { 187 + if (strlen($this->name) && !$this->isAutotarget()) { 180 188 return $this->name; 181 189 } 182 190
+100
src/applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php
··· 1 + <?php 2 + 3 + final class HarbormasterBuildUnitMessage 4 + extends HarbormasterDAO { 5 + 6 + protected $buildTargetPHID; 7 + protected $engine; 8 + protected $namespace; 9 + protected $name; 10 + protected $result; 11 + protected $duration; 12 + protected $properties = array(); 13 + 14 + private $buildTarget = self::ATTACHABLE; 15 + 16 + public static function initializeNewUnitMessage( 17 + HarbormasterBuildTarget $build_target) { 18 + return id(new HarbormasterBuildLintMessage()) 19 + ->setBuildTargetPHID($build_target->getPHID()); 20 + } 21 + 22 + public static function newFromDictionary( 23 + HarbormasterBuildTarget $build_target, 24 + array $dict) { 25 + 26 + $obj = self::initializeNewUnitMessage($build_target); 27 + 28 + $spec = array( 29 + 'engine' => 'optional string', 30 + 'namespace' => 'optional string', 31 + 'name' => 'string', 32 + 'result' => 'string', 33 + 'duration' => 'optional float', 34 + 'path' => 'optional string', 35 + 'coverage' => 'optional string', 36 + ); 37 + 38 + // We're just going to ignore extra keys for now, to make it easier to 39 + // add stuff here later on. 40 + $dict = array_select_keys($dict, array_keys($spec)); 41 + PhutilTypeSpec::checkMap($dict, $spec); 42 + 43 + $obj->setEngine(idx($dict, 'engine', '')); 44 + $obj->setNamespace(idx($dict, 'namespace', '')); 45 + $obj->setName($dict['name']); 46 + $obj->setResult($dict['result']); 47 + $obj->setDuration(idx($dict, 'duration')); 48 + 49 + $path = idx($dict, 'path'); 50 + if (strlen($path)) { 51 + $obj->setProperty('path', $path); 52 + } 53 + 54 + $coverage = idx($dict, 'coverage'); 55 + if (strlen($coverage)) { 56 + $obj->setProperty('coverage', $coverage); 57 + } 58 + 59 + return $obj; 60 + } 61 + 62 + protected function getConfiguration() { 63 + return array( 64 + self::CONFIG_SERIALIZATION => array( 65 + 'properties' => self::SERIALIZATION_JSON, 66 + ), 67 + self::CONFIG_COLUMN_SCHEMA => array( 68 + 'engine' => 'text255', 69 + 'namespace' => 'text255', 70 + 'name' => 'text255', 71 + 'result' => 'text32', 72 + 'duration' => 'double?', 73 + ), 74 + self::CONFIG_KEY_SCHEMA => array( 75 + 'key_target' => array( 76 + 'columns' => array('buildTargetPHID'), 77 + ), 78 + ), 79 + ) + parent::getConfiguration(); 80 + } 81 + 82 + public function attachBuildTarget(HarbormasterBuildTarget $build_target) { 83 + $this->buildTarget = $build_target; 84 + return $this; 85 + } 86 + 87 + public function getBuildTarget() { 88 + return $this->assertAttached($this->buildTarget); 89 + } 90 + 91 + public function getProperty($key, $default = null) { 92 + return idx($this->properties, $key, $default); 93 + } 94 + 95 + public function setProperty($key, $value) { 96 + $this->properties[$key] = $value; 97 + return $this; 98 + } 99 + 100 + }
+63 -2
src/applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php
··· 1 1 <?php 2 2 3 + /** 4 + * @task autoplan Autoplans 5 + */ 3 6 final class HarbormasterBuildPlan extends HarbormasterDAO 4 7 implements 5 8 PhabricatorApplicationTransactionInterface, ··· 8 11 9 12 protected $name; 10 13 protected $planStatus; 14 + protected $planAutoKey; 11 15 12 16 const STATUS_ACTIVE = 'active'; 13 17 const STATUS_DISABLED = 'disabled'; ··· 16 20 17 21 public static function initializeNewBuildPlan(PhabricatorUser $actor) { 18 22 return id(new HarbormasterBuildPlan()) 19 - ->setPlanStatus(self::STATUS_ACTIVE); 23 + ->setName('') 24 + ->setPlanStatus(self::STATUS_ACTIVE) 25 + ->attachBuildSteps(array()); 20 26 } 21 27 22 28 protected function getConfiguration() { ··· 25 31 self::CONFIG_COLUMN_SCHEMA => array( 26 32 'name' => 'sort128', 27 33 'planStatus' => 'text32', 34 + 'planAutoKey' => 'text32?', 28 35 ), 29 36 self::CONFIG_KEY_SCHEMA => array( 30 37 'key_status' => array( ··· 33 40 'key_name' => array( 34 41 'columns' => array('name'), 35 42 ), 43 + 'key_planautokey' => array( 44 + 'columns' => array('planAutoKey'), 45 + 'unique' => true, 46 + ), 36 47 ), 37 48 ) + parent::getConfiguration(); 38 49 } ··· 57 68 } 58 69 59 70 71 + /* -( Autoplans )---------------------------------------------------------- */ 72 + 73 + 74 + public function isAutoplan() { 75 + return ($this->getPlanAutoKey() !== null); 76 + } 77 + 78 + 79 + public function getAutoplan() { 80 + if (!$this->isAutoplan()) { 81 + return null; 82 + } 83 + 84 + return HarbormasterBuildAutoplan::getAutoplan($this->getPlanAutoKey()); 85 + } 86 + 87 + 88 + public function getName() { 89 + $autoplan = $this->getAutoplan(); 90 + if ($autoplan) { 91 + return $autoplan->getAutoplanName(); 92 + } 93 + 94 + return parent::getName(); 95 + } 96 + 97 + 60 98 /* -( PhabricatorSubscribableInterface )----------------------------------- */ 61 99 62 100 ··· 102 140 public function getCapabilities() { 103 141 return array( 104 142 PhabricatorPolicyCapability::CAN_VIEW, 143 + PhabricatorPolicyCapability::CAN_EDIT, 105 144 ); 106 145 } 107 146 ··· 109 148 switch ($capability) { 110 149 case PhabricatorPolicyCapability::CAN_VIEW: 111 150 return PhabricatorPolicies::getMostOpenPolicy(); 151 + case PhabricatorPolicyCapability::CAN_EDIT: 152 + // NOTE: In practice, this policy is always limited by the "Mangage 153 + // Build Plans" policy. 154 + 155 + if ($this->isAutoplan()) { 156 + return PhabricatorPolicies::POLICY_NOONE; 157 + } 158 + 159 + return PhabricatorPolicies::getMostOpenPolicy(); 112 160 } 113 161 } 114 162 ··· 117 165 } 118 166 119 167 public function describeAutomaticCapability($capability) { 120 - return null; 168 + $messages = array(); 169 + 170 + switch ($capability) { 171 + case PhabricatorPolicyCapability::CAN_EDIT: 172 + if ($this->isAutoplan()) { 173 + $messages[] = pht( 174 + 'This is an autoplan (a builtin plan provided by an application) '. 175 + 'so it can not be edited.'); 176 + } 177 + break; 178 + } 179 + 180 + return $messages; 121 181 } 182 + 122 183 }
+14 -1
src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php
··· 12 12 protected $className; 13 13 protected $details = array(); 14 14 protected $sequence = 0; 15 + protected $stepAutoKey; 15 16 16 17 private $buildPlan = self::ATTACHABLE; 17 18 private $customFields = self::ATTACHABLE; 18 19 private $implementation; 19 20 20 21 public static function initializeNewStep(PhabricatorUser $actor) { 21 - return id(new HarbormasterBuildStep()); 22 + return id(new HarbormasterBuildStep()) 23 + ->setName('') 24 + ->setDescription(''); 22 25 } 23 26 24 27 protected function getConfiguration() { ··· 37 40 // which predated editable names. These should be backfilled with 38 41 // default names, then the code for handling `null` shoudl be removed. 39 42 'name' => 'text255?', 43 + 'stepAutoKey' => 'text32?', 40 44 ), 41 45 self::CONFIG_KEY_SCHEMA => array( 42 46 'key_plan' => array( 43 47 'columns' => array('buildPlanPHID'), 44 48 ), 49 + 'key_stepautokey' => array( 50 + 'columns' => array('buildPlanPHID', 'stepAutoKey'), 51 + 'unique' => true, 52 + ), 45 53 ), 46 54 ) + parent::getConfiguration(); 47 55 } ··· 88 96 return $this->implementation; 89 97 } 90 98 99 + public function isAutostep() { 100 + return ($this->getStepAutoKey() !== null); 101 + } 102 + 91 103 92 104 /* -( PhabricatorApplicationTransactionInterface )------------------------- */ 93 105 ··· 118 130 public function getCapabilities() { 119 131 return array( 120 132 PhabricatorPolicyCapability::CAN_VIEW, 133 + PhabricatorPolicyCapability::CAN_EDIT, 121 134 ); 122 135 } 123 136
+2
src/applications/legalpad/application/PhabricatorLegalpadApplication.php
··· 77 77 LegalpadCreateDocumentsCapability::CAPABILITY => array(), 78 78 LegalpadDefaultViewCapability::CAPABILITY => array( 79 79 'template' => PhabricatorLegalpadDocumentPHIDType::TYPECONST, 80 + 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 80 81 ), 81 82 LegalpadDefaultEditCapability::CAPABILITY => array( 82 83 'template' => PhabricatorLegalpadDocumentPHIDType::TYPECONST, 84 + 'capability' => PhabricatorPolicyCapability::CAN_EDIT, 83 85 ), 84 86 ); 85 87 }
+2
src/applications/maniphest/application/PhabricatorManiphestApplication.php
··· 132 132 ManiphestDefaultViewCapability::CAPABILITY => array( 133 133 'caption' => pht('Default view policy for newly created tasks.'), 134 134 'template' => ManiphestTaskPHIDType::TYPECONST, 135 + 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 135 136 ), 136 137 ManiphestDefaultEditCapability::CAPABILITY => array( 137 138 'caption' => pht('Default edit policy for newly created tasks.'), 138 139 'template' => ManiphestTaskPHIDType::TYPECONST, 140 + 'capability' => PhabricatorPolicyCapability::CAN_EDIT, 139 141 ), 140 142 ManiphestEditStatusCapability::CAPABILITY => array(), 141 143 ManiphestEditAssignCapability::CAPABILITY => array(),
+12
src/applications/maniphest/controller/ManiphestBatchEditController.php
··· 25 25 $task_ids = $request->getStrList('batch'); 26 26 } 27 27 28 + if (!$task_ids) { 29 + throw new Exception( 30 + pht( 31 + 'No tasks are selected.')); 32 + } 33 + 28 34 $tasks = id(new ManiphestTaskQuery()) 29 35 ->setViewer($viewer) 30 36 ->withIDs($task_ids) ··· 36 42 ->needSubscriberPHIDs(true) 37 43 ->needProjectPHIDs(true) 38 44 ->execute(); 45 + 46 + if (!$tasks) { 47 + throw new Exception( 48 + pht( 49 + "You don't have permission to edit any of the selected tasks.")); 50 + } 39 51 40 52 if ($project) { 41 53 $cancel_uri = '/project/board/'.$project->getID().'/';
+95 -111
src/applications/maniphest/query/ManiphestTaskQuery.php
··· 6 6 */ 7 7 final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery { 8 8 9 - private $taskIDs = array(); 10 - private $taskPHIDs = array(); 11 - private $authorPHIDs = array(); 12 - private $ownerPHIDs = array(); 9 + private $taskIDs; 10 + private $taskPHIDs; 11 + private $authorPHIDs; 12 + private $ownerPHIDs; 13 13 private $noOwner; 14 14 private $anyOwner; 15 - private $subscriberPHIDs = array(); 15 + private $subscriberPHIDs; 16 16 private $dateCreatedAfter; 17 17 private $dateCreatedBefore; 18 18 private $dateModifiedAfter; ··· 216 216 $task_dao = new ManiphestTask(); 217 217 $conn = $task_dao->establishConnection('r'); 218 218 219 - $where = array(); 220 - $where[] = $this->buildTaskIDsWhereClause($conn); 221 - $where[] = $this->buildTaskPHIDsWhereClause($conn); 222 - $where[] = $this->buildStatusWhereClause($conn); 223 - $where[] = $this->buildStatusesWhereClause($conn); 224 - $where[] = $this->buildDependenciesWhereClause($conn); 225 - $where[] = $this->buildAuthorWhereClause($conn); 226 - $where[] = $this->buildOwnerWhereClause($conn); 227 - $where[] = $this->buildFullTextWhereClause($conn); 228 - 229 - if ($this->dateCreatedAfter) { 230 - $where[] = qsprintf( 231 - $conn, 232 - 'task.dateCreated >= %d', 233 - $this->dateCreatedAfter); 234 - } 235 - 236 - if ($this->dateCreatedBefore) { 237 - $where[] = qsprintf( 238 - $conn, 239 - 'task.dateCreated <= %d', 240 - $this->dateCreatedBefore); 241 - } 242 - 243 - if ($this->dateModifiedAfter) { 244 - $where[] = qsprintf( 245 - $conn, 246 - 'task.dateModified >= %d', 247 - $this->dateModifiedAfter); 248 - } 249 - 250 - if ($this->dateModifiedBefore) { 251 - $where[] = qsprintf( 252 - $conn, 253 - 'task.dateModified <= %d', 254 - $this->dateModifiedBefore); 255 - } 256 - 257 - if ($this->priorities) { 258 - $where[] = qsprintf( 259 - $conn, 260 - 'task.priority IN (%Ld)', 261 - $this->priorities); 262 - } 263 - 264 - if ($this->subpriorities) { 265 - $where[] = qsprintf( 266 - $conn, 267 - 'task.subpriority IN (%Lf)', 268 - $this->subpriorities); 269 - } 270 - 271 - if ($this->subpriorityMin) { 272 - $where[] = qsprintf( 273 - $conn, 274 - 'task.subpriority >= %f', 275 - $this->subpriorityMin); 276 - } 277 - 278 - if ($this->subpriorityMax) { 279 - $where[] = qsprintf( 280 - $conn, 281 - 'task.subpriority <= %f', 282 - $this->subpriorityMax); 283 - } 284 - 285 - $where[] = $this->buildWhereClauseParts($conn); 286 - 287 - $where = $this->formatWhereClause($where); 219 + $where = $this->buildWhereClause($conn); 288 220 289 221 $group_column = ''; 290 222 switch ($this->groupBy) { ··· 392 324 return $tasks; 393 325 } 394 326 395 - private function buildTaskIDsWhereClause(AphrontDatabaseConnection $conn) { 396 - if (!$this->taskIDs) { 397 - return null; 327 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 328 + $where = parent::buildWhereClauseParts($conn); 329 + 330 + $where[] = $this->buildStatusWhereClause($conn); 331 + $where[] = $this->buildDependenciesWhereClause($conn); 332 + $where[] = $this->buildOwnerWhereClause($conn); 333 + $where[] = $this->buildFullTextWhereClause($conn); 334 + 335 + if ($this->taskIDs !== null) { 336 + $where[] = qsprintf( 337 + $conn, 338 + 'task.id in (%Ld)', 339 + $this->taskIDs); 398 340 } 399 341 400 - return qsprintf( 401 - $conn, 402 - 'task.id in (%Ld)', 403 - $this->taskIDs); 404 - } 342 + if ($this->taskPHIDs !== null) { 343 + $where[] = qsprintf( 344 + $conn, 345 + 'task.phid in (%Ls)', 346 + $this->taskPHIDs); 347 + } 405 348 406 - private function buildTaskPHIDsWhereClause(AphrontDatabaseConnection $conn) { 407 - if (!$this->taskPHIDs) { 408 - return null; 349 + if ($this->statuses !== null) { 350 + $where[] = qsprintf( 351 + $conn, 352 + 'task.status IN (%Ls)', 353 + $this->statuses); 354 + } 355 + 356 + if ($this->authorPHIDs !== null) { 357 + $where[] = qsprintf( 358 + $conn, 359 + 'task.authorPHID in (%Ls)', 360 + $this->authorPHIDs); 361 + } 362 + 363 + if ($this->dateCreatedAfter) { 364 + $where[] = qsprintf( 365 + $conn, 366 + 'task.dateCreated >= %d', 367 + $this->dateCreatedAfter); 368 + } 369 + 370 + if ($this->dateCreatedBefore) { 371 + $where[] = qsprintf( 372 + $conn, 373 + 'task.dateCreated <= %d', 374 + $this->dateCreatedBefore); 375 + } 376 + 377 + if ($this->dateModifiedAfter) { 378 + $where[] = qsprintf( 379 + $conn, 380 + 'task.dateModified >= %d', 381 + $this->dateModifiedAfter); 382 + } 383 + 384 + if ($this->dateModifiedBefore) { 385 + $where[] = qsprintf( 386 + $conn, 387 + 'task.dateModified <= %d', 388 + $this->dateModifiedBefore); 389 + } 390 + 391 + if ($this->priorities !== null) { 392 + $where[] = qsprintf( 393 + $conn, 394 + 'task.priority IN (%Ld)', 395 + $this->priorities); 396 + } 397 + 398 + if ($this->subpriorities !== null) { 399 + $where[] = qsprintf( 400 + $conn, 401 + 'task.subpriority IN (%Lf)', 402 + $this->subpriorities); 403 + } 404 + 405 + if ($this->subpriorityMin !== null) { 406 + $where[] = qsprintf( 407 + $conn, 408 + 'task.subpriority >= %f', 409 + $this->subpriorityMin); 410 + } 411 + 412 + if ($this->subpriorityMax !== null) { 413 + $where[] = qsprintf( 414 + $conn, 415 + 'task.subpriority <= %f', 416 + $this->subpriorityMax); 409 417 } 410 418 411 - return qsprintf( 412 - $conn, 413 - 'task.phid in (%Ls)', 414 - $this->taskPHIDs); 419 + return $where; 415 420 } 416 421 417 422 private function buildStatusWhereClause(AphrontDatabaseConnection $conn) { ··· 446 451 'task.status = %s', 447 452 $constant); 448 453 } 449 - } 450 - 451 - private function buildStatusesWhereClause(AphrontDatabaseConnection $conn) { 452 - if ($this->statuses) { 453 - return qsprintf( 454 - $conn, 455 - 'task.status IN (%Ls)', 456 - $this->statuses); 457 - } 458 - return null; 459 - } 460 - 461 - private function buildAuthorWhereClause(AphrontDatabaseConnection $conn) { 462 - if (!$this->authorPHIDs) { 463 - return null; 464 - } 465 - 466 - return qsprintf( 467 - $conn, 468 - 'task.authorPHID in (%Ls)', 469 - $this->authorPHIDs); 470 454 } 471 455 472 456 private function buildOwnerWhereClause(AphrontDatabaseConnection $conn) { ··· 590 574 id(new ManiphestTask())->getTableName()); 591 575 } 592 576 593 - if ($this->subscriberPHIDs) { 577 + if ($this->subscriberPHIDs !== null) { 594 578 $joins[] = qsprintf( 595 579 $conn_r, 596 580 'JOIN %T e_ccs ON e_ccs.src = task.phid '.
+2
src/applications/nuance/application/PhabricatorNuanceApplication.php
··· 73 73 NuanceSourceDefaultViewCapability::CAPABILITY => array( 74 74 'caption' => pht('Default view policy for newly created sources.'), 75 75 'template' => NuanceSourcePHIDType::TYPECONST, 76 + 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 76 77 ), 77 78 NuanceSourceDefaultEditCapability::CAPABILITY => array( 78 79 'caption' => pht('Default edit policy for newly created sources.'), 79 80 'template' => NuanceSourcePHIDType::TYPECONST, 81 + 'capability' => PhabricatorPolicyCapability::CAN_EDIT, 80 82 ), 81 83 NuanceSourceManageCapability::CAPABILITY => array(), 82 84 );
+20
src/applications/passphrase/application/PhabricatorPassphraseApplication.php
··· 63 63 ); 64 64 } 65 65 66 + protected function getCustomCapabilities() { 67 + $policy_key = id(new PassphraseCredentialAuthorPolicyRule()) 68 + ->getObjectPolicyFullKey(); 69 + 70 + return array( 71 + PassphraseDefaultViewCapability::CAPABILITY => array( 72 + 'caption' => pht('Default view policy for newly created credentials.'), 73 + 'template' => PassphraseCredentialPHIDType::TYPECONST, 74 + 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 75 + 'default' => $policy_key, 76 + ), 77 + PassphraseDefaultEditCapability::CAPABILITY => array( 78 + 'caption' => pht('Default edit policy for newly created credentials.'), 79 + 'template' => PassphraseCredentialPHIDType::TYPECONST, 80 + 'capability' => PhabricatorPolicyCapability::CAN_EDIT, 81 + 'default' => $policy_key, 82 + ), 83 + ); 84 + } 85 + 66 86 }
+12
src/applications/passphrase/capability/PassphraseDefaultEditCapability.php
··· 1 + <?php 2 + 3 + final class PassphraseDefaultEditCapability 4 + extends PhabricatorPolicyCapability { 5 + 6 + const CAPABILITY = 'passphrase.default.edit'; 7 + 8 + public function getCapabilityName() { 9 + return pht('Default Edit Policy'); 10 + } 11 + 12 + }
+16
src/applications/passphrase/capability/PassphraseDefaultViewCapability.php
··· 1 + <?php 2 + 3 + final class PassphraseDefaultViewCapability 4 + extends PhabricatorPolicyCapability { 5 + 6 + const CAPABILITY = 'passphrase.default.view'; 7 + 8 + public function getCapabilityName() { 9 + return pht('Default View Policy'); 10 + } 11 + 12 + public function shouldAllowPublicPolicySetting() { 13 + return true; 14 + } 15 + 16 + }
+9 -2
src/applications/passphrase/controller/PassphraseCredentialEditController.php
··· 60 60 $e_name = true; 61 61 62 62 $v_desc = $credential->getDescription(); 63 + $v_space = $credential->getSpacePHID(); 63 64 64 65 $v_username = $credential->getUsername(); 65 66 $e_username = true; ··· 93 94 $v_is_locked = $request->getStr('lock'); 94 95 95 96 $v_secret = $request->getStr('secret'); 97 + $v_space = $request->getStr('spacePHID'); 96 98 $v_password = $request->getStr('password'); 97 99 $v_decrypt = $v_secret; 98 100 ··· 127 129 $type_is_locked = PassphraseCredentialTransaction::TYPE_LOCK; 128 130 $type_view_policy = PhabricatorTransactions::TYPE_VIEW_POLICY; 129 131 $type_edit_policy = PhabricatorTransactions::TYPE_EDIT_POLICY; 132 + $type_space = PhabricatorTransactions::TYPE_SPACE; 130 133 131 134 $xactions = array(); 132 135 ··· 145 148 $xactions[] = id(new PassphraseCredentialTransaction()) 146 149 ->setTransactionType($type_edit_policy) 147 150 ->setNewValue($v_edit_policy); 151 + 152 + $xactions[] = id(new PassphraseCredentialTransaction()) 153 + ->setTransactionType($type_space) 154 + ->setNewValue($v_space); 148 155 149 156 // Open a transaction in case we're writing a new secret; this limits 150 157 // the amount of code which handles secret plaintexts. ··· 244 251 ->setValue($type->getCredentialTypeName())) 245 252 ->appendChild( 246 253 id(new AphrontFormDividerControl())) 247 - ->appendChild( 254 + ->appendControl( 248 255 id(new AphrontFormPolicyControl()) 249 256 ->setName('viewPolicy') 250 257 ->setPolicyObject($credential) 251 258 ->setCapability(PhabricatorPolicyCapability::CAN_VIEW) 252 259 ->setPolicies($policies)) 253 - ->appendChild( 260 + ->appendControl( 254 261 id(new AphrontFormPolicyControl()) 255 262 ->setName('editPolicy') 256 263 ->setPolicyObject($credential)
+48
src/applications/passphrase/policyrule/PassphraseCredentialAuthorPolicyRule.php
··· 1 + <?php 2 + 3 + final class PassphraseCredentialAuthorPolicyRule 4 + extends PhabricatorPolicyRule { 5 + 6 + public function getObjectPolicyKey() { 7 + return 'passphrase.author'; 8 + } 9 + 10 + public function getObjectPolicyName() { 11 + return pht('Credential Author'); 12 + } 13 + 14 + public function getPolicyExplanation() { 15 + return pht('The author of this credential can take this action.'); 16 + } 17 + 18 + public function getRuleDescription() { 19 + return pht('credential author'); 20 + } 21 + 22 + public function canApplyToObject(PhabricatorPolicyInterface $object) { 23 + return ($object instanceof PassphraseCredential); 24 + } 25 + 26 + public function applyRule( 27 + PhabricatorUser $viewer, 28 + $value, 29 + PhabricatorPolicyInterface $object) { 30 + 31 + $author_phid = $object->getAuthorPHID(); 32 + if (!$author_phid) { 33 + return false; 34 + } 35 + 36 + $viewer_phid = $viewer->getPHID(); 37 + if (!$viewer_phid) { 38 + return false; 39 + } 40 + 41 + return ($viewer_phid == $author_phid); 42 + } 43 + 44 + public function getValueControlType() { 45 + return self::CONTROL_TYPE_NONE; 46 + } 47 + 48 + }
+21 -30
src/applications/passphrase/query/PassphraseCredentialQuery.php
··· 53 53 return $this; 54 54 } 55 55 56 + public function newResultObject() { 57 + return new PassphraseCredential(); 58 + } 59 + 56 60 protected function loadPage() { 57 - $table = new PassphraseCredential(); 58 - $conn_r = $table->establishConnection('r'); 59 - 60 - $rows = queryfx_all( 61 - $conn_r, 62 - 'SELECT * FROM %T %Q %Q %Q', 63 - $table->getTableName(), 64 - $this->buildWhereClause($conn_r), 65 - $this->buildOrderClause($conn_r), 66 - $this->buildLimitClause($conn_r)); 67 - 68 - return $table->loadAllFromArray($rows); 61 + return $this->loadStandardPage($this->newResultObject()); 69 62 } 70 63 71 64 protected function willFilterPage(array $page) { ··· 99 92 return $page; 100 93 } 101 94 102 - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { 103 - $where = array(); 95 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 96 + $where = parent::buildWhereClauseParts($conn); 104 97 105 - $where[] = $this->buildPagingClause($conn_r); 106 - 107 - if ($this->ids) { 98 + if ($this->ids !== null) { 108 99 $where[] = qsprintf( 109 - $conn_r, 100 + $conn, 110 101 'id IN (%Ld)', 111 102 $this->ids); 112 103 } 113 104 114 - if ($this->phids) { 105 + if ($this->phids !== null) { 115 106 $where[] = qsprintf( 116 - $conn_r, 107 + $conn, 117 108 'phid IN (%Ls)', 118 109 $this->phids); 119 110 } 120 111 121 - if ($this->credentialTypes) { 112 + if ($this->credentialTypes !== null) { 122 113 $where[] = qsprintf( 123 - $conn_r, 114 + $conn, 124 115 'credentialType in (%Ls)', 125 116 $this->credentialTypes); 126 117 } 127 118 128 - if ($this->providesTypes) { 119 + if ($this->providesTypes !== null) { 129 120 $where[] = qsprintf( 130 - $conn_r, 121 + $conn, 131 122 'providesType IN (%Ls)', 132 123 $this->providesTypes); 133 124 } 134 125 135 126 if ($this->isDestroyed !== null) { 136 127 $where[] = qsprintf( 137 - $conn_r, 128 + $conn, 138 129 'isDestroyed = %d', 139 130 (int)$this->isDestroyed); 140 131 } 141 132 142 133 if ($this->allowConduit !== null) { 143 134 $where[] = qsprintf( 144 - $conn_r, 135 + $conn, 145 136 'allowConduit = %d', 146 137 (int)$this->allowConduit); 147 138 } 148 139 149 140 if (strlen($this->nameContains)) { 150 141 $where[] = qsprintf( 151 - $conn_r, 152 - 'name LIKE %~', 153 - $this->nameContains); 142 + $conn, 143 + 'LOWER(name) LIKE %~', 144 + phutil_utf8_strtolower($this->nameContains)); 154 145 } 155 146 156 - return $this->formatWhereClause($where); 147 + return $where; 157 148 } 158 149 159 150 public function getQueryApplicationClass() {
+22 -41
src/applications/passphrase/query/PassphraseCredentialSearchEngine.php
··· 11 11 return 'PhabricatorPassphraseApplication'; 12 12 } 13 13 14 - public function buildSavedQueryFromRequest(AphrontRequest $request) { 15 - $saved = new PhabricatorSavedQuery(); 16 - 17 - $saved->setParameter( 18 - 'isDestroyed', 19 - $this->readBoolFromRequest($request, 'isDestroyed')); 20 - $saved->setParameter('name', $request->getStr('name')); 14 + public function newQuery() { 15 + return new PassphraseCredentialQuery(); 16 + } 21 17 22 - return $saved; 18 + protected function buildCustomSearchFields() { 19 + return array( 20 + id(new PhabricatorSearchThreeStateField()) 21 + ->setLabel(pht('Status')) 22 + ->setKey('isDestroyed') 23 + ->setOptions( 24 + pht('Show All'), 25 + pht('Show Only Destroyed Credentials'), 26 + pht('Show Only Active Credentials')), 27 + id(new PhabricatorSearchTextField()) 28 + ->setLabel(pht('Name Contains')) 29 + ->setKey('name'), 30 + ); 23 31 } 24 32 25 - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { 26 - $query = id(new PassphraseCredentialQuery()); 33 + protected function buildQueryFromParameters(array $map) { 34 + $query = $this->newQuery(); 27 35 28 - $destroyed = $saved->getParameter('isDestroyed'); 29 - if ($destroyed !== null) { 30 - $query->withIsDestroyed($destroyed); 36 + if ($map['isDestroyed'] !== null) { 37 + $query->withIsDestroyed($map['isDestroyed']); 31 38 } 32 39 33 - $name = $saved->getParameter('name'); 34 - if (strlen($name)) { 35 - $query->withNameContains($name); 40 + if (strlen($map['name'])) { 41 + $query->withNameContains($map['name']); 36 42 } 37 43 38 44 return $query; 39 - } 40 - 41 - public function buildSearchForm( 42 - AphrontFormView $form, 43 - PhabricatorSavedQuery $saved_query) { 44 - 45 - $name = $saved_query->getParameter('name'); 46 - 47 - $form 48 - ->appendChild( 49 - id(new AphrontFormSelectControl()) 50 - ->setName('isDestroyed') 51 - ->setLabel(pht('Status')) 52 - ->setValue($this->getBoolFromQuery($saved_query, 'isDestroyed')) 53 - ->setOptions( 54 - array( 55 - '' => pht('Show All Credentials'), 56 - 'false' => pht('Show Only Active Credentials'), 57 - 'true' => pht('Show Only Destroyed Credentials'), 58 - ))) 59 - ->appendChild( 60 - id(new AphrontFormTextControl()) 61 - ->setName('name') 62 - ->setLabel(pht('Name Contains')) 63 - ->setValue($name)); 64 45 } 65 46 66 47 protected function getURI($path) {
+25 -3
src/applications/passphrase/storage/PassphraseCredential.php
··· 4 4 implements 5 5 PhabricatorApplicationTransactionInterface, 6 6 PhabricatorPolicyInterface, 7 - PhabricatorDestructibleInterface { 7 + PhabricatorDestructibleInterface, 8 + PhabricatorSpacesInterface { 8 9 9 10 protected $name; 10 11 protected $credentialType; ··· 17 18 protected $isDestroyed; 18 19 protected $isLocked = 0; 19 20 protected $allowConduit = 0; 21 + protected $authorPHID; 22 + protected $spacePHID; 20 23 21 24 private $secret = self::ATTACHABLE; 22 25 23 26 public static function initializeNewCredential(PhabricatorUser $actor) { 27 + $app = id(new PhabricatorApplicationQuery()) 28 + ->setViewer($actor) 29 + ->withClasses(array('PhabricatorPassphraseApplication')) 30 + ->executeOne(); 31 + 32 + $view_policy = $app->getPolicy(PassphraseDefaultViewCapability::CAPABILITY); 33 + $edit_policy = $app->getPolicy(PassphraseDefaultEditCapability::CAPABILITY); 34 + 24 35 return id(new PassphraseCredential()) 25 36 ->setName('') 26 37 ->setUsername('') 27 38 ->setDescription('') 28 39 ->setIsDestroyed(0) 29 - ->setViewPolicy($actor->getPHID()) 30 - ->setEditPolicy($actor->getPHID()); 40 + ->setAuthorPHID($actor->getPHID()) 41 + ->setViewPolicy($view_policy) 42 + ->setEditPolicy($edit_policy) 43 + ->setSpacePHID($actor->getDefaultSpacePHID()); 31 44 } 32 45 33 46 public function getMonogram() { ··· 148 161 $this->delete(); 149 162 $this->saveTransaction(); 150 163 } 164 + 165 + 166 + /* -( PhabricatorSpacesInterface )----------------------------------------- */ 167 + 168 + 169 + public function getSpacePHID() { 170 + return $this->spacePHID; 171 + } 172 + 151 173 }
+2
src/applications/paste/application/PhabricatorPasteApplication.php
··· 65 65 PasteDefaultViewCapability::CAPABILITY => array( 66 66 'caption' => pht('Default view policy for newly created pastes.'), 67 67 'template' => PhabricatorPastePastePHIDType::TYPECONST, 68 + 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 68 69 ), 69 70 PasteDefaultEditCapability::CAPABILITY => array( 70 71 'caption' => pht('Default edit policy for newly created pastes.'), 71 72 'template' => PhabricatorPastePastePHIDType::TYPECONST, 73 + 'capability' => PhabricatorPolicyCapability::CAN_EDIT, 72 74 ), 73 75 ); 74 76 }
+2
src/applications/pholio/application/PhabricatorPholioApplication.php
··· 73 73 return array( 74 74 PholioDefaultViewCapability::CAPABILITY => array( 75 75 'template' => PholioMockPHIDType::TYPECONST, 76 + 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 76 77 ), 77 78 PholioDefaultEditCapability::CAPABILITY => array( 78 79 'template' => PholioMockPHIDType::TYPECONST, 80 + 'capability' => PhabricatorPolicyCapability::CAN_EDIT, 79 81 ), 80 82 ); 81 83 }
-4
src/applications/policy/controller/PhabricatorPolicyEditController.php
··· 6 6 public function handleRequest(AphrontRequest $request) { 7 7 $viewer = $this->getViewer(); 8 8 9 - // TODO: This doesn't do anything yet, but sets up template policies; see 10 - // T6860. 11 - $is_template = false; 12 9 13 10 $object_phid = $request->getURIData('objectPHID'); 14 11 if ($object_phid) { ··· 23 20 $object_type = $request->getURIData('objectType'); 24 21 if (!$object_type) { 25 22 $object_type = $request->getURIData('templateType'); 26 - $is_template = true; 27 23 } 28 24 29 25 $phid_types = PhabricatorPHIDType::getAllInstalledTypes($viewer);
+51 -14
src/applications/policy/controller/PhabricatorPolicyExplainController.php
··· 3 3 final class PhabricatorPolicyExplainController 4 4 extends PhabricatorPolicyController { 5 5 6 - private $phid; 7 - private $capability; 8 - 9 6 public function shouldAllowPublic() { 10 7 return true; 11 8 } 12 9 13 - public function willProcessRequest(array $data) { 14 - $this->phid = $data['phid']; 15 - $this->capability = $data['capability']; 16 - } 10 + public function handleRequest(AphrontRequest $request) { 11 + $viewer = $this->getViewer(); 17 12 18 - public function processRequest() { 19 - $request = $this->getRequest(); 20 - $viewer = $request->getUser(); 21 - 22 - $phid = $this->phid; 23 - $capability = $this->capability; 13 + $phid = $request->getURIData('phid'); 14 + $capability = $request->getURIData('capability'); 24 15 25 16 $object = id(new PhabricatorObjectQuery()) 26 17 ->setViewer($viewer) ··· 84 75 $handle->getTypeName(), 85 76 $handle->getObjectName()); 86 77 87 - return $dialog 78 + $dialog 88 79 ->setTitle(pht('Policy Details: %s', $object_name)) 89 80 ->appendParagraph($intro) 90 81 ->appendChild($auto_info) 91 82 ->addCancelButton($object_uri, pht('Done')); 83 + 84 + $this->appendStrengthInformation($dialog, $object, $policy, $capability); 85 + 86 + return $dialog; 92 87 } 93 88 94 89 private function appendSpaceInformation( ··· 178 173 pht( 179 174 'After a user passes space policy checks, they must still pass '. 180 175 'object policy checks.')); 176 + } 177 + 178 + private function appendStrengthInformation( 179 + AphrontDialogView $dialog, 180 + PhabricatorPolicyInterface $object, 181 + PhabricatorPolicy $policy, 182 + $capability) { 183 + $viewer = $this->getViewer(); 184 + 185 + $default_policy = PhabricatorPolicyQuery::getDefaultPolicyForObject( 186 + $viewer, 187 + $object, 188 + $capability); 189 + if (!$default_policy) { 190 + return; 191 + } 192 + 193 + if ($default_policy->getPHID() == $policy->getPHID()) { 194 + return; 195 + } 196 + 197 + if ($default_policy->isStrongerThan($policy)) { 198 + $info = pht( 199 + 'This object has a less restrictive policy ("%s") than the default '. 200 + 'policy for similar objects (which is "%s").', 201 + $policy->getShortName(), 202 + $default_policy->getShortName()); 203 + } else if ($policy->isStrongerThan($default_policy)) { 204 + $info = pht( 205 + 'This object has a more restrictive policy ("%s") than the default '. 206 + 'policy for similar objects (which is "%s").', 207 + $policy->getShortName(), 208 + $default_policy->getShortName()); 209 + } else { 210 + $info = pht( 211 + 'This object has a different policy ("%s") than the default policy '. 212 + 'for similar objects (which is "%s").', 213 + $policy->getShortName(), 214 + $default_policy->getShortName()); 215 + } 216 + 217 + $dialog->appendParagraph($info); 181 218 } 182 219 183 220 }
+41
src/applications/policy/query/PhabricatorPolicyQuery.php
··· 342 342 return $results; 343 343 } 344 344 345 + public static function getDefaultPolicyForObject( 346 + PhabricatorUser $viewer, 347 + PhabricatorPolicyInterface $object, 348 + $capability) { 349 + 350 + $phid = $object->getPHID(); 351 + if (!$phid) { 352 + return null; 353 + } 354 + 355 + $type = phid_get_type($phid); 356 + 357 + $map = self::getDefaultObjectTypePolicyMap(); 358 + 359 + if (empty($map[$type][$capability])) { 360 + return null; 361 + } 362 + 363 + $policy_phid = $map[$type][$capability]; 364 + 365 + return id(new PhabricatorPolicyQuery()) 366 + ->setViewer($viewer) 367 + ->withPHIDs(array($policy_phid)) 368 + ->executeOne(); 369 + } 370 + 371 + private static function getDefaultObjectTypePolicyMap() { 372 + static $map; 373 + 374 + if ($map === null) { 375 + $map = array(); 376 + 377 + $apps = PhabricatorApplication::getAllApplications(); 378 + foreach ($apps as $app) { 379 + $map += $app->getDefaultObjectTypePolicyMap(); 380 + } 381 + } 382 + 383 + return $map; 384 + } 385 + 345 386 346 387 }
+3
src/applications/project/application/PhabricatorProjectApplication.php
··· 121 121 ProjectDefaultViewCapability::CAPABILITY => array( 122 122 'caption' => pht('Default view policy for newly created projects.'), 123 123 'template' => PhabricatorProjectProjectPHIDType::TYPECONST, 124 + 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 124 125 ), 125 126 ProjectDefaultEditCapability::CAPABILITY => array( 126 127 'caption' => pht('Default edit policy for newly created projects.'), 127 128 'template' => PhabricatorProjectProjectPHIDType::TYPECONST, 129 + 'capability' => PhabricatorPolicyCapability::CAN_EDIT, 128 130 ), 129 131 ProjectDefaultJoinCapability::CAPABILITY => array( 130 132 'caption' => pht('Default join policy for newly created projects.'), 131 133 'template' => PhabricatorProjectProjectPHIDType::TYPECONST, 134 + 'capability' => PhabricatorPolicyCapability::CAN_JOIN, 132 135 ), 133 136 ); 134 137 }
+1
src/applications/slowvote/application/PhabricatorSlowvoteApplication.php
··· 65 65 PhabricatorSlowvoteDefaultViewCapability::CAPABILITY => array( 66 66 'caption' => pht('Default view policy for new polls.'), 67 67 'template' => PhabricatorSlowvotePollPHIDType::TYPECONST, 68 + 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 68 69 ), 69 70 ); 70 71 }
+2
src/applications/spaces/application/PhabricatorSpacesApplication.php
··· 74 74 PhabricatorSpacesCapabilityDefaultView::CAPABILITY => array( 75 75 'caption' => pht('Default view policy for newly created spaces.'), 76 76 'template' => PhabricatorSpacesNamespacePHIDType::TYPECONST, 77 + 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 77 78 ), 78 79 PhabricatorSpacesCapabilityDefaultEdit::CAPABILITY => array( 79 80 'caption' => pht('Default edit policy for newly created spaces.'), 80 81 'default' => PhabricatorPolicies::POLICY_ADMIN, 81 82 'template' => PhabricatorSpacesNamespacePHIDType::TYPECONST, 83 + 'capability' => PhabricatorPolicyCapability::CAN_EDIT, 82 84 ), 83 85 ); 84 86 }
+247
src/docs/user/field/repository_imports.diviner
··· 1 + @title Troubleshooting Repository Imports 2 + @group fieldmanual 3 + 4 + Guide to the troubleshooting repositories which import incompletely. 5 + 6 + Overview 7 + ======== 8 + 9 + When you first import an external source code repository (or push new commits to 10 + a hosted repository), Phabricator imports those commits in the background. 11 + 12 + While a repository is initially importing, some features won't work. While 13 + individual commits are importing, some of their metadata won't be available in 14 + the web UI. 15 + 16 + Sometimes, the import process may hang or fail to complete. This document can 17 + help you understand the import process and troubleshoot problems with it. 18 + 19 + 20 + Understanding the Import Pipeline 21 + ================================= 22 + 23 + Phabricator first performs commit discovery on repositories. This examines 24 + a repository and identifies all the commits in it at a very shallow level, 25 + then creates stub objects for them. These stub objects primarily serve to 26 + assign various internal IDs to each commit. 27 + 28 + Commit discovery occurs in the update phase, and you can learn more about 29 + updates in @{article:Diffusion User Guide: Repository Updates}. 30 + 31 + After commits are discovered, background tasks are queued to actually import 32 + commits. These tasks do things like look at commit messages, trigger mentions 33 + and autoclose rules, cache changes, trigger Herald, publish feed stories and 34 + email, and apply Owners rules. You can learn more about some of these steps in 35 + @{article:Diffusion User Guide: Autoclose}. 36 + 37 + Specifically, the import pipeline has four steps: 38 + 39 + - **Message**: Parses the commit message and author metadata. 40 + - **Change**: Caches the paths the commit affected. 41 + - **Owners**: Runs Owners rules. 42 + - **Herald**: Runs Herald rules and publishes notifications. 43 + 44 + These steps run in sequence for each commit, but all discovered commits import 45 + in parallel. 46 + 47 + 48 + Identifying Missing Steps 49 + ========================= 50 + 51 + There are a few major pieces of information you can look at to understand where 52 + the import process is stuck. 53 + 54 + First, to identify which commits have missing import steps, run this command: 55 + 56 + ``` 57 + phabricator/ $ ./bin/repository importing rXYZ 58 + ``` 59 + 60 + That will show what work remains to be done. Each line shows a commit which 61 + is discovered but not imported, and the import steps that are remaining for 62 + that commit. Generally, the commit is stuck on the first step in the list. 63 + 64 + Second, load the Daemon Console (at `/daemon/` in the web UI). This will show 65 + what work is currently being done and waiting to complete. The most important 66 + sections are "Queued Tasks" (work waiting in queue) and "Leased Tasks" 67 + (work currently being done). 68 + 69 + Third, run this command to look at the daemon logs: 70 + 71 + ``` 72 + phabricator/ $ ./bin/phd log 73 + ``` 74 + 75 + This can show you any errors the daemons have encountered recently. 76 + 77 + The next sections will walk through how to use this information to understand 78 + and resolve the issue. 79 + 80 + 81 + Handling Permanent Failures 82 + =========================== 83 + 84 + Some commits can not be imported, which will permanently stop a repository from 85 + fully importing. These are rare, but can be caused by unusual data in a 86 + repository, version peculiarities, or bugs in the importer. 87 + 88 + Permanent failures usually look like a small number of commits stuck on the 89 + "Message" or "Change" steps in the output of `repository importing`. If you 90 + have a larger number of commits, it is less likely that there are any permanent 91 + problems. 92 + 93 + In the Daemon console, permanent failures usually look like a small number of 94 + tasks in "Leased Tasks" with a large failure count. These tasks are retrying 95 + until they succeed, but a bug is permanently preventing them from succeeding, 96 + so they'll rack up a large number of retries over time. 97 + 98 + In the daemon log, these commits usually emit specific errors showing why 99 + they're failing to import. 100 + 101 + These failures are the easiest to identify and understand, and can often be 102 + resolved quickly. Choose some failing commit from the output of `bin/repository 103 + importing` and use this command to re-run any missing steps manually in the 104 + foreground: 105 + 106 + ``` 107 + phabricator/ $ ./bin/repository reparse --importing --trace rXYZabcdef012... 108 + ``` 109 + 110 + This command is always safe to run, no matter what the actual root cause of 111 + the problem is. 112 + 113 + If this fails with an error, you've likely identified a problem with 114 + Phabricator. Collect as much information as you can about what makes the commit 115 + special and file a bug in the upstream by following the instructions in 116 + @{article:Contributing Bug Reports}. 117 + 118 + If the commit imports cleanly, this is more likely to be caused by some other 119 + issue. 120 + 121 + 122 + Handling Temporary Failures 123 + =========================== 124 + 125 + Some commits may temporarily fail to import: perhaps the network or services 126 + may have briefly been down, or some configuration wasn't quite right, or the 127 + daemons were killed halfway through the work. 128 + 129 + These commits will retry eventually and usually succeed, but some of the retry 130 + time limits are very conserative (up to 24 hours) and you might not want to 131 + wait that long. 132 + 133 + In the Daemon console, temporarily failures usually look like tasks in the 134 + "Leased Tasks" column with a large "Expires" value but a low "Failures" count 135 + (usually 0 or 1). The "Expires" column is showing how long Phabricator is 136 + waiting to retry these tasks. 137 + 138 + In the daemon log, these temporary failures might have created log entries, but 139 + might also not have. For example, if the failure was rooted in a network issue 140 + that probably will create a log entry, but if the faiulre was rooted in the 141 + daemons being abruptly killed that may not create a log entry. 142 + 143 + You can follow the instructions from "Handling Permanent Failures" above to 144 + reparse steps individually to look for an error that represents a root cause, 145 + but sometimes this can happen because of some transient issue which won't be 146 + identifiable. 147 + 148 + The easiest way to fix this is to restart the daemons. When you restart 149 + daemons, all task leases are immediately expired, so any tasks waiting for a 150 + long time will run right away: 151 + 152 + ``` 153 + phabricator/ $ ./bin/phd restart 154 + ``` 155 + 156 + This command is always safe to run, no matter what the actual root cause of 157 + the problem is. 158 + 159 + After restarting the daemons, any pending tasks should be able to retry 160 + immediately. 161 + 162 + For more information on managing the daemons, see 163 + @{article:Managing Daemons with phd}. 164 + 165 + 166 + Forced Parsing 167 + ============== 168 + 169 + In rare cases, the actual tasks may be lost from the task queue. Usually, they 170 + have been stolen by gremlins or spritied away by ghosts, or someone may have 171 + been too ambitious with running manual SQL commands and deleted a bunch of 172 + extra things they shouldn't have. 173 + 174 + There is no normal set of conditions under which this should occur, but you can 175 + force Phabricator to re-queue the tasks to recover from it if it does occur. 176 + 177 + This will look like missing steps in `repository importing`, but nothing in the 178 + "Queued Tasks" or "Leased Tasks" sections of the daemon console. The daemon 179 + logs will also be empty, since the tasks have vanished. 180 + 181 + To re-queue parse tasks for a repository, run this command, which will queue 182 + up all of the missing work in `repository importing`: 183 + 184 + ``` 185 + phabricator/ $ ./bin/repository reparse --importing --all rXYZ 186 + ``` 187 + 188 + This command may cause duplicate work to occur if you have misdiagnosed the 189 + problem and the tasks aren't actually lost. For example, it could queue a 190 + second task to perform publishing, which could cause Phabricator to send a 191 + second copy of email about the commit. Other than that, it is safe to run even 192 + if this isn't the problem. 193 + 194 + After running this command, you should see tasks in "Queued Tasks" and "Leased 195 + Tasks" in the console which correspond to the commits in `repository 196 + importing`, and progress should resume. 197 + 198 + 199 + Forced Imports 200 + ============== 201 + 202 + In some cases, you might want to force a repository to be flagged as imported 203 + even though the import isn't complete. The most common and reasonable case 204 + where you might want to do this is if you've identified a permanent failure 205 + with a small number of commits (maybe just one) and reported it upstream, and 206 + are waiting for a fix. You might want to start using the repository immediately, 207 + even if a few things can't import yet. 208 + 209 + You should be cautious about doing this. The "importing" flag controls 210 + publishing of notifications and email, so if you flag a repository as imported 211 + but it still has a lot of work queued, it may send an enormous amount of email 212 + as that work completes. 213 + 214 + To mark a repository as imported even though it really isn't, run this 215 + command: 216 + 217 + ``` 218 + phabricator/ $ ./bin/repository mark-imported rXYZ 219 + ``` 220 + 221 + If you do this by mistake, you can reverse it later by using the 222 + `--mark-not-imported` flag. 223 + 224 + 225 + General Tips 226 + ============ 227 + 228 + Broadly, `bin/repository` contains several useful debugging commands which 229 + let you figure out where failures are occuring. You can add the `--trace` flag 230 + to any command to get more details about what it is doing. For any command, 231 + you can use `help` to learn more about what it does and which flag it takes: 232 + 233 + ``` 234 + phabricator/ $ bin/repository help <command> 235 + ``` 236 + 237 + In particular, you can use flags with the the `repository reparse` command to 238 + manually run parse steps in the foreground, including re-running steps and 239 + running steps out of order. 240 + 241 + 242 + Next Steps 243 + ========== 244 + 245 + Continue by: 246 + 247 + - returning to the @{article:Diffusion User Guide}.
+18 -4
src/docs/user/userguide/diffusion.diviner
··· 94 94 95 95 = Next Steps = 96 96 97 - - Learn about creating a symbol index at 97 + Continue by: 98 + 99 + - learning how to creating a symbol index at 98 100 @{article:Diffusion User Guide: Symbol Indexes}; or 99 - - set up repository hosting with 101 + - setting up repository hosting with 100 102 @{article:Diffusion User Guide: Repository Hosting}; or 101 - - understand daemons in detail with @{article:Managing Daemons with phd}; or 102 - - give us feedback at @{article:Give Feedback! Get Support!}. 103 + - managing repository hooks with 104 + @{article:Diffusion User Guide: Commit Hooks}; or 105 + - understanding daemons in more detail with 106 + @{article:Managing Daemons with phd}. 107 + 108 + If you're having trouble getting things working, these topic guides may be 109 + helpful: 110 + 111 + - get details about automatically closing tasks and revisions in response 112 + to commits in @{article:Diffusion User Guide: Autoclose}; or 113 + - understand how Phabricator updates repositories with 114 + @{article:Diffusion User Guide: Repository Updates}; or 115 + - fix issues with repository imports with 116 + @{article:Troubleshooting Repository Imports}.
+8
src/docs/user/userguide/diffusion_autoclose.diviner
··· 58 58 no containing branch was configured to autoclose. 59 59 - //Field Not Present// This commit was processed before we implemented 60 60 this diagnostic feature, and no information is available. 61 + 62 + Next Steps 63 + ========== 64 + 65 + Continue by: 66 + 67 + - troubleshooting in greater depth with 68 + @{article:Troubleshooting Repository Imports}.
+8
src/docs/user/userguide/diffusion_updates.diviner
··· 113 113 114 114 To catch potential issues with permissions, run this command as the same user 115 115 that the daemons run as. 116 + 117 + Next Steps 118 + ========== 119 + 120 + Continue by: 121 + 122 + - troubleshooting in greater depth with 123 + @{article:Troubleshooting Repository Imports}.
+40 -1
src/view/phui/PHUIHeaderView.php
··· 362 362 // NOTE: We'll do this even if the viewer has access to only one space, and 363 363 // show them information about the existence of spaces if they click 364 364 // through. 365 + $use_space_policy = false; 365 366 if ($object instanceof PhabricatorSpacesInterface) { 366 367 $space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID( 367 368 $object); ··· 376 377 if ($space_policy) { 377 378 if ($space_policy->isStrongerThan($policy)) { 378 379 $policy = $space_policy; 380 + $use_space_policy = true; 379 381 } 380 382 } 381 383 } 382 384 } 383 385 386 + $container_classes = array(); 387 + $container_classes[] = 'policy-header-callout'; 384 388 $phid = $object->getPHID(); 385 389 390 + // If we're going to show the object policy, try to determine if the object 391 + // policy differs from the default policy. If it does, we'll call it out 392 + // as changed. 393 + if (!$use_space_policy) { 394 + $default_policy = PhabricatorPolicyQuery::getDefaultPolicyForObject( 395 + $viewer, 396 + $object, 397 + $view_capability); 398 + if ($default_policy) { 399 + if ($default_policy->getPHID() != $policy->getPHID()) { 400 + $container_classes[] = 'policy-adjusted'; 401 + if ($default_policy->isStrongerThan($policy)) { 402 + // The policy has strictly been weakened. For example, the 403 + // default might be "All Users" and the current policy is "Public". 404 + $container_classes[] = 'policy-adjusted-weaker'; 405 + } else if ($policy->isStrongerThan($default_policy)) { 406 + // The policy has strictly been strengthened, and is now more 407 + // restrictive than the default. For example, "All Users" has 408 + // been replaced with "No One". 409 + $container_classes[] = 'policy-adjusted-stronger'; 410 + } else { 411 + // The policy has been adjusted but not strictly strengthened 412 + // or weakened. For example, "Members of X" has been replaced with 413 + // "Members of Y". 414 + $container_classes[] = 'policy-adjusted-different'; 415 + } 416 + } 417 + } 418 + } 419 + 386 420 $icon = id(new PHUIIconView()) 387 421 ->setIconFont($policy->getIcon().' bluegrey'); 388 422 ··· 395 429 ), 396 430 $policy->getShortName()); 397 431 398 - return array($icon, $link); 432 + return phutil_tag( 433 + 'span', 434 + array( 435 + 'class' => implode(' ', $container_classes), 436 + ), 437 + array($icon, $link)); 399 438 } 400 439 401 440 }
+35
webroot/rsrc/css/phui/phui-header-view.css
··· 144 144 color: {$darkbluetext}; 145 145 } 146 146 147 + .policy-header-callout.policy-adjusted { 148 + padding: 0 4px; 149 + border-radius: 3px; 150 + } 151 + 152 + .policy-header-callout.policy-adjusted-weaker { 153 + background: {$lightgreen}; 154 + border: 1px solid {$green}; 155 + } 156 + 157 + .policy-header-callout.policy-adjusted-weaker .policy-link, 158 + .policy-header-callout.policy-adjusted-weaker .phui-icon-view { 159 + color: {$green}; 160 + } 161 + 162 + .policy-header-callout.policy-adjusted-stronger { 163 + background: {$lightred}; 164 + border: 1px solid {$red}; 165 + } 166 + 167 + .policy-header-callout.policy-adjusted-stronger .policy-link, 168 + .policy-header-callout.policy-adjusted-stronger .phui-icon-view { 169 + color: {$red}; 170 + } 171 + 172 + .policy-header-callout.policy-adjusted-different { 173 + background: {$lightorange}; 174 + border: 1px solid {$orange}; 175 + } 176 + 177 + .policy-header-callout.policy-adjusted-different .policy-link, 178 + .policy-header-callout.policy-adjusted-different .phui-icon-view { 179 + color: {$orange}; 180 + } 181 + 147 182 .phui-header-subheader .phui-header-status-dark { 148 183 color: {$sh-indigotext}; 149 184 background: {$sh-indigobackground};