1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-01-21 04:01:30 +01:00

(stable) Promote 2019 Week 21

This commit is contained in:
epriestley 2019-05-28 10:27:40 -07:00
commit d9b41d3a0f
129 changed files with 4322 additions and 1153 deletions

View file

@ -141,7 +141,7 @@ return array(
'rsrc/css/phui/phui-big-info-view.css' => '362ad37b', 'rsrc/css/phui/phui-big-info-view.css' => '362ad37b',
'rsrc/css/phui/phui-box.css' => '5ed3b8cb', 'rsrc/css/phui/phui-box.css' => '5ed3b8cb',
'rsrc/css/phui/phui-bulk-editor.css' => '374d5e30', 'rsrc/css/phui/phui-bulk-editor.css' => '374d5e30',
'rsrc/css/phui/phui-chart.css' => '7853a69b', 'rsrc/css/phui/phui-chart.css' => '10135a9d',
'rsrc/css/phui/phui-cms.css' => '8c05c41e', 'rsrc/css/phui/phui-cms.css' => '8c05c41e',
'rsrc/css/phui/phui-comment-form.css' => '68a2d99a', 'rsrc/css/phui/phui-comment-form.css' => '68a2d99a',
'rsrc/css/phui/phui-comment-panel.css' => 'ec4e31c0', 'rsrc/css/phui/phui-comment-panel.css' => 'ec4e31c0',
@ -183,7 +183,7 @@ return array(
'rsrc/css/sprite-login.css' => '18b368a6', 'rsrc/css/sprite-login.css' => '18b368a6',
'rsrc/css/sprite-tokens.css' => 'f1896dc5', 'rsrc/css/sprite-tokens.css' => 'f1896dc5',
'rsrc/css/syntax/syntax-default.css' => '055fc231', 'rsrc/css/syntax/syntax-default.css' => '055fc231',
'rsrc/externals/d3/d3.min.js' => 'd67475f5', 'rsrc/externals/d3/d3.min.js' => '9d068042',
'rsrc/externals/font/fontawesome/fontawesome-webfont.eot' => '23f8c698', 'rsrc/externals/font/fontawesome/fontawesome-webfont.eot' => '23f8c698',
'rsrc/externals/font/fontawesome/fontawesome-webfont.ttf' => '70983df0', 'rsrc/externals/font/fontawesome/fontawesome-webfont.ttf' => '70983df0',
'rsrc/externals/font/fontawesome/fontawesome-webfont.woff' => 'cd02f93b', 'rsrc/externals/font/fontawesome/fontawesome-webfont.woff' => 'cd02f93b',
@ -389,7 +389,9 @@ return array(
'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'c715c123', 'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'c715c123',
'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => '6a85bc5a', 'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => '6a85bc5a',
'rsrc/js/application/drydock/drydock-live-operation-status.js' => '47a0728b', 'rsrc/js/application/drydock/drydock-live-operation-status.js' => '47a0728b',
'rsrc/js/application/fact/Chart.js' => 'fcb0c07d', 'rsrc/js/application/fact/Chart.js' => 'eec96de0',
'rsrc/js/application/fact/ChartCurtainView.js' => '86954222',
'rsrc/js/application/fact/ChartFunctionLabel.js' => '81de1dab',
'rsrc/js/application/files/behavior-document-engine.js' => '243d6c22', 'rsrc/js/application/files/behavior-document-engine.js' => '243d6c22',
'rsrc/js/application/files/behavior-icon-composer.js' => '38a6cedb', 'rsrc/js/application/files/behavior-icon-composer.js' => '38a6cedb',
'rsrc/js/application/files/behavior-launch-icon-composer.js' => 'a17b84f1', 'rsrc/js/application/files/behavior-launch-icon-composer.js' => 'a17b84f1',
@ -398,7 +400,6 @@ return array(
'rsrc/js/application/herald/PathTypeahead.js' => 'ad486db3', 'rsrc/js/application/herald/PathTypeahead.js' => 'ad486db3',
'rsrc/js/application/herald/herald-rule-editor.js' => '0922e81d', 'rsrc/js/application/herald/herald-rule-editor.js' => '0922e81d',
'rsrc/js/application/maniphest/behavior-batch-selector.js' => '139ef688', 'rsrc/js/application/maniphest/behavior-batch-selector.js' => '139ef688',
'rsrc/js/application/maniphest/behavior-line-chart-legacy.js' => 'faf3ab6b',
'rsrc/js/application/maniphest/behavior-line-chart.js' => 'ad258e28', 'rsrc/js/application/maniphest/behavior-line-chart.js' => 'ad258e28',
'rsrc/js/application/maniphest/behavior-list-edit.js' => 'c687e867', 'rsrc/js/application/maniphest/behavior-list-edit.js' => 'c687e867',
'rsrc/js/application/owners/OwnersPathEditor.js' => '2a8b62d9', 'rsrc/js/application/owners/OwnersPathEditor.js' => '2a8b62d9',
@ -551,7 +552,7 @@ return array(
'conpherence-participant-pane-css' => '69e0058a', 'conpherence-participant-pane-css' => '69e0058a',
'conpherence-thread-manager' => 'aec8e38c', 'conpherence-thread-manager' => 'aec8e38c',
'conpherence-transaction-css' => '3a3f5e7e', 'conpherence-transaction-css' => '3a3f5e7e',
'd3' => 'd67475f5', 'd3' => '9d068042',
'differential-changeset-view-css' => 'bde53589', 'differential-changeset-view-css' => 'bde53589',
'differential-core-view-css' => '7300a73e', 'differential-core-view-css' => '7300a73e',
'differential-revision-add-comment-css' => '7e5900d9', 'differential-revision-add-comment-css' => '7e5900d9',
@ -628,7 +629,6 @@ return array(
'javelin-behavior-launch-icon-composer' => 'a17b84f1', 'javelin-behavior-launch-icon-composer' => 'a17b84f1',
'javelin-behavior-lightbox-attachments' => 'c7e748bf', 'javelin-behavior-lightbox-attachments' => 'c7e748bf',
'javelin-behavior-line-chart' => 'ad258e28', 'javelin-behavior-line-chart' => 'ad258e28',
'javelin-behavior-line-chart-legacy' => 'faf3ab6b',
'javelin-behavior-linked-container' => '74446546', 'javelin-behavior-linked-container' => '74446546',
'javelin-behavior-maniphest-batch-selector' => '139ef688', 'javelin-behavior-maniphest-batch-selector' => '139ef688',
'javelin-behavior-maniphest-list-editor' => 'c687e867', 'javelin-behavior-maniphest-list-editor' => 'c687e867',
@ -698,7 +698,9 @@ return array(
'javelin-behavior-user-menu' => '60cd9241', 'javelin-behavior-user-menu' => '60cd9241',
'javelin-behavior-view-placeholder' => 'a9942052', 'javelin-behavior-view-placeholder' => 'a9942052',
'javelin-behavior-workflow' => '9623adc1', 'javelin-behavior-workflow' => '9623adc1',
'javelin-chart' => 'fcb0c07d', 'javelin-chart' => 'eec96de0',
'javelin-chart-curtain-view' => '86954222',
'javelin-chart-function-label' => '81de1dab',
'javelin-color' => '78f811c9', 'javelin-color' => '78f811c9',
'javelin-cookie' => '05d290ef', 'javelin-cookie' => '05d290ef',
'javelin-diffusion-locate-file-source' => '94243d89', 'javelin-diffusion-locate-file-source' => '94243d89',
@ -825,7 +827,7 @@ return array(
'phui-calendar-day-css' => '9597d706', 'phui-calendar-day-css' => '9597d706',
'phui-calendar-list-css' => 'ccd7e4e2', 'phui-calendar-list-css' => 'ccd7e4e2',
'phui-calendar-month-css' => 'cb758c42', 'phui-calendar-month-css' => 'cb758c42',
'phui-chart-css' => '7853a69b', 'phui-chart-css' => '10135a9d',
'phui-cms-css' => '8c05c41e', 'phui-cms-css' => '8c05c41e',
'phui-comment-form-css' => '68a2d99a', 'phui-comment-form-css' => '68a2d99a',
'phui-comment-panel-css' => 'ec4e31c0', 'phui-comment-panel-css' => 'ec4e31c0',
@ -2123,6 +2125,12 @@ return array(
'phabricator-keyboard-shortcut', 'phabricator-keyboard-shortcut',
'javelin-stratcom', 'javelin-stratcom',
), ),
'eec96de0' => array(
'phui-chart-css',
'd3',
'javelin-chart-curtain-view',
'javelin-chart-function-label',
),
'ef836bf2' => array( 'ef836bf2' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-dom', 'javelin-dom',
@ -2182,16 +2190,6 @@ return array(
'fa74cc35' => array( 'fa74cc35' => array(
'phui-oi-list-view-css', 'phui-oi-list-view-css',
), ),
'faf3ab6b' => array(
'javelin-behavior',
'javelin-dom',
'javelin-vector',
'phui-chart-css',
),
'fcb0c07d' => array(
'phui-chart-css',
'd3',
),
'fdc13e4e' => array( 'fdc13e4e' => array(
'javelin-install', 'javelin-install',
), ),

View file

@ -1,34 +1,7 @@
<?php <?php
// See T11741. Long ago, in T11922, we switched from "MyISAM FULLTEXT" to
// "InnoDB FULLTEXT". This migration prompted installs to rebuild the index.
$use_mysql = false; // Later, in T12974, we switched from "InnoDB FULLTEXT" to "Ferret", mostly
// mooting this. The underlying tables and engines were later removed entirely.
$services = PhabricatorSearchService::getAllServices();
foreach ($services as $service) {
$engine = $service->getEngine();
if ($engine instanceof PhabricatorMySQLFulltextStorageEngine) {
$use_mysql = true;
}
}
if ($use_mysql) {
$field = new PhabricatorSearchDocumentField();
$conn = $field->establishConnection('r');
// We're only going to require this if the index isn't empty: if you're on a
// fresh install, you don't have to do anything.
$any_documents = queryfx_one(
$conn,
'SELECT * FROM %T LIMIT 1',
$field->getTableName());
if ($any_documents) {
try {
id(new PhabricatorConfigManualActivity())
->setActivityType(PhabricatorConfigManualActivity::TYPE_REINDEX)
->save();
} catch (AphrontDuplicateKeyQueryException $ex) {
// If we've already noted that this activity is required, just move on.
}
}
}

View file

@ -0,0 +1 @@
DROP TABLE IF EXISTS {$NAMESPACE}_search.search_documentfield;

View file

@ -538,6 +538,7 @@ phutil_register_library_map(array(
'DifferentialMailEngineExtension' => 'applications/differential/engineextension/DifferentialMailEngineExtension.php', 'DifferentialMailEngineExtension' => 'applications/differential/engineextension/DifferentialMailEngineExtension.php',
'DifferentialMailView' => 'applications/differential/mail/DifferentialMailView.php', 'DifferentialMailView' => 'applications/differential/mail/DifferentialMailView.php',
'DifferentialManiphestTasksField' => 'applications/differential/customfield/DifferentialManiphestTasksField.php', 'DifferentialManiphestTasksField' => 'applications/differential/customfield/DifferentialManiphestTasksField.php',
'DifferentialNoReviewersDatasource' => 'applications/differential/typeahead/DifferentialNoReviewersDatasource.php',
'DifferentialParseCacheGarbageCollector' => 'applications/differential/garbagecollector/DifferentialParseCacheGarbageCollector.php', 'DifferentialParseCacheGarbageCollector' => 'applications/differential/garbagecollector/DifferentialParseCacheGarbageCollector.php',
'DifferentialParseCommitMessageConduitAPIMethod' => 'applications/differential/conduit/DifferentialParseCommitMessageConduitAPIMethod.php', 'DifferentialParseCommitMessageConduitAPIMethod' => 'applications/differential/conduit/DifferentialParseCommitMessageConduitAPIMethod.php',
'DifferentialParseRenderTestCase' => 'applications/differential/__tests__/DifferentialParseRenderTestCase.php', 'DifferentialParseRenderTestCase' => 'applications/differential/__tests__/DifferentialParseRenderTestCase.php',
@ -561,6 +562,7 @@ phutil_register_library_map(array(
'DifferentialReviewer' => 'applications/differential/storage/DifferentialReviewer.php', 'DifferentialReviewer' => 'applications/differential/storage/DifferentialReviewer.php',
'DifferentialReviewerDatasource' => 'applications/differential/typeahead/DifferentialReviewerDatasource.php', 'DifferentialReviewerDatasource' => 'applications/differential/typeahead/DifferentialReviewerDatasource.php',
'DifferentialReviewerForRevisionEdgeType' => 'applications/differential/edge/DifferentialReviewerForRevisionEdgeType.php', 'DifferentialReviewerForRevisionEdgeType' => 'applications/differential/edge/DifferentialReviewerForRevisionEdgeType.php',
'DifferentialReviewerFunctionDatasource' => 'applications/differential/typeahead/DifferentialReviewerFunctionDatasource.php',
'DifferentialReviewerStatus' => 'applications/differential/constants/DifferentialReviewerStatus.php', 'DifferentialReviewerStatus' => 'applications/differential/constants/DifferentialReviewerStatus.php',
'DifferentialReviewersAddBlockingReviewersHeraldAction' => 'applications/differential/herald/DifferentialReviewersAddBlockingReviewersHeraldAction.php', 'DifferentialReviewersAddBlockingReviewersHeraldAction' => 'applications/differential/herald/DifferentialReviewersAddBlockingReviewersHeraldAction.php',
'DifferentialReviewersAddBlockingSelfHeraldAction' => 'applications/differential/herald/DifferentialReviewersAddBlockingSelfHeraldAction.php', 'DifferentialReviewersAddBlockingSelfHeraldAction' => 'applications/differential/herald/DifferentialReviewersAddBlockingSelfHeraldAction.php',
@ -997,6 +999,9 @@ phutil_register_library_map(array(
'DiffusionServeController' => 'applications/diffusion/controller/DiffusionServeController.php', 'DiffusionServeController' => 'applications/diffusion/controller/DiffusionServeController.php',
'DiffusionSetPasswordSettingsPanel' => 'applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php', 'DiffusionSetPasswordSettingsPanel' => 'applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php',
'DiffusionSetupException' => 'applications/diffusion/exception/DiffusionSetupException.php', 'DiffusionSetupException' => 'applications/diffusion/exception/DiffusionSetupException.php',
'DiffusionSourceHyperlinkEngineExtension' => 'applications/diffusion/engineextension/DiffusionSourceHyperlinkEngineExtension.php',
'DiffusionSourceLinkRemarkupRule' => 'applications/diffusion/remarkup/DiffusionSourceLinkRemarkupRule.php',
'DiffusionSourceLinkView' => 'applications/diffusion/view/DiffusionSourceLinkView.php',
'DiffusionSubversionCommandEngine' => 'applications/diffusion/protocol/DiffusionSubversionCommandEngine.php', 'DiffusionSubversionCommandEngine' => 'applications/diffusion/protocol/DiffusionSubversionCommandEngine.php',
'DiffusionSubversionSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSubversionSSHWorkflow.php', 'DiffusionSubversionSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSubversionSSHWorkflow.php',
'DiffusionSubversionServeSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php', 'DiffusionSubversionServeSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php',
@ -1072,7 +1077,6 @@ phutil_register_library_map(array(
'DivinerSymbolRemarkupRule' => 'applications/diviner/markup/DivinerSymbolRemarkupRule.php', 'DivinerSymbolRemarkupRule' => 'applications/diviner/markup/DivinerSymbolRemarkupRule.php',
'DivinerWorkflow' => 'applications/diviner/workflow/DivinerWorkflow.php', 'DivinerWorkflow' => 'applications/diviner/workflow/DivinerWorkflow.php',
'DoorkeeperAsanaFeedWorker' => 'applications/doorkeeper/worker/DoorkeeperAsanaFeedWorker.php', 'DoorkeeperAsanaFeedWorker' => 'applications/doorkeeper/worker/DoorkeeperAsanaFeedWorker.php',
'DoorkeeperAsanaRemarkupRule' => 'applications/doorkeeper/remarkup/DoorkeeperAsanaRemarkupRule.php',
'DoorkeeperBridge' => 'applications/doorkeeper/bridge/DoorkeeperBridge.php', 'DoorkeeperBridge' => 'applications/doorkeeper/bridge/DoorkeeperBridge.php',
'DoorkeeperBridgeAsana' => 'applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php', 'DoorkeeperBridgeAsana' => 'applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php',
'DoorkeeperBridgeGitHub' => 'applications/doorkeeper/bridge/DoorkeeperBridgeGitHub.php', 'DoorkeeperBridgeGitHub' => 'applications/doorkeeper/bridge/DoorkeeperBridgeGitHub.php',
@ -1088,15 +1092,16 @@ phutil_register_library_map(array(
'DoorkeeperExternalObjectQuery' => 'applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php', 'DoorkeeperExternalObjectQuery' => 'applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php',
'DoorkeeperFeedStoryPublisher' => 'applications/doorkeeper/engine/DoorkeeperFeedStoryPublisher.php', 'DoorkeeperFeedStoryPublisher' => 'applications/doorkeeper/engine/DoorkeeperFeedStoryPublisher.php',
'DoorkeeperFeedWorker' => 'applications/doorkeeper/worker/DoorkeeperFeedWorker.php', 'DoorkeeperFeedWorker' => 'applications/doorkeeper/worker/DoorkeeperFeedWorker.php',
'DoorkeeperHyperlinkEngineExtension' => 'applications/doorkeeper/engineextension/DoorkeeperHyperlinkEngineExtension.php',
'DoorkeeperImportEngine' => 'applications/doorkeeper/engine/DoorkeeperImportEngine.php', 'DoorkeeperImportEngine' => 'applications/doorkeeper/engine/DoorkeeperImportEngine.php',
'DoorkeeperJIRAFeedWorker' => 'applications/doorkeeper/worker/DoorkeeperJIRAFeedWorker.php', 'DoorkeeperJIRAFeedWorker' => 'applications/doorkeeper/worker/DoorkeeperJIRAFeedWorker.php',
'DoorkeeperJIRARemarkupRule' => 'applications/doorkeeper/remarkup/DoorkeeperJIRARemarkupRule.php',
'DoorkeeperMissingLinkException' => 'applications/doorkeeper/exception/DoorkeeperMissingLinkException.php', 'DoorkeeperMissingLinkException' => 'applications/doorkeeper/exception/DoorkeeperMissingLinkException.php',
'DoorkeeperObjectRef' => 'applications/doorkeeper/engine/DoorkeeperObjectRef.php', 'DoorkeeperObjectRef' => 'applications/doorkeeper/engine/DoorkeeperObjectRef.php',
'DoorkeeperRemarkupRule' => 'applications/doorkeeper/remarkup/DoorkeeperRemarkupRule.php', 'DoorkeeperRemarkupURIInterface' => 'applications/doorkeeper/interface/DoorkeeperRemarkupURIInterface.php',
'DoorkeeperSchemaSpec' => 'applications/doorkeeper/storage/DoorkeeperSchemaSpec.php', 'DoorkeeperSchemaSpec' => 'applications/doorkeeper/storage/DoorkeeperSchemaSpec.php',
'DoorkeeperTagView' => 'applications/doorkeeper/view/DoorkeeperTagView.php', 'DoorkeeperTagView' => 'applications/doorkeeper/view/DoorkeeperTagView.php',
'DoorkeeperTagsController' => 'applications/doorkeeper/controller/DoorkeeperTagsController.php', 'DoorkeeperTagsController' => 'applications/doorkeeper/controller/DoorkeeperTagsController.php',
'DoorkeeperURIRef' => 'applications/doorkeeper/engine/DoorkeeperURIRef.php',
'DrydockAcquiredBrokenResourceException' => 'applications/drydock/exception/DrydockAcquiredBrokenResourceException.php', 'DrydockAcquiredBrokenResourceException' => 'applications/drydock/exception/DrydockAcquiredBrokenResourceException.php',
'DrydockAlmanacServiceHostBlueprintImplementation' => 'applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php', 'DrydockAlmanacServiceHostBlueprintImplementation' => 'applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php',
'DrydockApacheWebrootInterface' => 'applications/drydock/interface/webroot/DrydockApacheWebrootInterface.php', 'DrydockApacheWebrootInterface' => 'applications/drydock/interface/webroot/DrydockApacheWebrootInterface.php',
@ -2107,6 +2112,7 @@ phutil_register_library_map(array(
'PhabricatorAccessLog' => 'infrastructure/log/PhabricatorAccessLog.php', 'PhabricatorAccessLog' => 'infrastructure/log/PhabricatorAccessLog.php',
'PhabricatorAccessLogConfigOptions' => 'applications/config/option/PhabricatorAccessLogConfigOptions.php', 'PhabricatorAccessLogConfigOptions' => 'applications/config/option/PhabricatorAccessLogConfigOptions.php',
'PhabricatorAccessibilitySetting' => 'applications/settings/setting/PhabricatorAccessibilitySetting.php', 'PhabricatorAccessibilitySetting' => 'applications/settings/setting/PhabricatorAccessibilitySetting.php',
'PhabricatorAccumulateChartFunction' => 'applications/fact/chart/PhabricatorAccumulateChartFunction.php',
'PhabricatorActionListView' => 'view/layout/PhabricatorActionListView.php', 'PhabricatorActionListView' => 'view/layout/PhabricatorActionListView.php',
'PhabricatorActionView' => 'view/layout/PhabricatorActionView.php', 'PhabricatorActionView' => 'view/layout/PhabricatorActionView.php',
'PhabricatorActivitySettingsPanel' => 'applications/settings/panel/PhabricatorActivitySettingsPanel.php', 'PhabricatorActivitySettingsPanel' => 'applications/settings/panel/PhabricatorActivitySettingsPanel.php',
@ -2657,9 +2663,16 @@ phutil_register_library_map(array(
'PhabricatorChangesetResponse' => 'infrastructure/diff/PhabricatorChangesetResponse.php', 'PhabricatorChangesetResponse' => 'infrastructure/diff/PhabricatorChangesetResponse.php',
'PhabricatorChartAxis' => 'applications/fact/chart/PhabricatorChartAxis.php', 'PhabricatorChartAxis' => 'applications/fact/chart/PhabricatorChartAxis.php',
'PhabricatorChartDataQuery' => 'applications/fact/chart/PhabricatorChartDataQuery.php', 'PhabricatorChartDataQuery' => 'applications/fact/chart/PhabricatorChartDataQuery.php',
'PhabricatorChartDataset' => 'applications/fact/chart/PhabricatorChartDataset.php',
'PhabricatorChartDisplayData' => 'applications/fact/chart/PhabricatorChartDisplayData.php',
'PhabricatorChartEngine' => 'applications/fact/engine/PhabricatorChartEngine.php',
'PhabricatorChartFunction' => 'applications/fact/chart/PhabricatorChartFunction.php', 'PhabricatorChartFunction' => 'applications/fact/chart/PhabricatorChartFunction.php',
'PhabricatorChartFunctionArgument' => 'applications/fact/chart/PhabricatorChartFunctionArgument.php', 'PhabricatorChartFunctionArgument' => 'applications/fact/chart/PhabricatorChartFunctionArgument.php',
'PhabricatorChartFunctionArgumentParser' => 'applications/fact/chart/PhabricatorChartFunctionArgumentParser.php', 'PhabricatorChartFunctionArgumentParser' => 'applications/fact/chart/PhabricatorChartFunctionArgumentParser.php',
'PhabricatorChartFunctionLabel' => 'applications/fact/chart/PhabricatorChartFunctionLabel.php',
'PhabricatorChartInterval' => 'applications/fact/chart/PhabricatorChartInterval.php',
'PhabricatorChartRenderingEngine' => 'applications/fact/engine/PhabricatorChartRenderingEngine.php',
'PhabricatorChartStackedAreaDataset' => 'applications/fact/chart/PhabricatorChartStackedAreaDataset.php',
'PhabricatorChatLogApplication' => 'applications/chatlog/application/PhabricatorChatLogApplication.php', 'PhabricatorChatLogApplication' => 'applications/chatlog/application/PhabricatorChatLogApplication.php',
'PhabricatorChatLogChannel' => 'applications/chatlog/storage/PhabricatorChatLogChannel.php', 'PhabricatorChatLogChannel' => 'applications/chatlog/storage/PhabricatorChatLogChannel.php',
'PhabricatorChatLogChannelListController' => 'applications/chatlog/controller/PhabricatorChatLogChannelListController.php', 'PhabricatorChatLogChannelListController' => 'applications/chatlog/controller/PhabricatorChatLogChannelListController.php',
@ -2695,6 +2708,7 @@ phutil_register_library_map(array(
'PhabricatorCommitSearchEngine' => 'applications/audit/query/PhabricatorCommitSearchEngine.php', 'PhabricatorCommitSearchEngine' => 'applications/audit/query/PhabricatorCommitSearchEngine.php',
'PhabricatorCommitTagsField' => 'applications/repository/customfield/PhabricatorCommitTagsField.php', 'PhabricatorCommitTagsField' => 'applications/repository/customfield/PhabricatorCommitTagsField.php',
'PhabricatorCommonPasswords' => 'applications/auth/constants/PhabricatorCommonPasswords.php', 'PhabricatorCommonPasswords' => 'applications/auth/constants/PhabricatorCommonPasswords.php',
'PhabricatorComposeChartFunction' => 'applications/fact/chart/PhabricatorComposeChartFunction.php',
'PhabricatorConduitAPIController' => 'applications/conduit/controller/PhabricatorConduitAPIController.php', 'PhabricatorConduitAPIController' => 'applications/conduit/controller/PhabricatorConduitAPIController.php',
'PhabricatorConduitApplication' => 'applications/conduit/application/PhabricatorConduitApplication.php', 'PhabricatorConduitApplication' => 'applications/conduit/application/PhabricatorConduitApplication.php',
'PhabricatorConduitCallManagementWorkflow' => 'applications/conduit/management/PhabricatorConduitCallManagementWorkflow.php', 'PhabricatorConduitCallManagementWorkflow' => 'applications/conduit/management/PhabricatorConduitCallManagementWorkflow.php',
@ -2928,6 +2942,8 @@ phutil_register_library_map(array(
'PhabricatorDashboardApplication' => 'applications/dashboard/application/PhabricatorDashboardApplication.php', 'PhabricatorDashboardApplication' => 'applications/dashboard/application/PhabricatorDashboardApplication.php',
'PhabricatorDashboardApplicationInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardApplicationInstallWorkflow.php', 'PhabricatorDashboardApplicationInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardApplicationInstallWorkflow.php',
'PhabricatorDashboardArchiveController' => 'applications/dashboard/controller/dashboard/PhabricatorDashboardArchiveController.php', 'PhabricatorDashboardArchiveController' => 'applications/dashboard/controller/dashboard/PhabricatorDashboardArchiveController.php',
'PhabricatorDashboardChartPanelChartTransaction' => 'applications/dashboard/xaction/panel/PhabricatorDashboardChartPanelChartTransaction.php',
'PhabricatorDashboardChartPanelType' => 'applications/dashboard/paneltype/PhabricatorDashboardChartPanelType.php',
'PhabricatorDashboardColumn' => 'applications/dashboard/layoutconfig/PhabricatorDashboardColumn.php', 'PhabricatorDashboardColumn' => 'applications/dashboard/layoutconfig/PhabricatorDashboardColumn.php',
'PhabricatorDashboardConsoleController' => 'applications/dashboard/controller/PhabricatorDashboardConsoleController.php', 'PhabricatorDashboardConsoleController' => 'applications/dashboard/controller/PhabricatorDashboardConsoleController.php',
'PhabricatorDashboardController' => 'applications/dashboard/controller/PhabricatorDashboardController.php', 'PhabricatorDashboardController' => 'applications/dashboard/controller/PhabricatorDashboardController.php',
@ -3263,6 +3279,9 @@ phutil_register_library_map(array(
'PhabricatorFeedStoryNotification' => 'applications/notification/storage/PhabricatorFeedStoryNotification.php', 'PhabricatorFeedStoryNotification' => 'applications/notification/storage/PhabricatorFeedStoryNotification.php',
'PhabricatorFeedStoryPublisher' => 'applications/feed/PhabricatorFeedStoryPublisher.php', 'PhabricatorFeedStoryPublisher' => 'applications/feed/PhabricatorFeedStoryPublisher.php',
'PhabricatorFeedStoryReference' => 'applications/feed/storage/PhabricatorFeedStoryReference.php', 'PhabricatorFeedStoryReference' => 'applications/feed/storage/PhabricatorFeedStoryReference.php',
'PhabricatorFeedTransactionListController' => 'applications/feed/controller/PhabricatorFeedTransactionListController.php',
'PhabricatorFeedTransactionQuery' => 'applications/feed/query/PhabricatorFeedTransactionQuery.php',
'PhabricatorFeedTransactionSearchEngine' => 'applications/feed/query/PhabricatorFeedTransactionSearchEngine.php',
'PhabricatorFerretEngine' => 'applications/search/ferret/PhabricatorFerretEngine.php', 'PhabricatorFerretEngine' => 'applications/search/ferret/PhabricatorFerretEngine.php',
'PhabricatorFerretEngineTestCase' => 'applications/search/ferret/__tests__/PhabricatorFerretEngineTestCase.php', 'PhabricatorFerretEngineTestCase' => 'applications/search/ferret/__tests__/PhabricatorFerretEngineTestCase.php',
'PhabricatorFerretFulltextEngineExtension' => 'applications/search/engineextension/PhabricatorFerretFulltextEngineExtension.php', 'PhabricatorFerretFulltextEngineExtension' => 'applications/search/engineextension/PhabricatorFerretFulltextEngineExtension.php',
@ -3425,6 +3444,7 @@ phutil_register_library_map(array(
'PhabricatorHeraldContentSource' => 'applications/herald/contentsource/PhabricatorHeraldContentSource.php', 'PhabricatorHeraldContentSource' => 'applications/herald/contentsource/PhabricatorHeraldContentSource.php',
'PhabricatorHexdumpDocumentEngine' => 'applications/files/document/PhabricatorHexdumpDocumentEngine.php', 'PhabricatorHexdumpDocumentEngine' => 'applications/files/document/PhabricatorHexdumpDocumentEngine.php',
'PhabricatorHighSecurityRequestExceptionHandler' => 'aphront/handler/PhabricatorHighSecurityRequestExceptionHandler.php', 'PhabricatorHighSecurityRequestExceptionHandler' => 'aphront/handler/PhabricatorHighSecurityRequestExceptionHandler.php',
'PhabricatorHigherOrderChartFunction' => 'applications/fact/chart/PhabricatorHigherOrderChartFunction.php',
'PhabricatorHomeApplication' => 'applications/home/application/PhabricatorHomeApplication.php', 'PhabricatorHomeApplication' => 'applications/home/application/PhabricatorHomeApplication.php',
'PhabricatorHomeConstants' => 'applications/home/constants/PhabricatorHomeConstants.php', 'PhabricatorHomeConstants' => 'applications/home/constants/PhabricatorHomeConstants.php',
'PhabricatorHomeController' => 'applications/home/controller/PhabricatorHomeController.php', 'PhabricatorHomeController' => 'applications/home/controller/PhabricatorHomeController.php',
@ -3602,6 +3622,7 @@ phutil_register_library_map(array(
'PhabricatorMarkupInterface' => 'infrastructure/markup/PhabricatorMarkupInterface.php', 'PhabricatorMarkupInterface' => 'infrastructure/markup/PhabricatorMarkupInterface.php',
'PhabricatorMarkupOneOff' => 'infrastructure/markup/PhabricatorMarkupOneOff.php', 'PhabricatorMarkupOneOff' => 'infrastructure/markup/PhabricatorMarkupOneOff.php',
'PhabricatorMarkupPreviewController' => 'infrastructure/markup/PhabricatorMarkupPreviewController.php', 'PhabricatorMarkupPreviewController' => 'infrastructure/markup/PhabricatorMarkupPreviewController.php',
'PhabricatorMaxChartFunction' => 'applications/fact/chart/PhabricatorMaxChartFunction.php',
'PhabricatorMemeEngine' => 'applications/macro/engine/PhabricatorMemeEngine.php', 'PhabricatorMemeEngine' => 'applications/macro/engine/PhabricatorMemeEngine.php',
'PhabricatorMemeRemarkupRule' => 'applications/macro/markup/PhabricatorMemeRemarkupRule.php', 'PhabricatorMemeRemarkupRule' => 'applications/macro/markup/PhabricatorMemeRemarkupRule.php',
'PhabricatorMentionRemarkupRule' => 'applications/people/markup/PhabricatorMentionRemarkupRule.php', 'PhabricatorMentionRemarkupRule' => 'applications/people/markup/PhabricatorMentionRemarkupRule.php',
@ -3656,6 +3677,7 @@ phutil_register_library_map(array(
'PhabricatorMetronome' => 'infrastructure/util/PhabricatorMetronome.php', 'PhabricatorMetronome' => 'infrastructure/util/PhabricatorMetronome.php',
'PhabricatorMetronomeTestCase' => 'infrastructure/util/__tests__/PhabricatorMetronomeTestCase.php', 'PhabricatorMetronomeTestCase' => 'infrastructure/util/__tests__/PhabricatorMetronomeTestCase.php',
'PhabricatorMetronomicTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorMetronomicTriggerClock.php', 'PhabricatorMetronomicTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorMetronomicTriggerClock.php',
'PhabricatorMinChartFunction' => 'applications/fact/chart/PhabricatorMinChartFunction.php',
'PhabricatorModularTransaction' => 'applications/transactions/storage/PhabricatorModularTransaction.php', 'PhabricatorModularTransaction' => 'applications/transactions/storage/PhabricatorModularTransaction.php',
'PhabricatorModularTransactionType' => 'applications/transactions/storage/PhabricatorModularTransactionType.php', 'PhabricatorModularTransactionType' => 'applications/transactions/storage/PhabricatorModularTransactionType.php',
'PhabricatorMonogramDatasourceEngineExtension' => 'applications/typeahead/engineextension/PhabricatorMonogramDatasourceEngineExtension.php', 'PhabricatorMonogramDatasourceEngineExtension' => 'applications/typeahead/engineextension/PhabricatorMonogramDatasourceEngineExtension.php',
@ -4127,6 +4149,7 @@ phutil_register_library_map(array(
'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php', 'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php',
'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php', 'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php',
'PhabricatorProjectBuiltinsExample' => 'applications/uiexample/examples/PhabricatorProjectBuiltinsExample.php', 'PhabricatorProjectBuiltinsExample' => 'applications/uiexample/examples/PhabricatorProjectBuiltinsExample.php',
'PhabricatorProjectBurndownChartEngine' => 'applications/project/chart/PhabricatorProjectBurndownChartEngine.php',
'PhabricatorProjectCardView' => 'applications/project/view/PhabricatorProjectCardView.php', 'PhabricatorProjectCardView' => 'applications/project/view/PhabricatorProjectCardView.php',
'PhabricatorProjectColorTransaction' => 'applications/project/xaction/PhabricatorProjectColorTransaction.php', 'PhabricatorProjectColorTransaction' => 'applications/project/xaction/PhabricatorProjectColorTransaction.php',
'PhabricatorProjectColorsConfigType' => 'applications/project/config/PhabricatorProjectColorsConfigType.php', 'PhabricatorProjectColorsConfigType' => 'applications/project/config/PhabricatorProjectColorsConfigType.php',
@ -4230,6 +4253,8 @@ phutil_register_library_map(array(
'PhabricatorProjectProjectPHIDType' => 'applications/project/phid/PhabricatorProjectProjectPHIDType.php', 'PhabricatorProjectProjectPHIDType' => 'applications/project/phid/PhabricatorProjectProjectPHIDType.php',
'PhabricatorProjectQuery' => 'applications/project/query/PhabricatorProjectQuery.php', 'PhabricatorProjectQuery' => 'applications/project/query/PhabricatorProjectQuery.php',
'PhabricatorProjectRemoveHeraldAction' => 'applications/project/herald/PhabricatorProjectRemoveHeraldAction.php', 'PhabricatorProjectRemoveHeraldAction' => 'applications/project/herald/PhabricatorProjectRemoveHeraldAction.php',
'PhabricatorProjectReportsController' => 'applications/project/controller/PhabricatorProjectReportsController.php',
'PhabricatorProjectReportsProfileMenuItem' => 'applications/project/menuitem/PhabricatorProjectReportsProfileMenuItem.php',
'PhabricatorProjectSchemaSpec' => 'applications/project/storage/PhabricatorProjectSchemaSpec.php', 'PhabricatorProjectSchemaSpec' => 'applications/project/storage/PhabricatorProjectSchemaSpec.php',
'PhabricatorProjectSearchEngine' => 'applications/project/query/PhabricatorProjectSearchEngine.php', 'PhabricatorProjectSearchEngine' => 'applications/project/query/PhabricatorProjectSearchEngine.php',
'PhabricatorProjectSearchField' => 'applications/project/searchfield/PhabricatorProjectSearchField.php', 'PhabricatorProjectSearchField' => 'applications/project/searchfield/PhabricatorProjectSearchField.php',
@ -4332,6 +4357,7 @@ phutil_register_library_map(array(
'PhabricatorRemarkupDocumentEngine' => 'applications/files/document/PhabricatorRemarkupDocumentEngine.php', 'PhabricatorRemarkupDocumentEngine' => 'applications/files/document/PhabricatorRemarkupDocumentEngine.php',
'PhabricatorRemarkupEditField' => 'applications/transactions/editfield/PhabricatorRemarkupEditField.php', 'PhabricatorRemarkupEditField' => 'applications/transactions/editfield/PhabricatorRemarkupEditField.php',
'PhabricatorRemarkupFigletBlockInterpreter' => 'infrastructure/markup/interpreter/PhabricatorRemarkupFigletBlockInterpreter.php', 'PhabricatorRemarkupFigletBlockInterpreter' => 'infrastructure/markup/interpreter/PhabricatorRemarkupFigletBlockInterpreter.php',
'PhabricatorRemarkupHyperlinkEngineExtension' => 'applications/remarkup/engineextension/PhabricatorRemarkupHyperlinkEngineExtension.php',
'PhabricatorRemarkupUIExample' => 'applications/uiexample/examples/PhabricatorRemarkupUIExample.php', 'PhabricatorRemarkupUIExample' => 'applications/uiexample/examples/PhabricatorRemarkupUIExample.php',
'PhabricatorRepositoriesSetupCheck' => 'applications/config/check/PhabricatorRepositoriesSetupCheck.php', 'PhabricatorRepositoriesSetupCheck' => 'applications/config/check/PhabricatorRepositoriesSetupCheck.php',
'PhabricatorRepository' => 'applications/repository/storage/PhabricatorRepository.php', 'PhabricatorRepository' => 'applications/repository/storage/PhabricatorRepository.php',
@ -4504,7 +4530,6 @@ phutil_register_library_map(array(
'PhabricatorSearchDefaultController' => 'applications/search/controller/PhabricatorSearchDefaultController.php', 'PhabricatorSearchDefaultController' => 'applications/search/controller/PhabricatorSearchDefaultController.php',
'PhabricatorSearchDeleteController' => 'applications/search/controller/PhabricatorSearchDeleteController.php', 'PhabricatorSearchDeleteController' => 'applications/search/controller/PhabricatorSearchDeleteController.php',
'PhabricatorSearchDocument' => 'applications/search/storage/document/PhabricatorSearchDocument.php', 'PhabricatorSearchDocument' => 'applications/search/storage/document/PhabricatorSearchDocument.php',
'PhabricatorSearchDocumentField' => 'applications/search/storage/document/PhabricatorSearchDocumentField.php',
'PhabricatorSearchDocumentFieldType' => 'applications/search/constants/PhabricatorSearchDocumentFieldType.php', 'PhabricatorSearchDocumentFieldType' => 'applications/search/constants/PhabricatorSearchDocumentFieldType.php',
'PhabricatorSearchDocumentQuery' => 'applications/search/query/PhabricatorSearchDocumentQuery.php', 'PhabricatorSearchDocumentQuery' => 'applications/search/query/PhabricatorSearchDocumentQuery.php',
'PhabricatorSearchDocumentRelationship' => 'applications/search/storage/document/PhabricatorSearchDocumentRelationship.php', 'PhabricatorSearchDocumentRelationship' => 'applications/search/storage/document/PhabricatorSearchDocumentRelationship.php',
@ -4725,6 +4750,7 @@ phutil_register_library_map(array(
'PhabricatorSubscriptionsUIEventListener' => 'applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php', 'PhabricatorSubscriptionsUIEventListener' => 'applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php',
'PhabricatorSubscriptionsUnsubscribeEmailCommand' => 'applications/subscriptions/command/PhabricatorSubscriptionsUnsubscribeEmailCommand.php', 'PhabricatorSubscriptionsUnsubscribeEmailCommand' => 'applications/subscriptions/command/PhabricatorSubscriptionsUnsubscribeEmailCommand.php',
'PhabricatorSubtypeEditEngineExtension' => 'applications/transactions/engineextension/PhabricatorSubtypeEditEngineExtension.php', 'PhabricatorSubtypeEditEngineExtension' => 'applications/transactions/engineextension/PhabricatorSubtypeEditEngineExtension.php',
'PhabricatorSumChartFunction' => 'applications/fact/chart/PhabricatorSumChartFunction.php',
'PhabricatorSupportApplication' => 'applications/support/application/PhabricatorSupportApplication.php', 'PhabricatorSupportApplication' => 'applications/support/application/PhabricatorSupportApplication.php',
'PhabricatorSyntaxHighlighter' => 'infrastructure/markup/PhabricatorSyntaxHighlighter.php', 'PhabricatorSyntaxHighlighter' => 'infrastructure/markup/PhabricatorSyntaxHighlighter.php',
'PhabricatorSyntaxHighlightingConfigOptions' => 'applications/config/option/PhabricatorSyntaxHighlightingConfigOptions.php', 'PhabricatorSyntaxHighlightingConfigOptions' => 'applications/config/option/PhabricatorSyntaxHighlightingConfigOptions.php',
@ -4802,6 +4828,7 @@ phutil_register_library_map(array(
'PhabricatorTransactionsApplication' => 'applications/transactions/application/PhabricatorTransactionsApplication.php', 'PhabricatorTransactionsApplication' => 'applications/transactions/application/PhabricatorTransactionsApplication.php',
'PhabricatorTransactionsDestructionEngineExtension' => 'applications/transactions/engineextension/PhabricatorTransactionsDestructionEngineExtension.php', 'PhabricatorTransactionsDestructionEngineExtension' => 'applications/transactions/engineextension/PhabricatorTransactionsDestructionEngineExtension.php',
'PhabricatorTransactionsFulltextEngineExtension' => 'applications/transactions/engineextension/PhabricatorTransactionsFulltextEngineExtension.php', 'PhabricatorTransactionsFulltextEngineExtension' => 'applications/transactions/engineextension/PhabricatorTransactionsFulltextEngineExtension.php',
'PhabricatorTransactionsObjectTypeDatasource' => 'applications/transactions/typeahead/PhabricatorTransactionsObjectTypeDatasource.php',
'PhabricatorTransformedFile' => 'applications/files/storage/PhabricatorTransformedFile.php', 'PhabricatorTransformedFile' => 'applications/files/storage/PhabricatorTransformedFile.php',
'PhabricatorTranslationSetting' => 'applications/settings/setting/PhabricatorTranslationSetting.php', 'PhabricatorTranslationSetting' => 'applications/settings/setting/PhabricatorTranslationSetting.php',
'PhabricatorTranslationsConfigOptions' => 'applications/config/option/PhabricatorTranslationsConfigOptions.php', 'PhabricatorTranslationsConfigOptions' => 'applications/config/option/PhabricatorTranslationsConfigOptions.php',
@ -4952,7 +4979,6 @@ phutil_register_library_map(array(
'PhabricatorWorkingCopyDiscoveryTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyDiscoveryTestCase.php', 'PhabricatorWorkingCopyDiscoveryTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyDiscoveryTestCase.php',
'PhabricatorWorkingCopyPullTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyPullTestCase.php', 'PhabricatorWorkingCopyPullTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyPullTestCase.php',
'PhabricatorWorkingCopyTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyTestCase.php', 'PhabricatorWorkingCopyTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyTestCase.php',
'PhabricatorXChartFunction' => 'applications/fact/chart/PhabricatorXChartFunction.php',
'PhabricatorXHPASTDAO' => 'applications/phpast/storage/PhabricatorXHPASTDAO.php', 'PhabricatorXHPASTDAO' => 'applications/phpast/storage/PhabricatorXHPASTDAO.php',
'PhabricatorXHPASTParseTree' => 'applications/phpast/storage/PhabricatorXHPASTParseTree.php', 'PhabricatorXHPASTParseTree' => 'applications/phpast/storage/PhabricatorXHPASTParseTree.php',
'PhabricatorXHPASTViewController' => 'applications/phpast/controller/PhabricatorXHPASTViewController.php', 'PhabricatorXHPASTViewController' => 'applications/phpast/controller/PhabricatorXHPASTViewController.php',
@ -6190,6 +6216,7 @@ phutil_register_library_map(array(
'DifferentialMailEngineExtension' => 'PhabricatorMailEngineExtension', 'DifferentialMailEngineExtension' => 'PhabricatorMailEngineExtension',
'DifferentialMailView' => 'Phobject', 'DifferentialMailView' => 'Phobject',
'DifferentialManiphestTasksField' => 'DifferentialCoreCustomField', 'DifferentialManiphestTasksField' => 'DifferentialCoreCustomField',
'DifferentialNoReviewersDatasource' => 'PhabricatorTypeaheadDatasource',
'DifferentialParseCacheGarbageCollector' => 'PhabricatorGarbageCollector', 'DifferentialParseCacheGarbageCollector' => 'PhabricatorGarbageCollector',
'DifferentialParseCommitMessageConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialParseCommitMessageConduitAPIMethod' => 'DifferentialConduitAPIMethod',
'DifferentialParseRenderTestCase' => 'PhabricatorTestCase', 'DifferentialParseRenderTestCase' => 'PhabricatorTestCase',
@ -6213,6 +6240,7 @@ phutil_register_library_map(array(
'DifferentialReviewer' => 'DifferentialDAO', 'DifferentialReviewer' => 'DifferentialDAO',
'DifferentialReviewerDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'DifferentialReviewerDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'DifferentialReviewerForRevisionEdgeType' => 'PhabricatorEdgeType', 'DifferentialReviewerForRevisionEdgeType' => 'PhabricatorEdgeType',
'DifferentialReviewerFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'DifferentialReviewerStatus' => 'Phobject', 'DifferentialReviewerStatus' => 'Phobject',
'DifferentialReviewersAddBlockingReviewersHeraldAction' => 'DifferentialReviewersHeraldAction', 'DifferentialReviewersAddBlockingReviewersHeraldAction' => 'DifferentialReviewersHeraldAction',
'DifferentialReviewersAddBlockingSelfHeraldAction' => 'DifferentialReviewersHeraldAction', 'DifferentialReviewersAddBlockingSelfHeraldAction' => 'DifferentialReviewersHeraldAction',
@ -6670,6 +6698,9 @@ phutil_register_library_map(array(
'DiffusionServeController' => 'DiffusionController', 'DiffusionServeController' => 'DiffusionController',
'DiffusionSetPasswordSettingsPanel' => 'PhabricatorSettingsPanel', 'DiffusionSetPasswordSettingsPanel' => 'PhabricatorSettingsPanel',
'DiffusionSetupException' => 'Exception', 'DiffusionSetupException' => 'Exception',
'DiffusionSourceHyperlinkEngineExtension' => 'PhabricatorRemarkupHyperlinkEngineExtension',
'DiffusionSourceLinkRemarkupRule' => 'PhutilRemarkupRule',
'DiffusionSourceLinkView' => 'AphrontView',
'DiffusionSubversionCommandEngine' => 'DiffusionCommandEngine', 'DiffusionSubversionCommandEngine' => 'DiffusionCommandEngine',
'DiffusionSubversionSSHWorkflow' => 'DiffusionSSHWorkflow', 'DiffusionSubversionSSHWorkflow' => 'DiffusionSSHWorkflow',
'DiffusionSubversionServeSSHWorkflow' => 'DiffusionSubversionSSHWorkflow', 'DiffusionSubversionServeSSHWorkflow' => 'DiffusionSubversionSSHWorkflow',
@ -6758,7 +6789,6 @@ phutil_register_library_map(array(
'DivinerSymbolRemarkupRule' => 'PhutilRemarkupRule', 'DivinerSymbolRemarkupRule' => 'PhutilRemarkupRule',
'DivinerWorkflow' => 'PhabricatorManagementWorkflow', 'DivinerWorkflow' => 'PhabricatorManagementWorkflow',
'DoorkeeperAsanaFeedWorker' => 'DoorkeeperFeedWorker', 'DoorkeeperAsanaFeedWorker' => 'DoorkeeperFeedWorker',
'DoorkeeperAsanaRemarkupRule' => 'DoorkeeperRemarkupRule',
'DoorkeeperBridge' => 'Phobject', 'DoorkeeperBridge' => 'Phobject',
'DoorkeeperBridgeAsana' => 'DoorkeeperBridge', 'DoorkeeperBridgeAsana' => 'DoorkeeperBridge',
'DoorkeeperBridgeGitHub' => 'DoorkeeperBridge', 'DoorkeeperBridgeGitHub' => 'DoorkeeperBridge',
@ -6776,15 +6806,15 @@ phutil_register_library_map(array(
'DoorkeeperExternalObjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'DoorkeeperExternalObjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'DoorkeeperFeedStoryPublisher' => 'Phobject', 'DoorkeeperFeedStoryPublisher' => 'Phobject',
'DoorkeeperFeedWorker' => 'FeedPushWorker', 'DoorkeeperFeedWorker' => 'FeedPushWorker',
'DoorkeeperHyperlinkEngineExtension' => 'PhabricatorRemarkupHyperlinkEngineExtension',
'DoorkeeperImportEngine' => 'Phobject', 'DoorkeeperImportEngine' => 'Phobject',
'DoorkeeperJIRAFeedWorker' => 'DoorkeeperFeedWorker', 'DoorkeeperJIRAFeedWorker' => 'DoorkeeperFeedWorker',
'DoorkeeperJIRARemarkupRule' => 'DoorkeeperRemarkupRule',
'DoorkeeperMissingLinkException' => 'Exception', 'DoorkeeperMissingLinkException' => 'Exception',
'DoorkeeperObjectRef' => 'Phobject', 'DoorkeeperObjectRef' => 'Phobject',
'DoorkeeperRemarkupRule' => 'PhutilRemarkupRule',
'DoorkeeperSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'DoorkeeperSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'DoorkeeperTagView' => 'AphrontView', 'DoorkeeperTagView' => 'AphrontView',
'DoorkeeperTagsController' => 'PhabricatorController', 'DoorkeeperTagsController' => 'PhabricatorController',
'DoorkeeperURIRef' => 'Phobject',
'DrydockAcquiredBrokenResourceException' => 'Exception', 'DrydockAcquiredBrokenResourceException' => 'Exception',
'DrydockAlmanacServiceHostBlueprintImplementation' => 'DrydockBlueprintImplementation', 'DrydockAlmanacServiceHostBlueprintImplementation' => 'DrydockBlueprintImplementation',
'DrydockApacheWebrootInterface' => 'DrydockWebrootInterface', 'DrydockApacheWebrootInterface' => 'DrydockWebrootInterface',
@ -7987,6 +8017,7 @@ phutil_register_library_map(array(
'PhabricatorAccessLog' => 'Phobject', 'PhabricatorAccessLog' => 'Phobject',
'PhabricatorAccessLogConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorAccessLogConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorAccessibilitySetting' => 'PhabricatorSelectSetting', 'PhabricatorAccessibilitySetting' => 'PhabricatorSelectSetting',
'PhabricatorAccumulateChartFunction' => 'PhabricatorChartFunction',
'PhabricatorActionListView' => 'AphrontTagView', 'PhabricatorActionListView' => 'AphrontTagView',
'PhabricatorActionView' => 'AphrontView', 'PhabricatorActionView' => 'AphrontView',
'PhabricatorActivitySettingsPanel' => 'PhabricatorSettingsPanel', 'PhabricatorActivitySettingsPanel' => 'PhabricatorSettingsPanel',
@ -8086,7 +8117,10 @@ phutil_register_library_map(array(
'PhabricatorApplicationsController' => 'PhabricatorController', 'PhabricatorApplicationsController' => 'PhabricatorController',
'PhabricatorApplicationsListController' => 'PhabricatorApplicationsController', 'PhabricatorApplicationsListController' => 'PhabricatorApplicationsController',
'PhabricatorApplyEditField' => 'PhabricatorEditField', 'PhabricatorApplyEditField' => 'PhabricatorEditField',
'PhabricatorAsanaAuthProvider' => 'PhabricatorOAuth2AuthProvider', 'PhabricatorAsanaAuthProvider' => array(
'PhabricatorOAuth2AuthProvider',
'DoorkeeperRemarkupURIInterface',
),
'PhabricatorAsanaConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorAsanaConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorAsanaSubtaskHasObjectEdgeType' => 'PhabricatorEdgeType', 'PhabricatorAsanaSubtaskHasObjectEdgeType' => 'PhabricatorEdgeType',
'PhabricatorAsanaTaskHasObjectEdgeType' => 'PhabricatorEdgeType', 'PhabricatorAsanaTaskHasObjectEdgeType' => 'PhabricatorEdgeType',
@ -8648,9 +8682,16 @@ phutil_register_library_map(array(
'PhabricatorChangesetResponse' => 'AphrontProxyResponse', 'PhabricatorChangesetResponse' => 'AphrontProxyResponse',
'PhabricatorChartAxis' => 'Phobject', 'PhabricatorChartAxis' => 'Phobject',
'PhabricatorChartDataQuery' => 'Phobject', 'PhabricatorChartDataQuery' => 'Phobject',
'PhabricatorChartDataset' => 'Phobject',
'PhabricatorChartDisplayData' => 'Phobject',
'PhabricatorChartEngine' => 'Phobject',
'PhabricatorChartFunction' => 'Phobject', 'PhabricatorChartFunction' => 'Phobject',
'PhabricatorChartFunctionArgument' => 'Phobject', 'PhabricatorChartFunctionArgument' => 'Phobject',
'PhabricatorChartFunctionArgumentParser' => 'Phobject', 'PhabricatorChartFunctionArgumentParser' => 'Phobject',
'PhabricatorChartFunctionLabel' => 'Phobject',
'PhabricatorChartInterval' => 'Phobject',
'PhabricatorChartRenderingEngine' => 'Phobject',
'PhabricatorChartStackedAreaDataset' => 'PhabricatorChartDataset',
'PhabricatorChatLogApplication' => 'PhabricatorApplication', 'PhabricatorChatLogApplication' => 'PhabricatorApplication',
'PhabricatorChatLogChannel' => array( 'PhabricatorChatLogChannel' => array(
'PhabricatorChatLogDAO', 'PhabricatorChatLogDAO',
@ -8691,6 +8732,7 @@ phutil_register_library_map(array(
'PhabricatorCommitSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorCommitSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorCommitTagsField' => 'PhabricatorCommitCustomField', 'PhabricatorCommitTagsField' => 'PhabricatorCommitCustomField',
'PhabricatorCommonPasswords' => 'Phobject', 'PhabricatorCommonPasswords' => 'Phobject',
'PhabricatorComposeChartFunction' => 'PhabricatorHigherOrderChartFunction',
'PhabricatorConduitAPIController' => 'PhabricatorConduitController', 'PhabricatorConduitAPIController' => 'PhabricatorConduitController',
'PhabricatorConduitApplication' => 'PhabricatorApplication', 'PhabricatorConduitApplication' => 'PhabricatorApplication',
'PhabricatorConduitCallManagementWorkflow' => 'PhabricatorConduitManagementWorkflow', 'PhabricatorConduitCallManagementWorkflow' => 'PhabricatorConduitManagementWorkflow',
@ -8957,6 +8999,8 @@ phutil_register_library_map(array(
'PhabricatorDashboardApplication' => 'PhabricatorApplication', 'PhabricatorDashboardApplication' => 'PhabricatorApplication',
'PhabricatorDashboardApplicationInstallWorkflow' => 'PhabricatorDashboardInstallWorkflow', 'PhabricatorDashboardApplicationInstallWorkflow' => 'PhabricatorDashboardInstallWorkflow',
'PhabricatorDashboardArchiveController' => 'PhabricatorDashboardController', 'PhabricatorDashboardArchiveController' => 'PhabricatorDashboardController',
'PhabricatorDashboardChartPanelChartTransaction' => 'PhabricatorDashboardPanelPropertyTransaction',
'PhabricatorDashboardChartPanelType' => 'PhabricatorDashboardPanelType',
'PhabricatorDashboardColumn' => 'Phobject', 'PhabricatorDashboardColumn' => 'Phobject',
'PhabricatorDashboardConsoleController' => 'PhabricatorDashboardController', 'PhabricatorDashboardConsoleController' => 'PhabricatorDashboardController',
'PhabricatorDashboardController' => 'PhabricatorController', 'PhabricatorDashboardController' => 'PhabricatorController',
@ -9323,6 +9367,9 @@ phutil_register_library_map(array(
'PhabricatorFeedStoryNotification' => 'PhabricatorFeedDAO', 'PhabricatorFeedStoryNotification' => 'PhabricatorFeedDAO',
'PhabricatorFeedStoryPublisher' => 'Phobject', 'PhabricatorFeedStoryPublisher' => 'Phobject',
'PhabricatorFeedStoryReference' => 'PhabricatorFeedDAO', 'PhabricatorFeedStoryReference' => 'PhabricatorFeedDAO',
'PhabricatorFeedTransactionListController' => 'PhabricatorFeedController',
'PhabricatorFeedTransactionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorFeedTransactionSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorFerretEngine' => 'Phobject', 'PhabricatorFerretEngine' => 'Phobject',
'PhabricatorFerretEngineTestCase' => 'PhabricatorTestCase', 'PhabricatorFerretEngineTestCase' => 'PhabricatorTestCase',
'PhabricatorFerretFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension', 'PhabricatorFerretFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
@ -9520,6 +9567,7 @@ phutil_register_library_map(array(
'PhabricatorHeraldContentSource' => 'PhabricatorContentSource', 'PhabricatorHeraldContentSource' => 'PhabricatorContentSource',
'PhabricatorHexdumpDocumentEngine' => 'PhabricatorDocumentEngine', 'PhabricatorHexdumpDocumentEngine' => 'PhabricatorDocumentEngine',
'PhabricatorHighSecurityRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler', 'PhabricatorHighSecurityRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
'PhabricatorHigherOrderChartFunction' => 'PhabricatorChartFunction',
'PhabricatorHomeApplication' => 'PhabricatorApplication', 'PhabricatorHomeApplication' => 'PhabricatorApplication',
'PhabricatorHomeConstants' => 'PhabricatorHomeController', 'PhabricatorHomeConstants' => 'PhabricatorHomeController',
'PhabricatorHomeController' => 'PhabricatorController', 'PhabricatorHomeController' => 'PhabricatorController',
@ -9563,7 +9611,10 @@ phutil_register_library_map(array(
'PhabricatorIteratedMD5PasswordHasher' => 'PhabricatorPasswordHasher', 'PhabricatorIteratedMD5PasswordHasher' => 'PhabricatorPasswordHasher',
'PhabricatorIteratedMD5PasswordHasherTestCase' => 'PhabricatorTestCase', 'PhabricatorIteratedMD5PasswordHasherTestCase' => 'PhabricatorTestCase',
'PhabricatorIteratorFileUploadSource' => 'PhabricatorFileUploadSource', 'PhabricatorIteratorFileUploadSource' => 'PhabricatorFileUploadSource',
'PhabricatorJIRAAuthProvider' => 'PhabricatorOAuth1AuthProvider', 'PhabricatorJIRAAuthProvider' => array(
'PhabricatorOAuth1AuthProvider',
'DoorkeeperRemarkupURIInterface',
),
'PhabricatorJSONConfigType' => 'PhabricatorTextConfigType', 'PhabricatorJSONConfigType' => 'PhabricatorTextConfigType',
'PhabricatorJSONDocumentEngine' => 'PhabricatorTextDocumentEngine', 'PhabricatorJSONDocumentEngine' => 'PhabricatorTextDocumentEngine',
'PhabricatorJSONExportFormat' => 'PhabricatorExportFormat', 'PhabricatorJSONExportFormat' => 'PhabricatorExportFormat',
@ -9698,6 +9749,7 @@ phutil_register_library_map(array(
'PhabricatorMarkupInterface', 'PhabricatorMarkupInterface',
), ),
'PhabricatorMarkupPreviewController' => 'PhabricatorController', 'PhabricatorMarkupPreviewController' => 'PhabricatorController',
'PhabricatorMaxChartFunction' => 'PhabricatorChartFunction',
'PhabricatorMemeEngine' => 'Phobject', 'PhabricatorMemeEngine' => 'Phobject',
'PhabricatorMemeRemarkupRule' => 'PhutilRemarkupRule', 'PhabricatorMemeRemarkupRule' => 'PhutilRemarkupRule',
'PhabricatorMentionRemarkupRule' => 'PhutilRemarkupRule', 'PhabricatorMentionRemarkupRule' => 'PhutilRemarkupRule',
@ -9764,6 +9816,7 @@ phutil_register_library_map(array(
'PhabricatorMetronome' => 'Phobject', 'PhabricatorMetronome' => 'Phobject',
'PhabricatorMetronomeTestCase' => 'PhabricatorTestCase', 'PhabricatorMetronomeTestCase' => 'PhabricatorTestCase',
'PhabricatorMetronomicTriggerClock' => 'PhabricatorTriggerClock', 'PhabricatorMetronomicTriggerClock' => 'PhabricatorTriggerClock',
'PhabricatorMinChartFunction' => 'PhabricatorChartFunction',
'PhabricatorModularTransaction' => 'PhabricatorApplicationTransaction', 'PhabricatorModularTransaction' => 'PhabricatorApplicationTransaction',
'PhabricatorModularTransactionType' => 'Phobject', 'PhabricatorModularTransactionType' => 'Phobject',
'PhabricatorMonogramDatasourceEngineExtension' => 'PhabricatorDatasourceEngineExtension', 'PhabricatorMonogramDatasourceEngineExtension' => 'PhabricatorDatasourceEngineExtension',
@ -10344,6 +10397,7 @@ phutil_register_library_map(array(
'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectBuiltinsExample' => 'PhabricatorUIExample', 'PhabricatorProjectBuiltinsExample' => 'PhabricatorUIExample',
'PhabricatorProjectBurndownChartEngine' => 'PhabricatorChartEngine',
'PhabricatorProjectCardView' => 'AphrontTagView', 'PhabricatorProjectCardView' => 'AphrontTagView',
'PhabricatorProjectColorTransaction' => 'PhabricatorProjectTransactionType', 'PhabricatorProjectColorTransaction' => 'PhabricatorProjectTransactionType',
'PhabricatorProjectColorsConfigType' => 'PhabricatorJSONConfigType', 'PhabricatorProjectColorsConfigType' => 'PhabricatorJSONConfigType',
@ -10459,6 +10513,8 @@ phutil_register_library_map(array(
'PhabricatorProjectProjectPHIDType' => 'PhabricatorPHIDType', 'PhabricatorProjectProjectPHIDType' => 'PhabricatorPHIDType',
'PhabricatorProjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorProjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorProjectRemoveHeraldAction' => 'PhabricatorProjectHeraldAction', 'PhabricatorProjectRemoveHeraldAction' => 'PhabricatorProjectHeraldAction',
'PhabricatorProjectReportsController' => 'PhabricatorProjectController',
'PhabricatorProjectReportsProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorProjectSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'PhabricatorProjectSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'PhabricatorProjectSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorProjectSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorProjectSearchField' => 'PhabricatorSearchTokenizerField', 'PhabricatorProjectSearchField' => 'PhabricatorSearchTokenizerField',
@ -10573,6 +10629,7 @@ phutil_register_library_map(array(
'PhabricatorRemarkupDocumentEngine' => 'PhabricatorDocumentEngine', 'PhabricatorRemarkupDocumentEngine' => 'PhabricatorDocumentEngine',
'PhabricatorRemarkupEditField' => 'PhabricatorEditField', 'PhabricatorRemarkupEditField' => 'PhabricatorEditField',
'PhabricatorRemarkupFigletBlockInterpreter' => 'PhutilRemarkupBlockInterpreter', 'PhabricatorRemarkupFigletBlockInterpreter' => 'PhutilRemarkupBlockInterpreter',
'PhabricatorRemarkupHyperlinkEngineExtension' => 'PhutilRemarkupHyperlinkEngineExtension',
'PhabricatorRemarkupUIExample' => 'PhabricatorUIExample', 'PhabricatorRemarkupUIExample' => 'PhabricatorUIExample',
'PhabricatorRepositoriesSetupCheck' => 'PhabricatorSetupCheck', 'PhabricatorRepositoriesSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorRepository' => array( 'PhabricatorRepository' => array(
@ -10816,7 +10873,6 @@ phutil_register_library_map(array(
'PhabricatorSearchDefaultController' => 'PhabricatorSearchBaseController', 'PhabricatorSearchDefaultController' => 'PhabricatorSearchBaseController',
'PhabricatorSearchDeleteController' => 'PhabricatorSearchBaseController', 'PhabricatorSearchDeleteController' => 'PhabricatorSearchBaseController',
'PhabricatorSearchDocument' => 'PhabricatorSearchDAO', 'PhabricatorSearchDocument' => 'PhabricatorSearchDAO',
'PhabricatorSearchDocumentField' => 'PhabricatorSearchDAO',
'PhabricatorSearchDocumentFieldType' => 'Phobject', 'PhabricatorSearchDocumentFieldType' => 'Phobject',
'PhabricatorSearchDocumentQuery' => 'PhabricatorPolicyAwareQuery', 'PhabricatorSearchDocumentQuery' => 'PhabricatorPolicyAwareQuery',
'PhabricatorSearchDocumentRelationship' => 'PhabricatorSearchDAO', 'PhabricatorSearchDocumentRelationship' => 'PhabricatorSearchDAO',
@ -10861,7 +10917,7 @@ phutil_register_library_map(array(
'PhabricatorSecuritySetupCheck' => 'PhabricatorSetupCheck', 'PhabricatorSecuritySetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorSelectEditField' => 'PhabricatorEditField', 'PhabricatorSelectEditField' => 'PhabricatorEditField',
'PhabricatorSelectSetting' => 'PhabricatorSetting', 'PhabricatorSelectSetting' => 'PhabricatorSetting',
'PhabricatorSelfHyperlinkEngineExtension' => 'PhutilRemarkupHyperlinkEngineExtension', 'PhabricatorSelfHyperlinkEngineExtension' => 'PhabricatorRemarkupHyperlinkEngineExtension',
'PhabricatorSessionsSettingsPanel' => 'PhabricatorSettingsPanel', 'PhabricatorSessionsSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorSetConfigType' => 'PhabricatorTextConfigType', 'PhabricatorSetConfigType' => 'PhabricatorTextConfigType',
'PhabricatorSetting' => 'Phobject', 'PhabricatorSetting' => 'Phobject',
@ -11053,6 +11109,7 @@ phutil_register_library_map(array(
'PhabricatorSubscriptionsUIEventListener' => 'PhabricatorEventListener', 'PhabricatorSubscriptionsUIEventListener' => 'PhabricatorEventListener',
'PhabricatorSubscriptionsUnsubscribeEmailCommand' => 'MetaMTAEmailTransactionCommand', 'PhabricatorSubscriptionsUnsubscribeEmailCommand' => 'MetaMTAEmailTransactionCommand',
'PhabricatorSubtypeEditEngineExtension' => 'PhabricatorEditEngineExtension', 'PhabricatorSubtypeEditEngineExtension' => 'PhabricatorEditEngineExtension',
'PhabricatorSumChartFunction' => 'PhabricatorHigherOrderChartFunction',
'PhabricatorSupportApplication' => 'PhabricatorApplication', 'PhabricatorSupportApplication' => 'PhabricatorApplication',
'PhabricatorSyntaxHighlighter' => 'Phobject', 'PhabricatorSyntaxHighlighter' => 'Phobject',
'PhabricatorSyntaxHighlightingConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorSyntaxHighlightingConfigOptions' => 'PhabricatorApplicationConfigOptions',
@ -11140,6 +11197,7 @@ phutil_register_library_map(array(
'PhabricatorTransactionsApplication' => 'PhabricatorApplication', 'PhabricatorTransactionsApplication' => 'PhabricatorApplication',
'PhabricatorTransactionsDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension', 'PhabricatorTransactionsDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
'PhabricatorTransactionsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension', 'PhabricatorTransactionsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
'PhabricatorTransactionsObjectTypeDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorTransformedFile' => 'PhabricatorFileDAO', 'PhabricatorTransformedFile' => 'PhabricatorFileDAO',
'PhabricatorTranslationSetting' => 'PhabricatorOptionGroupSetting', 'PhabricatorTranslationSetting' => 'PhabricatorOptionGroupSetting',
'PhabricatorTranslationsConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorTranslationsConfigOptions' => 'PhabricatorApplicationConfigOptions',
@ -11323,7 +11381,6 @@ phutil_register_library_map(array(
'PhabricatorWorkingCopyDiscoveryTestCase' => 'PhabricatorWorkingCopyTestCase', 'PhabricatorWorkingCopyDiscoveryTestCase' => 'PhabricatorWorkingCopyTestCase',
'PhabricatorWorkingCopyPullTestCase' => 'PhabricatorWorkingCopyTestCase', 'PhabricatorWorkingCopyPullTestCase' => 'PhabricatorWorkingCopyTestCase',
'PhabricatorWorkingCopyTestCase' => 'PhabricatorTestCase', 'PhabricatorWorkingCopyTestCase' => 'PhabricatorTestCase',
'PhabricatorXChartFunction' => 'PhabricatorChartFunction',
'PhabricatorXHPASTDAO' => 'PhabricatorLiskDAO', 'PhabricatorXHPASTDAO' => 'PhabricatorLiskDAO',
'PhabricatorXHPASTParseTree' => 'PhabricatorXHPASTDAO', 'PhabricatorXHPASTParseTree' => 'PhabricatorXHPASTDAO',
'PhabricatorXHPASTViewController' => 'PhabricatorController', 'PhabricatorXHPASTViewController' => 'PhabricatorController',

View file

@ -663,7 +663,7 @@ final class AphrontRequest extends Phobject {
} }
public function isContinueRequest() { public function isContinueRequest() {
return $this->isFormPost() && $this->getStr('__continue__'); return $this->isFormOrHisecPost() && $this->getStr('__continue__');
} }
public function isPreviewRequest() { public function isPreviewRequest() {

View file

@ -120,6 +120,13 @@ final class PhabricatorHighSecurityRequestExceptionHandler
$dialog->addHiddenInput($key, $value); $dialog->addHiddenInput($key, $value);
} }
// See T13289. If the user hit a "some transactions have no effect" dialog
// and elected to continue, we want to pass that flag through the MFA
// dialog even though it is not normally a passthrough request parameter.
if ($request->isContinueRequest()) {
$dialog->addHiddenInput(AphrontRequest::TYPE_CONTINUE, 1);
}
return $dialog; return $dialog;
} }

View file

@ -239,7 +239,7 @@ final class PhabricatorAuditEditor
$object); $object);
if ($request) { if ($request) {
$xactions[] = $request; $xactions[] = $request;
$this->setUnmentionablePHIDMap($request->getNewValue()); $this->addUnmentionablePHIDs($request->getNewValue());
} }
break; break;
default: default:
@ -360,7 +360,6 @@ final class PhabricatorAuditEditor
$flat_blocks = mpull($changes, 'getNewValue'); $flat_blocks = mpull($changes, 'getNewValue');
$huge_block = implode("\n\n", $flat_blocks); $huge_block = implode("\n\n", $flat_blocks);
$phid_map = array(); $phid_map = array();
$phid_map[] = $this->getUnmentionablePHIDMap();
$monograms = array(); $monograms = array();
$task_refs = id(new ManiphestCustomFieldStatusParser()) $task_refs = id(new ManiphestCustomFieldStatusParser())
@ -385,7 +384,6 @@ final class PhabricatorAuditEditor
->execute(); ->execute();
$phid_map[] = mpull($objects, 'getPHID', 'getPHID'); $phid_map[] = mpull($objects, 'getPHID', 'getPHID');
$reverts_refs = id(new DifferentialCustomFieldRevertsParser()) $reverts_refs = id(new DifferentialCustomFieldRevertsParser())
->parseCorpus($huge_block); ->parseCorpus($huge_block);
$reverts = array_mergev(ipull($reverts_refs, 'monograms')); $reverts = array_mergev(ipull($reverts_refs, 'monograms'));
@ -408,7 +406,7 @@ final class PhabricatorAuditEditor
} }
$phid_map = array_mergev($phid_map); $phid_map = array_mergev($phid_map);
$this->setUnmentionablePHIDMap($phid_map); $this->addUnmentionablePHIDs($phid_map);
return $result; return $result;
} }

View file

@ -50,7 +50,7 @@ final class PhabricatorAuditTransaction
switch ($type) { switch ($type) {
case self::TYPE_COMMIT: case self::TYPE_COMMIT:
return 3.0; return 300;
} }
return parent::getActionStrength(); return parent::getActionStrength();

View file

@ -1,6 +1,8 @@
<?php <?php
final class PhabricatorAsanaAuthProvider extends PhabricatorOAuth2AuthProvider { final class PhabricatorAsanaAuthProvider
extends PhabricatorOAuth2AuthProvider
implements DoorkeeperRemarkupURIInterface {
public function getProviderName() { public function getProviderName() {
return pht('Asana'); return pht('Asana');
@ -46,4 +48,26 @@ final class PhabricatorAsanaAuthProvider extends PhabricatorOAuth2AuthProvider {
return null; return null;
} }
/* -( DoorkeeperRemarkupURIInterface )------------------------------------- */
public function getDoorkeeperURIRef(PhutilURI $uri) {
$uri_string = phutil_string_cast($uri);
$pattern = '(https://app\\.asana\\.com/0/(\\d+)/(\\d+))';
$matches = null;
if (!preg_match($pattern, $uri_string, $matches)) {
return null;
}
$context_id = $matches[1];
$task_id = $matches[2];
return id(new DoorkeeperURIRef())
->setURI($uri)
->setApplicationType(DoorkeeperBridgeAsana::APPTYPE_ASANA)
->setApplicationDomain(DoorkeeperBridgeAsana::APPDOMAIN_ASANA)
->setObjectType(DoorkeeperBridgeAsana::OBJTYPE_TASK)
->setObjectID($task_id);
}
} }

View file

@ -1,10 +1,8 @@
<?php <?php
final class PhabricatorJIRAAuthProvider extends PhabricatorOAuth1AuthProvider { final class PhabricatorJIRAAuthProvider
extends PhabricatorOAuth1AuthProvider
public function getJIRABaseURI() { implements DoorkeeperRemarkupURIInterface {
return $this->getProviderConfig()->getProperty(self::PROPERTY_JIRA_URI);
}
public function getProviderName() { public function getProviderName() {
return pht('JIRA'); return pht('JIRA');
@ -332,4 +330,33 @@ final class PhabricatorJIRAAuthProvider extends PhabricatorOAuth1AuthProvider {
return $config->getProperty(self::PROPERTY_REPORT_COMMENT, true); return $config->getProperty(self::PROPERTY_REPORT_COMMENT, true);
} }
/* -( DoorkeeperRemarkupURIInterface )------------------------------------- */
public function getDoorkeeperURIRef(PhutilURI $uri) {
$uri_string = phutil_string_cast($uri);
$pattern = '((https?://\S+?)/browse/([A-Z]+-[1-9]\d*))';
$matches = null;
if (!preg_match($pattern, $uri_string, $matches)) {
return null;
}
$domain = $matches[1];
$issue = $matches[2];
$config = $this->getProviderConfig();
$base_uri = $config->getProperty(self::PROPERTY_JIRA_URI);
if ($domain !== rtrim($base_uri, '/')) {
return null;
}
return id(new DoorkeeperURIRef())
->setURI($uri)
->setApplicationType(DoorkeeperBridgeJIRA::APPTYPE_JIRA)
->setApplicationDomain($this->getProviderDomain())
->setObjectType(DoorkeeperBridgeJIRA::OBJTYPE_ISSUE)
->setObjectID($issue);
}
} }

View file

@ -368,6 +368,10 @@ abstract class PhabricatorDaemonManagementWorkflow
'class' => 'PhabricatorTriggerDaemon', 'class' => 'PhabricatorTriggerDaemon',
'label' => 'trigger', 'label' => 'trigger',
), ),
array(
'class' => 'PhabricatorFactDaemon',
'label' => 'fact',
),
array( array(
'class' => 'PhabricatorTaskmasterDaemon', 'class' => 'PhabricatorTaskmasterDaemon',
'label' => 'task', 'label' => 'task',

View file

@ -0,0 +1,76 @@
<?php
final class PhabricatorDashboardChartPanelType
extends PhabricatorDashboardPanelType {
public function getPanelTypeKey() {
return 'chart';
}
public function getPanelTypeName() {
return pht('Chart Panel');
}
public function getIcon() {
return 'fa-area-chart';
}
public function getPanelTypeDescription() {
return pht('Show a chart.');
}
protected function newEditEngineFields(PhabricatorDashboardPanel $panel) {
$chart_field = id(new PhabricatorTextEditField())
->setKey('chartKey')
->setLabel(pht('Chart'))
->setTransactionType(
PhabricatorDashboardChartPanelChartTransaction::TRANSACTIONTYPE)
->setValue($panel->getProperty('chartKey', ''));
return array(
$chart_field,
);
}
public function renderPanelContent(
PhabricatorUser $viewer,
PhabricatorDashboardPanel $panel,
PhabricatorDashboardPanelRenderingEngine $engine) {
$engine = id(new PhabricatorChartRenderingEngine())
->setViewer($viewer);
$chart = $engine->loadChart($panel->getProperty('chartKey'));
if (!$chart) {
return pht('no such chart!');
}
return $engine->newChartView();
}
public function adjustPanelHeader(
PhabricatorUser $viewer,
PhabricatorDashboardPanel $panel,
PhabricatorDashboardPanelRenderingEngine $engine,
PHUIHeaderView $header) {
$key = $panel->getProperty('chartKey');
$uri = PhabricatorChartRenderingEngine::getChartURI($key);
$icon = id(new PHUIIconView())
->setIcon('fa-area-chart');
$button = id(new PHUIButtonView())
->setTag('a')
->setText(pht('View Chart'))
->setIcon($icon)
->setHref($uri)
->setColor(PHUIButtonView::GREY);
$header->addActionLink($button);
return $header;
}
}

View file

@ -12,13 +12,6 @@ abstract class PhabricatorDashboardPanelType extends Phobject {
PhabricatorDashboardPanel $panel, PhabricatorDashboardPanel $panel,
PhabricatorDashboardPanelRenderingEngine $engine); PhabricatorDashboardPanelRenderingEngine $engine);
public function initializeFieldsFromRequest(
PhabricatorDashboardPanel $panel,
PhabricatorCustomFieldList $field_list,
AphrontRequest $request) {
return;
}
/** /**
* Should this panel pull content in over AJAX? * Should this panel pull content in over AJAX?
* *

View file

@ -55,33 +55,6 @@ final class PhabricatorDashboardQueryPanelType
); );
} }
public function initializeFieldsFromRequest(
PhabricatorDashboardPanel $panel,
PhabricatorCustomFieldList $field_list,
AphrontRequest $request) {
$map = array();
if (strlen($request->getStr('engine'))) {
$map['class'] = $request->getStr('engine');
}
if (strlen($request->getStr('query'))) {
$map['key'] = $request->getStr('query');
}
$full_map = array();
foreach ($map as $key => $value) {
$full_map["std:dashboard:core:{$key}"] = $value;
}
foreach ($field_list->getFields() as $field) {
$field_key = $field->getFieldKey();
if (isset($full_map[$field_key])) {
$field->setValueFromStorage($full_map[$field_key]);
}
}
}
public function renderPanelContent( public function renderPanelContent(
PhabricatorUser $viewer, PhabricatorUser $viewer,
PhabricatorDashboardPanel $panel, PhabricatorDashboardPanel $panel,

View file

@ -0,0 +1,12 @@
<?php
final class PhabricatorDashboardChartPanelChartTransaction
extends PhabricatorDashboardPanelPropertyTransaction {
const TRANSACTIONTYPE = 'chart.chartKey';
protected function getPropertyKey() {
return 'chartKey';
}
}

View file

@ -846,13 +846,9 @@ final class DifferentialTransactionEditor
$revert_phids = array(); $revert_phids = array();
} }
// See PHI574. Respect any unmentionable PHIDs which were set on the $this->addUnmentionablePHIDs($task_phids);
// Editor by the caller. $this->addUnmentionablePHIDs($rev_phids);
$unmentionable_map = $this->getUnmentionablePHIDMap(); $this->addUnmentionablePHIDs($revert_phids);
$unmentionable_map += $task_phids;
$unmentionable_map += $rev_phids;
$unmentionable_map += $revert_phids;
$this->setUnmentionablePHIDMap($unmentionable_map);
$result = array(); $result = array();
foreach ($edges as $type => $specs) { foreach ($edges as $type => $specs) {

View file

@ -26,6 +26,7 @@ final class DifferentialRevisionQuery
private $isOpen; private $isOpen;
private $createdEpochMin; private $createdEpochMin;
private $createdEpochMax; private $createdEpochMax;
private $noReviewers;
const ORDER_MODIFIED = 'order-modified'; const ORDER_MODIFIED = 'order-modified';
const ORDER_CREATED = 'order-created'; const ORDER_CREATED = 'order-created';
@ -98,7 +99,31 @@ final class DifferentialRevisionQuery
* @task config * @task config
*/ */
public function withReviewers(array $reviewer_phids) { public function withReviewers(array $reviewer_phids) {
$this->reviewers = $reviewer_phids; if ($reviewer_phids === array()) {
throw new Exception(
pht(
'Empty "withReviewers()" constraint is invalid. Provide one or '.
'more values, or remove the constraint.'));
}
$with_none = false;
foreach ($reviewer_phids as $key => $phid) {
switch ($phid) {
case DifferentialNoReviewersDatasource::FUNCTION_TOKEN:
$with_none = true;
unset($reviewer_phids[$key]);
break;
default:
break;
}
}
$this->noReviewers = $with_none;
if ($reviewer_phids) {
$this->reviewers = array_values($reviewer_phids);
}
return $this; return $this;
} }
@ -572,7 +597,7 @@ final class DifferentialRevisionQuery
if ($this->reviewers) { if ($this->reviewers) {
$joins[] = qsprintf( $joins[] = qsprintf(
$conn, $conn,
'JOIN %T reviewer ON reviewer.revisionPHID = r.phid 'LEFT JOIN %T reviewer ON reviewer.revisionPHID = r.phid
AND reviewer.reviewerStatus != %s AND reviewer.reviewerStatus != %s
AND reviewer.reviewerPHID in (%Ls)', AND reviewer.reviewerPHID in (%Ls)',
id(new DifferentialReviewer())->getTableName(), id(new DifferentialReviewer())->getTableName(),
@ -580,6 +605,15 @@ final class DifferentialRevisionQuery
$this->reviewers); $this->reviewers);
} }
if ($this->noReviewers) {
$joins[] = qsprintf(
$conn,
'LEFT JOIN %T no_reviewer ON no_reviewer.revisionPHID = r.phid
AND no_reviewer.reviewerStatus != %s',
id(new DifferentialReviewer())->getTableName(),
DifferentialReviewerStatus::STATUS_RESIGNED);
}
if ($this->draftAuthors) { if ($this->draftAuthors) {
$joins[] = qsprintf( $joins[] = qsprintf(
$conn, $conn,
@ -715,6 +749,24 @@ final class DifferentialRevisionQuery
$statuses); $statuses);
} }
$reviewer_subclauses = array();
if ($this->noReviewers) {
$reviewer_subclauses[] = qsprintf(
$conn,
'no_reviewer.reviewerPHID IS NULL');
}
if ($this->reviewers) {
$reviewer_subclauses[] = qsprintf(
$conn,
'reviewer.reviewerPHID IS NOT NULL');
}
if ($reviewer_subclauses) {
$where[] = qsprintf($conn, '%LO', $reviewer_subclauses);
}
$where[] = $this->buildWhereClauseParts($conn); $where[] = $this->buildWhereClauseParts($conn);
return $this->formatWhereClause($conn, $where); return $this->formatWhereClause($conn, $where);
@ -735,6 +787,10 @@ final class DifferentialRevisionQuery
return true; return true;
} }
if ($this->noReviewers) {
return true;
}
return parent::shouldGroupQueryResultRows(); return parent::shouldGroupQueryResultRows();
} }

View file

@ -73,7 +73,7 @@ final class DifferentialRevisionSearchEngine
->setLabel(pht('Reviewers')) ->setLabel(pht('Reviewers'))
->setKey('reviewerPHIDs') ->setKey('reviewerPHIDs')
->setAliases(array('reviewer', 'reviewers', 'reviewerPHID')) ->setAliases(array('reviewer', 'reviewers', 'reviewerPHID'))
->setDatasource(new DiffusionAuditorFunctionDatasource()) ->setDatasource(new DifferentialReviewerFunctionDatasource())
->setDescription( ->setDescription(
pht('Find revisions with specific reviewers.')), pht('Find revisions with specific reviewers.')),
id(new PhabricatorSearchDatasourceField()) id(new PhabricatorSearchDatasourceField())

View file

@ -130,7 +130,7 @@ final class DifferentialTransaction
public function getActionStrength() { public function getActionStrength() {
switch ($this->getTransactionType()) { switch ($this->getTransactionType()) {
case self::TYPE_ACTION: case self::TYPE_ACTION:
return 3; return 300;
} }
return parent::getActionStrength(); return parent::getActionStrength();

View file

@ -0,0 +1,78 @@
<?php
final class DifferentialNoReviewersDatasource
extends PhabricatorTypeaheadDatasource {
const FUNCTION_TOKEN = 'none()';
public function getBrowseTitle() {
return pht('Browse No Reviewers');
}
public function getPlaceholderText() {
return pht('Type "none"...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorDifferentialApplication';
}
public function getDatasourceFunctions() {
return array(
'none' => array(
'name' => pht('No Reviewers'),
'summary' => pht('Find results which have no reviewers.'),
'description' => pht(
"This function includes results which have no reviewers. Use a ".
"query like this to find results with no reviewers:\n\n%s\n\n".
"If you combine this function with other functions, the query will ".
"return results which match the other selectors //or// have no ".
"reviewers. For example, this query will find results which have ".
"`alincoln` as a reviewer, and will also find results which have ".
"no reviewers:".
"\n\n%s",
'> none()',
'> alincoln, none()'),
),
);
}
public function loadResults() {
$results = array(
$this->buildNoReviewersResult(),
);
return $this->filterResultsAgainstTokens($results);
}
protected function evaluateFunction($function, array $argv_list) {
$results = array();
foreach ($argv_list as $argv) {
$results[] = self::FUNCTION_TOKEN;
}
return $results;
}
public function renderFunctionTokens($function, array $argv_list) {
$results = array();
foreach ($argv_list as $argv) {
$results[] = PhabricatorTypeaheadTokenView::newFromTypeaheadResult(
$this->buildNoReviewersResult());
}
return $results;
}
private function buildNoReviewersResult() {
$name = pht('No Reviewers');
return $this->newFunctionResult()
->setName($name.' none')
->setDisplayName($name)
->setIcon('fa-ban')
->setPHID('none()')
->setUnique(true)
->addAttribute(pht('Select results with no reviewers.'));
}
}

View file

@ -0,0 +1,26 @@
<?php
final class DifferentialReviewerFunctionDatasource
extends PhabricatorTypeaheadCompositeDatasource {
public function getBrowseTitle() {
return pht('Browse Reviewers');
}
public function getPlaceholderText() {
return pht('Type a user, project, package name or function...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorDifferentialApplication';
}
public function getComponentDatasources() {
return array(
new PhabricatorProjectOrUserFunctionDatasource(),
new PhabricatorOwnersPackageFunctionDatasource(),
new DifferentialNoReviewersDatasource(),
);
}
}

View file

@ -60,6 +60,16 @@ final class DifferentialRevisionListView extends AphrontView {
$handle_phids = array(); $handle_phids = array();
foreach ($this->revisions as $key => $revision) { foreach ($this->revisions as $key => $revision) {
$reviewers = $revision->getReviewers(); $reviewers = $revision->getReviewers();
// Don't show reviewers who have resigned. The "Reviewers" constraint
// does not respect these reviewers and they largely don't count as
// reviewers.
foreach ($reviewers as $reviewer_key => $reviewer) {
if ($reviewer->isResigned()) {
unset($reviewers[$reviewer_key]);
}
}
if (count($reviewers) > $reviewer_limit) { if (count($reviewers) > $reviewer_limit) {
$reviewers = array_slice($reviewers, 0, $reviewer_limit); $reviewers = array_slice($reviewers, 0, $reviewer_limit);
$reviewer_more[$key] = true; $reviewer_more[$key] = true;

View file

@ -40,7 +40,7 @@ abstract class DifferentialRevisionActionTransaction
} }
public function getActionStrength() { public function getActionStrength() {
return 3; return 300;
} }
public function getRevisionActionOrderVector() { public function getRevisionActionOrderVector() {

View file

@ -10,6 +10,26 @@ final class DifferentialRevisionUpdateTransaction
return $object->getActiveDiffPHID(); return $object->getActiveDiffPHID();
} }
public function generateNewValue($object, $value) {
// See T13290. If we're updating the revision in response to a commit but
// the revision is already closed, return the old value so we no-op this
// transaction. We don't want to attach more than one commit-diff to a
// revision.
// Although we can try to bail out earlier so we don't generate this
// transaction in the first place, we may race another worker and end up
// trying to apply it anyway. Here, we have a lock on the object and can
// be certain about the object state.
if ($this->isCommitUpdate()) {
if ($object->isClosed()) {
return $this->generateOldValue($object);
}
}
return $value;
}
public function applyInternalEffects($object, $value) { public function applyInternalEffects($object, $value) {
$should_review = $this->shouldRequestReviewAfterUpdate($object); $should_review = $this->shouldRequestReviewAfterUpdate($object);
if ($should_review) { if ($should_review) {
@ -99,7 +119,7 @@ final class DifferentialRevisionUpdateTransaction
} }
public function getActionStrength() { public function getActionStrength() {
return 2; return 200;
} }
public function getTitle() { public function getTitle() {

View file

@ -26,7 +26,7 @@ final class DifferentialRevisionWrongBuildsTransaction
} }
public function getActionStrength() { public function getActionStrength() {
return 4; return 400;
} }
public function getTitle() { public function getTitle() {

View file

@ -22,7 +22,7 @@ final class DifferentialRevisionWrongStateTransaction
} }
public function getActionStrength() { public function getActionStrength() {
return 4; return 400;
} }
public function getTitle() { public function getTitle() {

View file

@ -40,6 +40,7 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
new DiffusionCommitRemarkupRule(), new DiffusionCommitRemarkupRule(),
new DiffusionRepositoryRemarkupRule(), new DiffusionRepositoryRemarkupRule(),
new DiffusionRepositoryByIDRemarkupRule(), new DiffusionRepositoryByIDRemarkupRule(),
new DiffusionSourceLinkRemarkupRule(),
); );
} }

View file

@ -0,0 +1,84 @@
<?php
final class DiffusionSourceHyperlinkEngineExtension
extends PhabricatorRemarkupHyperlinkEngineExtension {
const LINKENGINEKEY = 'diffusion-src';
public function processHyperlinks(array $hyperlinks) {
$engine = $this->getEngine();
$viewer = $engine->getConfig('viewer');
if (!$viewer) {
return;
}
$hyperlinks = $this->getSelfLinks($hyperlinks);
$links = array();
foreach ($hyperlinks as $link) {
$uri = $link->getURI();
$uri = new PhutilURI($uri);
$path = $uri->getPath();
$pattern =
'(^'.
'/(?:diffusion|source)'.
'/(?P<identifier>[^/]+)'.
'/browse'.
'/(?P<blob>.*)'.
'\z)';
$matches = null;
if (!preg_match($pattern, $path, $matches)) {
continue;
}
$links[] = array(
'ref' => $link,
'identifier' => $matches['identifier'],
'blob' => $matches['blob'],
);
}
if (!$links) {
return;
}
$identifiers = ipull($links, 'identifier');
$query = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->withIdentifiers($identifiers);
$query->execute();
$repository_map = $query->getIdentifierMap();
foreach ($links as $link) {
$identifier = $link['identifier'];
$repository = idx($repository_map, $identifier);
if (!$repository) {
continue;
}
$ref = $link['ref'];
$uri = $ref->getURI();
$tag = id(new DiffusionSourceLinkView())
->setViewer($viewer)
->setRepository($repository)
->setURI($uri)
->setBlob($link['blob']);
if (!$ref->isEmbed()) {
$tag->setText($uri);
}
$ref->setResult($tag);
}
}
}

View file

@ -0,0 +1,221 @@
<?php
final class DiffusionSourceLinkRemarkupRule
extends PhutilRemarkupRule {
const KEY_SOURCELINKS = 'diffusion.links';
public function getPriority() {
return 200.0;
}
public function apply($text) {
return preg_replace_callback(
'@{(?:src|source)\b((?:[^}\\\\]+|\\\\.)*)}@m',
array($this, 'markupSourceLink'),
$text);
}
public function markupSourceLink(array $matches) {
$engine = $this->getEngine();
$text_mode = $engine->isTextMode();
$mail_mode = $engine->isHTMLMailMode();
if (!$this->isFlatText($matches[0]) || $text_mode || $mail_mode) {
// We could do better than this in text mode and mail mode, but focus
// on web mode first.
return $matches[0];
}
$metadata_key = self::KEY_SOURCELINKS;
$metadata = $engine->getTextMetadata($metadata_key, array());
$token = $engine->storeText($matches[0]);
$metadata[] = array(
'token' => $token,
'raw' => $matches[0],
'input' => $matches[1],
);
$engine->setTextMetadata($metadata_key, $metadata);
return $token;
}
public function didMarkupText() {
$engine = $this->getEngine();
$metadata_key = self::KEY_SOURCELINKS;
$metadata = $engine->getTextMetadata($metadata_key, array());
if (!$metadata) {
return;
}
$viewer = $engine->getConfig('viewer');
if (!$viewer) {
return;
}
$defaults = array(
'repository' => null,
'line' => null,
'commit' => null,
'ref' => null,
);
$tags = array();
foreach ($metadata as $ref) {
$token = $ref['token'];
$raw = $ref['raw'];
$input = $ref['input'];
$pattern =
'(^'.
'[\s,]*'.
'(?:"(?P<quotedpath>(?:[^\\\\"]+|\\.)+)"|(?P<rawpath>[^\s,]+))'.
'[\s,]*'.
'(?P<options>.*)'.
'\z)';
$matches = null;
if (!preg_match($pattern, $input, $matches)) {
$hint_text = pht(
'Missing path, expected "{src path ...}" in: %s',
$raw);
$hint = $this->newSyntaxHint($hint_text);
$engine->overwriteStoredText($token, $hint);
continue;
}
$path = idx($matches, 'rawpath');
if (!strlen($path)) {
$path = idx($matches, 'quotedpath');
$path = stripcslashes($path);
}
$parts = explode(':', $path, 2);
if (count($parts) == 2) {
$repository = nonempty($parts[0], null);
$path = $parts[1];
} else {
$repository = null;
$path = $parts[0];
}
$options = $matches['options'];
$parser = new PhutilSimpleOptions();
$options = $parser->parse($options) + $defaults;
foreach ($options as $key => $value) {
if (!array_key_exists($key, $defaults)) {
$hint_text = pht(
'Unknown option "%s" in: %s',
$key,
$raw);
$hint = $this->newSyntaxHint($hint_text);
$engine->overwriteStoredText($token, $hint);
continue 2;
}
}
if ($options['repository'] !== null) {
$repository = $options['repository'];
}
if ($repository === null) {
$hint_text = pht(
'Missing repository, expected "{src repository:path ...}" '.
'or "{src path repository=...}" in: %s',
$raw);
$hint = $this->newSyntaxHint($hint_text);
$engine->overwriteStoredText($token, $hint);
continue;
}
$tags[] = array(
'token' => $token,
'raw' => $raw,
'identifier' => $repository,
'path' => $path,
'options' => $options,
);
}
if (!$tags) {
return;
}
$query = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->withIdentifiers(ipull($tags, 'identifier'));
$query->execute();
$repository_map = $query->getIdentifierMap();
foreach ($tags as $tag) {
$token = $tag['token'];
$identifier = $tag['identifier'];
$repository = idx($repository_map, $identifier);
if (!$repository) {
// For now, just bail out here. Ideally, we should distingiush between
// restricted and invalid repositories.
continue;
}
$drequest = DiffusionRequest::newFromDictionary(
array(
'user' => $viewer,
'repository' => $repository,
));
$options = $tag['options'];
$line = $options['line'];
$commit = $options['commit'];
$ref_name = $options['ref'];
$link_uri = $drequest->generateURI(
array(
'action' => 'browse',
'path' => $tag['path'],
'commit' => $commit,
'line' => $line,
'branch' => $ref_name,
));
$view = id(new DiffusionSourceLinkView())
->setRepository($repository)
->setPath($tag['path'])
->setURI($link_uri);
if ($line !== null) {
$view->setLine($line);
}
if ($commit !== null) {
$view->setCommit($commit);
}
if ($ref_name !== null) {
$view->setRefName($ref_name);
}
$engine->overwriteStoredText($token, $view);
}
}
private function newSyntaxHint($text) {
return id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE)
->setColor('red')
->setIcon('fa-exclamation-triangle')
->setName($text);
}
}

View file

@ -2,10 +2,6 @@
final class DiffusionGitRequest extends DiffusionRequest { final class DiffusionGitRequest extends DiffusionRequest {
public function supportsBranches() {
return true;
}
protected function isStableCommit($symbol) { protected function isStableCommit($symbol) {
return preg_match('/^[a-f0-9]{40}\z/', $symbol); return preg_match('/^[a-f0-9]{40}\z/', $symbol);
} }

View file

@ -2,10 +2,6 @@
final class DiffusionMercurialRequest extends DiffusionRequest { final class DiffusionMercurialRequest extends DiffusionRequest {
public function supportsBranches() {
return true;
}
protected function isStableCommit($symbol) { protected function isStableCommit($symbol) {
return preg_match('/^[a-f0-9]{40}\z/', $symbol); return preg_match('/^[a-f0-9]{40}\z/', $symbol);
} }

View file

@ -28,7 +28,10 @@ abstract class DiffusionRequest extends Phobject {
private $branchObject = false; private $branchObject = false;
private $refAlternatives; private $refAlternatives;
abstract public function supportsBranches(); final public function supportsBranches() {
return $this->getRepository()->supportsRefs();
}
abstract protected function isStableCommit($symbol); abstract protected function isStableCommit($symbol);
protected function didInitialize() { protected function didInitialize() {

View file

@ -2,10 +2,6 @@
final class DiffusionSvnRequest extends DiffusionRequest { final class DiffusionSvnRequest extends DiffusionRequest {
public function supportsBranches() {
return false;
}
protected function isStableCommit($symbol) { protected function isStableCommit($symbol) {
return preg_match('/^[1-9]\d*\z/', $symbol); return preg_match('/^[1-9]\d*\z/', $symbol);
} }

View file

@ -75,7 +75,7 @@ final class DiffusionReadmeView extends DiffusionView {
$engine = $markup_object->newMarkupEngine($markup_field); $engine = $markup_object->newMarkupEngine($markup_field);
$readme_content = $content; $readme_content = $content;
$class = null; $class = 'ml';
break; break;
case 'rainbow': case 'rainbow':
$content = id(new PhutilRainbowSyntaxHighlighter()) $content = id(new PhutilRainbowSyntaxHighlighter())
@ -93,10 +93,12 @@ final class DiffusionReadmeView extends DiffusionView {
break; break;
} }
$readme_content = phutil_tag_div($class, $readme_content); $readme_content = phutil_tag(
$document = id(new PHUIDocumentView()) 'div',
->setFluid(true) array(
->appendChild($readme_content); 'class' => $class,
),
$readme_content);
$header = id(new PHUIHeaderView()) $header = id(new PHUIHeaderView())
->setHeader($readme_name) ->setHeader($readme_name)
@ -106,7 +108,7 @@ final class DiffusionReadmeView extends DiffusionView {
->setHeader($header) ->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addClass('diffusion-mobile-view') ->addClass('diffusion-mobile-view')
->appendChild($document) ->appendChild($readme_content)
->addClass('diffusion-readme-view'); ->addClass('diffusion-readme-view');
} }

View file

@ -0,0 +1,208 @@
<?php
final class DiffusionSourceLinkView
extends AphrontView {
private $repository;
private $text;
private $uri;
private $blob;
private $blobMap;
private $refName;
private $path;
private $line;
private $commit;
public function setRepository($repository) {
$this->repository = $repository;
$this->blobMap = null;
return $this;
}
public function getRepository() {
return $this->repository;
}
public function setText($text) {
$this->text = $text;
return $this;
}
public function getText() {
return $this->text;
}
public function setURI($uri) {
$this->uri = $uri;
return $this;
}
public function getURI() {
return $this->uri;
}
public function setBlob($blob) {
$this->blob = $blob;
$this->blobMap = null;
return $this;
}
public function getBlob() {
return $this->blob;
}
public function setRefName($ref_name) {
$this->refName = $ref_name;
return $this;
}
public function getRefName() {
return $this->refName;
}
public function setPath($path) {
$this->path = $path;
return $this;
}
public function getPath() {
return $this->path;
}
public function setCommit($commit) {
$this->commit = $commit;
return $this;
}
public function getCommit() {
return $this->commit;
}
public function setLine($line) {
$this->line = $line;
return $this;
}
public function getLine() {
return $this->line;
}
public function getDisplayPath() {
if ($this->path !== null) {
return $this->path;
}
return $this->getBlobPath();
}
public function getDisplayRefName() {
if ($this->refName !== null) {
return $this->refName;
}
return $this->getBlobRefName();
}
public function getDisplayCommit() {
if ($this->commit !== null) {
return $this->commit;
}
return $this->getBlobCommit();
}
public function getDisplayLine() {
if ($this->line !== null) {
return $this->line;
}
return $this->getBlobLine();
}
private function getBlobPath() {
return idx($this->getBlobMap(), 'path');
}
private function getBlobRefName() {
return idx($this->getBlobMap(), 'branch');
}
private function getBlobLine() {
return idx($this->getBlobMap(), 'line');
}
private function getBlobCommit() {
return idx($this->getBlobMap(), 'commit');
}
private function getBlobMap() {
if ($this->blobMap === null) {
$repository = $this->getRepository();
$blob = $this->blob;
if ($repository && ($blob !== null)) {
$map = DiffusionRequest::parseRequestBlob(
$blob,
$repository->supportsRefs());
} else {
$map = array();
}
$this->blobMap = $map;
}
return $this->blobMap;
}
public function render() {
$repository = $this->getRepository();
$uri = $this->getURI();
$color = 'blue';
$icon = 'fa-file-text-o';
$text = $this->getText();
if (!strlen($text)) {
$path = $this->getDisplayPath();
$line = $this->getDisplayLine();
if ($line !== null) {
$path = pht('%s:%s', $path, $line);
}
if ($repository) {
$path = pht('%s %s', $repository->getMonogram(), $path);
}
if ($repository && $repository->supportsRefs()) {
$default_ref = $repository->getDefaultBranch();
} else {
$default_ref = null;
}
$ref_name = $this->getDisplayRefName();
if ($ref_name === $default_ref) {
$ref_name = null;
}
$commit = $this->getDisplayCommit();
if ($ref_name !== null && $commit !== null) {
$text = pht('%s (on %s at %s)', $path, $ref_name, $commit);
} else if ($ref_name !== null) {
$text = pht('%s (on %s)', $path, $ref_name);
} else if ($commit !== null) {
$text = pht('%s (at %s)', $path, $commit);
} else {
$text = $path;
}
}
return id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE)
->setColor($color)
->setIcon($icon)
->setHref($uri)
->setName($text);
}
}

View file

@ -139,10 +139,7 @@ final class DiffusionUpdateObjectAfterCommitWorker
->setContentSource($content_source) ->setContentSource($content_source)
->setContinueOnNoEffect(true) ->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true) ->setContinueOnMissingFields(true)
->setUnmentionablePHIDMap( ->addUnmentionablePHIDs(array($commit_phid));
array(
$commit_phid => $commit_phid,
));
$editor->applyTransactions($task, $xactions); $editor->applyTransactions($task, $xactions);
} }

View file

@ -22,13 +22,6 @@ final class PhabricatorDoorkeeperApplication extends PhabricatorApplication {
return pht('Connect to Other Software'); return pht('Connect to Other Software');
} }
public function getRemarkupRules() {
return array(
new DoorkeeperAsanaRemarkupRule(),
new DoorkeeperJIRARemarkupRule(),
);
}
public function getRoutes() { public function getRoutes() {
return array( return array(
'/doorkeeper/' => array( '/doorkeeper/' => array(

View file

@ -5,6 +5,16 @@ abstract class DoorkeeperBridge extends Phobject {
private $viewer; private $viewer;
private $context = array(); private $context = array();
private $throwOnMissingLink; private $throwOnMissingLink;
private $timeout;
public function setTimeout($timeout) {
$this->timeout = $timeout;
return $this;
}
public function getTimeout() {
return $this->timeout;
}
public function setThrowOnMissingLink($throw_on_missing_link) { public function setThrowOnMissingLink($throw_on_missing_link) {
$this->throwOnMissingLink = $throw_on_missing_link; $this->throwOnMissingLink = $throw_on_missing_link;

View file

@ -62,6 +62,11 @@ final class DoorkeeperBridgeAsana extends DoorkeeperBridge {
$template = id(new PhutilAsanaFuture()) $template = id(new PhutilAsanaFuture())
->setAccessToken($token); ->setAccessToken($token);
$timeout = $this->getTimeout();
if ($timeout !== null) {
$template->setTimeout($timeout);
}
$futures = array(); $futures = array();
foreach ($id_map as $key => $id) { foreach ($id_map as $key => $id) {
$futures[$key] = id(clone $template) $futures[$key] = id(clone $template)

View file

@ -47,12 +47,20 @@ final class DoorkeeperBridgeJIRA extends DoorkeeperBridge {
// (by querying all instances). For now, just query the one instance. // (by querying all instances). For now, just query the one instance.
$account = head($accounts); $account = head($accounts);
$timeout = $this->getTimeout();
$futures = array(); $futures = array();
foreach ($id_map as $key => $id) { foreach ($id_map as $key => $id) {
$futures[$key] = $provider->newJIRAFuture( $future = $provider->newJIRAFuture(
$account, $account,
'rest/api/2/issue/'.phutil_escape_uri($id), 'rest/api/2/issue/'.phutil_escape_uri($id),
'GET'); 'GET');
if ($timeout !== null) {
$future->setTimeout($timeout);
}
$futures[$key] = $future;
} }
$results = array(); $results = array();

View file

@ -26,6 +26,7 @@ final class DoorkeeperTagsController extends PhabricatorController {
$refs = id(new DoorkeeperImportEngine()) $refs = id(new DoorkeeperImportEngine())
->setViewer($viewer) ->setViewer($viewer)
->setRefs($refs) ->setRefs($refs)
->setTimeout(15)
->execute(); ->execute();
$results = array(); $results = array();

View file

@ -8,6 +8,7 @@ final class DoorkeeperImportEngine extends Phobject {
private $localOnly; private $localOnly;
private $throwOnMissingLink; private $throwOnMissingLink;
private $context = array(); private $context = array();
private $timeout;
public function setViewer(PhabricatorUser $viewer) { public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer; $this->viewer = $viewer;
@ -43,6 +44,15 @@ final class DoorkeeperImportEngine extends Phobject {
return $this; return $this;
} }
public function setTimeout($timeout) {
$this->timeout = $timeout;
return $this;
}
public function getTimeout() {
return $this->timeout;
}
/** /**
* Configure behavior if remote refs can not be retrieved because an * Configure behavior if remote refs can not be retrieved because an
* authentication link is missing. * authentication link is missing.
@ -98,10 +108,16 @@ final class DoorkeeperImportEngine extends Phobject {
->setFilterMethod('isEnabled') ->setFilterMethod('isEnabled')
->execute(); ->execute();
$timeout = $this->getTimeout();
foreach ($bridges as $key => $bridge) { foreach ($bridges as $key => $bridge) {
$bridge->setViewer($viewer); $bridge
$bridge->setThrowOnMissingLink($this->throwOnMissingLink); ->setViewer($viewer)
$bridge->setContext($this->context); ->setThrowOnMissingLink($this->throwOnMissingLink)
->setContext($this->context);
if ($timeout !== null) {
$bridge->setTimeout($timeout);
}
} }
$working_set = $refs; $working_set = $refs;

View file

@ -0,0 +1,91 @@
<?php
final class DoorkeeperURIRef extends Phobject {
private $uri;
private $applicationType;
private $applicationDomain;
private $objectType;
private $objectID;
private $text;
private $displayMode = self::DISPLAY_FULL;
const DISPLAY_FULL = 'full';
const DISPLAY_SHORT = 'short';
public function setURI(PhutilURI $uri) {
$this->uri = $uri;
return $this;
}
public function getURI() {
return $this->uri;
}
public function setApplicationType($application_type) {
$this->applicationType = $application_type;
return $this;
}
public function getApplicationType() {
return $this->applicationType;
}
public function setApplicationDomain($application_domain) {
$this->applicationDomain = $application_domain;
return $this;
}
public function getApplicationDomain() {
return $this->applicationDomain;
}
public function setObjectType($object_type) {
$this->objectType = $object_type;
return $this;
}
public function getObjectType() {
return $this->objectType;
}
public function setObjectID($object_id) {
$this->objectID = $object_id;
return $this;
}
public function getObjectID() {
return $this->objectID;
}
public function setText($text) {
$this->text = $text;
return $this;
}
public function getText() {
return $this->text;
}
public function setDisplayMode($display_mode) {
$options = array(
self::DISPLAY_FULL => true,
self::DISPLAY_SHORT => true,
);
if (!isset($options[$display_mode])) {
throw new Exception(
pht(
'DoorkeeperURIRef display mode "%s" is unknown.',
$display_mode));
}
$this->displayMode = $display_mode;
return $this;
}
public function getDisplayMode() {
return $this->displayMode;
}
}

View file

@ -0,0 +1,92 @@
<?php
final class DoorkeeperHyperlinkEngineExtension
extends PhabricatorRemarkupHyperlinkEngineExtension {
const LINKENGINEKEY = 'doorkeeper';
public function processHyperlinks(array $hyperlinks) {
$engine = $this->getEngine();
$viewer = $engine->getConfig('viewer');
if (!$viewer) {
return;
}
$configs = id(new PhabricatorAuthProviderConfigQuery())
->setViewer($viewer)
->withIsEnabled(true)
->execute();
$providers = array();
foreach ($configs as $key => $config) {
$provider = $config->getProvider();
if (($provider instanceof DoorkeeperRemarkupURIInterface)) {
$providers[] = $provider;
}
}
if (!$providers) {
return;
}
$refs = array();
foreach ($hyperlinks as $hyperlink) {
$uri = $hyperlink->getURI();
$uri = new PhutilURI($uri);
foreach ($providers as $provider) {
$ref = $provider->getDoorkeeperURIRef($uri);
if (($ref !== null) && !($ref instanceof DoorkeeperURIRef)) {
throw new Exception(
pht(
'Expected "getDoorkeeperURIRef()" to return "null" or an '.
'object of type "DoorkeeperURIRef", but got %s from provider '.
'"%s".',
phutil_describe_type($ref),
get_class($provider)));
}
if ($ref === null) {
continue;
}
$tag_id = celerity_generate_unique_node_id();
$href = phutil_string_cast($ref->getURI());
$refs[] = array(
'id' => $tag_id,
'href' => $href,
'ref' => array(
$ref->getApplicationType(),
$ref->getApplicationDomain(),
$ref->getObjectType(),
$ref->getObjectID(),
),
'view' => $ref->getDisplayMode(),
);
$text = $ref->getText();
if ($text === null) {
$text = $href;
}
$view = id(new PHUITagView())
->setID($tag_id)
->setName($text)
->setHref($href)
->setType(PHUITagView::TYPE_OBJECT)
->setExternal(true);
$hyperlink->setResult($view);
break;
}
}
if ($refs) {
Javelin::initBehavior('doorkeeper-tag', array('tags' => $refs));
}
}
}

View file

@ -0,0 +1,7 @@
<?php
interface DoorkeeperRemarkupURIInterface {
public function getDoorkeeperURIRef(PhutilURI $uri);
}

View file

@ -1,31 +0,0 @@
<?php
final class DoorkeeperAsanaRemarkupRule
extends DoorkeeperRemarkupRule {
public function apply($text) {
return preg_replace_callback(
'@https://app\\.asana\\.com/0/(\\d+)/(\\d+)@',
array($this, 'markupAsanaLink'),
$text);
}
public function markupAsanaLink($matches) {
return $this->addDoorkeeperTag(
array(
'href' => $matches[0],
'tag' => array(
'ref' => array(
DoorkeeperBridgeAsana::APPTYPE_ASANA,
DoorkeeperBridgeAsana::APPDOMAIN_ASANA,
DoorkeeperBridgeAsana::OBJTYPE_TASK,
$matches[2],
),
'extra' => array(
'asana.context' => $matches[1],
),
),
));
}
}

View file

@ -1,44 +0,0 @@
<?php
final class DoorkeeperJIRARemarkupRule
extends DoorkeeperRemarkupRule {
public function apply($text) {
return preg_replace_callback(
'@(https?://\S+?)/browse/([A-Z]+-[1-9]\d*)@',
array($this, 'markupJIRALink'),
$text);
}
public function markupJIRALink($matches) {
$match_domain = $matches[1];
$match_issue = $matches[2];
// TODO: When we support multiple instances, deal with them here.
$provider = PhabricatorJIRAAuthProvider::getJIRAProvider();
if (!$provider) {
return $matches[0];
}
$jira_base = $provider->getJIRABaseURI();
if ($match_domain != rtrim($jira_base, '/')) {
return $matches[0];
}
return $this->addDoorkeeperTag(
array(
'href' => $matches[0],
'tag' => array(
'ref' => array(
DoorkeeperBridgeJIRA::APPTYPE_JIRA,
$provider->getProviderDomain(),
DoorkeeperBridgeJIRA::OBJTYPE_ISSUE,
$match_issue,
),
),
));
}
}

View file

@ -1,103 +0,0 @@
<?php
abstract class DoorkeeperRemarkupRule extends PhutilRemarkupRule {
const KEY_TAGS = 'doorkeeper.tags';
const VIEW_FULL = 'full';
const VIEW_SHORT = 'short';
public function getPriority() {
return 350.0;
}
protected function addDoorkeeperTag(array $spec) {
PhutilTypeSpec::checkMap(
$spec,
array(
'href' => 'string',
'tag' => 'map<string, wild>',
'name' => 'optional string',
'view' => 'optional string',
));
$spec = $spec + array(
'view' => self::VIEW_FULL,
);
$views = array(
self::VIEW_FULL,
self::VIEW_SHORT,
);
$views = array_fuse($views);
if (!isset($views[$spec['view']])) {
throw new Exception(
pht(
'Unsupported Doorkeeper tag view mode "%s". Supported modes are: %s.',
$spec['view'],
implode(', ', $views)));
}
$key = self::KEY_TAGS;
$engine = $this->getEngine();
$token = $engine->storeText(get_class($this));
$tags = $engine->getTextMetadata($key, array());
$tags[] = array(
'token' => $token,
) + $spec + array(
'extra' => array(),
);
$engine->setTextMetadata($key, $tags);
return $token;
}
public function didMarkupText() {
$key = self::KEY_TAGS;
$engine = $this->getEngine();
$tags = $engine->getTextMetadata($key, array());
if (!$tags) {
return;
}
$refs = array();
foreach ($tags as $spec) {
$href = $spec['href'];
$name = idx($spec, 'name', $href);
$this->assertFlatText($href);
$this->assertFlatText($name);
if ($this->getEngine()->isTextMode()) {
$view = "{$name} <{$href}>";
} else {
$tag_id = celerity_generate_unique_node_id();
$refs[] = array(
'id' => $tag_id,
'view' => $spec['view'],
) + $spec['tag'];
$view = id(new PHUITagView())
->setID($tag_id)
->setName($name)
->setHref($href)
->setType(PHUITagView::TYPE_OBJECT)
->setExternal(true);
}
$engine->overwriteStoredText($spec['token'], $view);
}
if ($refs) {
Javelin::initBehavior('doorkeeper-tag', array('tags' => $refs));
}
$engine->setTextMetadata($key, array());
}
}

View file

@ -151,13 +151,15 @@ final class DrydockManagementLeaseWorkflow
while (!$is_active) { while (!$is_active) {
$lease->reload(); $lease->reload();
$pager = id(new AphrontCursorPagerView())
->setBeforeID($log_cursor);
// While we're waiting, show the user any logs which the daemons have // While we're waiting, show the user any logs which the daemons have
// generated to give them some clue about what's going on. // generated to give them some clue about what's going on.
$logs = id(new DrydockLogQuery()) $logs = id(new DrydockLogQuery())
->setViewer($viewer) ->setViewer($viewer)
->withLeasePHIDs(array($lease->getPHID())) ->withLeasePHIDs(array($lease->getPHID()))
->setBeforeID($log_cursor) ->executeWithCursorPager($pager);
->execute();
if ($logs) { if ($logs) {
$logs = mpull($logs, null, 'getID'); $logs = mpull($logs, null, 'getID');
ksort($logs); ksort($logs);

View file

@ -2,11 +2,17 @@
final class DrydockLogQuery extends DrydockQuery { final class DrydockLogQuery extends DrydockQuery {
private $ids;
private $blueprintPHIDs; private $blueprintPHIDs;
private $resourcePHIDs; private $resourcePHIDs;
private $leasePHIDs; private $leasePHIDs;
private $operationPHIDs; private $operationPHIDs;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withBlueprintPHIDs(array $phids) { public function withBlueprintPHIDs(array $phids) {
$this->blueprintPHIDs = $phids; $this->blueprintPHIDs = $phids;
return $this; return $this;
@ -126,6 +132,13 @@ final class DrydockLogQuery extends DrydockQuery {
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn); $where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'id IN (%Ls)',
$this->ids);
}
if ($this->blueprintPHIDs !== null) { if ($this->blueprintPHIDs !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,

View file

@ -30,7 +30,9 @@ final class PhabricatorFactApplication extends PhabricatorApplication {
return array( return array(
'/fact/' => array( '/fact/' => array(
'' => 'PhabricatorFactHomeController', '' => 'PhabricatorFactHomeController',
'(?<mode>chart|draw)/' => 'PhabricatorFactChartController', 'chart/' => 'PhabricatorFactChartController',
'chart/(?P<chartKey>[^/]+)/(?:(?P<mode>draw)/)?' =>
'PhabricatorFactChartController',
'object/(?<phid>[^/]+)/' => 'PhabricatorFactObjectController', 'object/(?<phid>[^/]+)/' => 'PhabricatorFactObjectController',
), ),
); );

View file

@ -0,0 +1,82 @@
<?php
final class PhabricatorAccumulateChartFunction
extends PhabricatorChartFunction {
const FUNCTIONKEY = 'accumulate';
protected function newArguments() {
return array(
$this->newArgument()
->setName('x')
->setType('function'),
);
}
public function getDomain() {
return $this->getArgument('x')->getDomain();
}
public function newInputValues(PhabricatorChartDataQuery $query) {
return $this->getArgument('x')->newInputValues($query);
}
public function evaluateFunction(array $xv) {
// First, we're going to accumulate the underlying function. Then
// we'll map the inputs through the accumulation.
$datasource = $this->getArgument('x');
// Use an unconstrained query to pull all the data from the underlying
// source. We need to accumulate data since the beginning of time to
// figure out the right Y-intercept -- otherwise, we'll always start at
// "0" wherever our domain begins.
$empty_query = new PhabricatorChartDataQuery();
$datasource_xv = $datasource->newInputValues($empty_query);
if (!$datasource_xv) {
// When the datasource has no datapoints, we can't evaluate the function
// anywhere.
return array_fill(0, count($xv), null);
}
$yv = $datasource->evaluateFunction($datasource_xv);
$map = array_combine($datasource_xv, $yv);
$accumulator = 0;
foreach ($map as $x => $y) {
$accumulator += $y;
$map[$x] = $accumulator;
}
// The value of "accumulate(x)" is the largest datapoint in the map which
// is no larger than "x".
$map_x = array_keys($map);
$idx = -1;
$max = count($map_x) - 1;
$yv = array();
$value = 0;
foreach ($xv as $x) {
// While the next "x" we need to evaluate the function at lies to the
// right of the next datapoint, move the current datapoint forward until
// we're at the rightmost datapoint which is not larger than "x".
while ($idx < $max) {
if ($map_x[$idx + 1] > $x) {
break;
}
$idx++;
$value = $map[$map_x[$idx]];
}
$yv[] = $value;
}
return $yv;
}
}

View file

@ -34,4 +34,48 @@ final class PhabricatorChartDataQuery
return $this->limit; return $this->limit;
} }
public function selectInputValues(array $xv) {
$result = array();
$x_min = $this->getMinimumValue();
$x_max = $this->getMaximumValue();
$limit = $this->getLimit();
if ($x_min !== null) {
foreach ($xv as $key => $x) {
if ($x < $x_min) {
unset($xv[$key]);
}
}
}
if ($x_max !== null) {
foreach ($xv as $key => $x) {
if ($x > $x_max) {
unset($xv[$key]);
}
}
}
// If we have too many data points, throw away some of the data.
// TODO: This doesn't work especially well right now.
if ($limit !== null) {
$count = count($xv);
if ($count > $limit) {
$ii = 0;
$every = ceil($count / $limit);
foreach ($xv as $key => $x) {
$ii++;
if (($ii % $every) && ($ii != $count)) {
unset($xv[$key]);
}
}
}
}
return array_values($xv);
}
} }

View file

@ -0,0 +1,78 @@
<?php
abstract class PhabricatorChartDataset
extends Phobject {
private $functions;
final public function getDatasetTypeKey() {
return $this->getPhobjectClassConstant('DATASETKEY', 32);
}
final public function getFunctions() {
return $this->functions;
}
final public function setFunctions(array $functions) {
assert_instances_of($functions, 'PhabricatorComposeChartFunction');
$this->functions = $functions;
return $this;
}
final public static function getAllDatasetTypes() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getDatasetTypeKey')
->execute();
}
final public static function newFromDictionary(array $map) {
PhutilTypeSpec::checkMap(
$map,
array(
'type' => 'string',
'functions' => 'list<wild>',
));
$types = self::getAllDatasetTypes();
$dataset_type = $map['type'];
if (!isset($types[$dataset_type])) {
throw new Exception(
pht(
'Trying to construct a dataset of type "%s", but this type is '.
'unknown. Supported types are: %s.',
$dataset_type,
implode(', ', array_keys($types))));
}
$dataset = id(clone $types[$dataset_type]);
$functions = array();
foreach ($map['functions'] as $map) {
$functions[] = PhabricatorChartFunction::newFromDictionary($map);
}
$dataset->setFunctions($functions);
return $dataset;
}
final public function toDictionary() {
return array(
'type' => $this->getDatasetTypeKey(),
'functions' => mpull($this->getFunctions(), 'toDictionary'),
);
}
final public function getChartDisplayData(
PhabricatorChartDataQuery $data_query) {
return $this->newChartDisplayData($data_query);
}
abstract protected function newChartDisplayData(
PhabricatorChartDataQuery $data_query);
}

View file

@ -0,0 +1,27 @@
<?php
final class PhabricatorChartDisplayData
extends Phobject {
private $wireData;
private $range;
public function setWireData(array $wire_data) {
$this->wireData = $wire_data;
return $this;
}
public function getWireData() {
return $this->wireData;
}
public function setRange(PhabricatorChartInterval $range) {
$this->range = $range;
return $this;
}
public function getRange() {
return $this->range;
}
}

View file

@ -3,11 +3,8 @@
abstract class PhabricatorChartFunction abstract class PhabricatorChartFunction
extends Phobject { extends Phobject {
private $xAxis;
private $yAxis;
private $argumentParser; private $argumentParser;
private $sourceFunction; private $functionLabel;
final public function getFunctionKey() { final public function getFunctionKey() {
return $this->getPhobjectClassConstant('FUNCTIONKEY', 32); return $this->getPhobjectClassConstant('FUNCTIONKEY', 32);
@ -44,15 +41,124 @@ abstract class PhabricatorChartFunction
$parser->setHaveAllArguments(true); $parser->setHaveAllArguments(true);
$parser->parseArguments(); $parser->parseArguments();
$source_argument = $parser->getSourceFunctionArgument(); return $this;
if ($source_argument) {
$source_function = $this->getArgument($source_argument->getName());
$this->setSourceFunction($source_function);
} }
public function setFunctionLabel(PhabricatorChartFunctionLabel $label) {
$this->functionLabel = $label;
return $this; return $this;
} }
public function getFunctionLabel() {
if (!$this->functionLabel) {
$this->functionLabel = id(new PhabricatorChartFunctionLabel())
->setName(pht('Unlabeled Function'))
->setColor('rgba(255, 0, 0, 1)')
->setFillColor('rgba(255, 0, 0, 0.15)');
}
return $this->functionLabel;
}
final public static function newFromDictionary(array $map) {
PhutilTypeSpec::checkMap(
$map,
array(
'function' => 'string',
'arguments' => 'list<wild>',
));
$functions = self::getAllFunctions();
$function_name = $map['function'];
if (!isset($functions[$function_name])) {
throw new Exception(
pht(
'Attempting to build function "%s" from dictionary, but that '.
'function is unknown. Known functions are: %s.',
$function_name,
implode(', ', array_keys($functions))));
}
$function = id(clone $functions[$function_name])
->setArguments($map['arguments']);
return $function;
}
public function toDictionary() {
return array(
'function' => $this->getFunctionKey(),
'arguments' => $this->getArgumentParser()->getRawArguments(),
);
}
public function getSubfunctions() {
$result = array();
$result[] = $this;
foreach ($this->getFunctionArguments() as $argument) {
foreach ($argument->getSubfunctions() as $subfunction) {
$result[] = $subfunction;
}
}
return $result;
}
public function getFunctionArguments() {
$results = array();
$parser = $this->getArgumentParser();
foreach ($parser->getAllArguments() as $argument) {
if ($argument->getType() !== 'function') {
continue;
}
$name = $argument->getName();
$value = $this->getArgument($name);
if (!is_array($value)) {
$results[] = $value;
} else {
foreach ($value as $arg_value) {
$results[] = $arg_value;
}
}
}
return $results;
}
public function newDatapoints(PhabricatorChartDataQuery $query) {
$xv = $this->newInputValues($query);
if ($xv === null) {
$xv = $this->newDefaultInputValues($query);
}
$xv = $query->selectInputValues($xv);
$n = count($xv);
$yv = $this->evaluateFunction($xv);
$points = array();
for ($ii = 0; $ii < $n; $ii++) {
$y = $yv[$ii];
if ($y === null) {
continue;
}
$points[] = array(
'x' => $xv[$ii],
'y' => $y,
);
}
return $points;
}
abstract protected function newArguments(); abstract protected function newArguments();
final protected function newArgument() { final protected function newArgument() {
@ -73,96 +179,26 @@ abstract class PhabricatorChartFunction
return $this->argumentParser; return $this->argumentParser;
} }
abstract public function evaluateFunction(array $xv);
public function getDomain() {
return null;
}
public function newInputValues(PhabricatorChartDataQuery $query) {
return null;
}
public function loadData() { public function loadData() {
return; return;
} }
protected function setSourceFunction(PhabricatorChartFunction $source) { protected function newDefaultInputValues(PhabricatorChartDataQuery $query) {
$this->sourceFunction = $source;
return $this;
}
protected function getSourceFunction() {
return $this->sourceFunction;
}
final public function setXAxis(PhabricatorChartAxis $x_axis) {
$this->xAxis = $x_axis;
return $this;
}
final public function getXAxis() {
return $this->xAxis;
}
final public function setYAxis(PhabricatorChartAxis $y_axis) {
$this->yAxis = $y_axis;
return $this;
}
final public function getYAxis() {
return $this->yAxis;
}
protected function canEvaluateFunction() {
return false;
}
protected function evaluateFunction($x) {
throw new PhutilMethodNotImplementedException();
}
public function hasDomain() {
if ($this->canEvaluateFunction()) {
return false;
}
throw new PhutilMethodNotImplementedException();
}
public function getDatapoints(PhabricatorChartDataQuery $query) {
if ($this->canEvaluateFunction()) {
$points = $this->newSourceDatapoints($query);
foreach ($points as $key => $point) {
$y = $point['y'];
$y = $this->evaluateFunction($y);
$points[$key]['y'] = $y;
}
return $points;
}
return $this->newDatapoints($query);
}
protected function newDatapoints(PhabricatorChartDataQuery $query) {
throw new PhutilMethodNotImplementedException();
}
protected function newSourceDatapoints(PhabricatorChartDataQuery $query) {
$source = $this->getSourceFunction();
if ($source) {
return $source->getDatapoints($query);
}
return $this->newDefaultDatapoints($query);
}
protected function newDefaultDatapoints(PhabricatorChartDataQuery $query) {
$x_min = $query->getMinimumValue(); $x_min = $query->getMinimumValue();
$x_max = $query->getMaximumValue(); $x_max = $query->getMaximumValue();
$limit = $query->getLimit(); $limit = $query->getLimit();
$points = array(); return $this->newLinearSteps($x_min, $x_max, $limit);
$steps = $this->newLinearSteps($x_min, $x_max, $limit);
foreach ($steps as $step) {
$points[] = array(
'x' => $step,
'y' => $step,
);
}
return $points;
} }
protected function newLinearSteps($src, $dst, $count) { protected function newLinearSteps($src, $dst, $count) {
@ -213,5 +249,4 @@ abstract class PhabricatorChartFunction
return $steps; return $steps;
} }
} }

View file

@ -5,7 +5,7 @@ final class PhabricatorChartFunctionArgument
private $name; private $name;
private $type; private $type;
private $isSourceFunction; private $repeatable;
public function setName($name) { public function setName($name) {
$this->name = $name; $this->name = $name;
@ -16,11 +16,21 @@ final class PhabricatorChartFunctionArgument
return $this->name; return $this->name;
} }
public function setRepeatable($repeatable) {
$this->repeatable = $repeatable;
return $this;
}
public function getRepeatable() {
return $this->repeatable;
}
public function setType($type) { public function setType($type) {
$types = array( $types = array(
'fact-key' => true, 'fact-key' => true,
'function' => true, 'function' => true,
'number' => true, 'number' => true,
'phid' => true,
); );
if (!isset($types[$type])) { if (!isset($types[$type])) {
@ -40,17 +50,12 @@ final class PhabricatorChartFunctionArgument
return $this->type; return $this->type;
} }
public function setIsSourceFunction($is_source_function) {
$this->isSourceFunction = $is_source_function;
return $this;
}
public function getIsSourceFunction() {
return $this->isSourceFunction;
}
public function newValue($value) { public function newValue($value) {
switch ($this->getType()) { switch ($this->getType()) {
case 'phid':
// TODO: This could be validated better, but probably should not be
// a primitive type.
return $value;
case 'fact-key': case 'fact-key':
if (!is_string($value)) { if (!is_string($value)) {
throw new Exception( throw new Exception(

View file

@ -11,6 +11,7 @@ final class PhabricatorChartFunctionArgumentParser
private $argumentMap = array(); private $argumentMap = array();
private $argumentPosition = 0; private $argumentPosition = 0;
private $argumentValues = array(); private $argumentValues = array();
private $repeatableArgument = null;
public function setFunction(PhabricatorChartFunction $function) { public function setFunction(PhabricatorChartFunction $function) {
$this->function = $function; $this->function = $function;
@ -55,6 +56,32 @@ final class PhabricatorChartFunctionArgumentParser
$name)); $name));
} }
if ($this->repeatableArgument) {
if ($spec->getRepeatable()) {
throw new Exception(
pht(
'Chart function "%s" emitted multiple repeatable argument '.
'specifications ("%s" and "%s"). Only one argument may be '.
'repeatable and it must be the last argument.',
$this->getFunctionArgumentSignature(),
$name,
$this->repeatableArgument->getName()));
} else {
throw new Exception(
pht(
'Chart function "%s" emitted a repeatable argument ("%s"), then '.
'another argument ("%s"). No arguments are permitted after a '.
'repeatable argument.',
$this->getFunctionArgumentSignature(),
$this->repeatableArgument->getName(),
$name));
}
}
if ($spec->getRepeatable()) {
$this->repeatableArgument = $spec;
}
$this->argumentMap[$name] = $spec; $this->argumentMap[$name] = $spec;
$this->unparsedArguments[] = $spec; $this->unparsedArguments[] = $spec;
@ -72,12 +99,30 @@ final class PhabricatorChartFunctionArgumentParser
return $this; return $this;
} }
public function getAllArguments() {
return array_values($this->argumentMap);
}
public function getRawArguments() {
return $this->rawArguments;
}
public function parseArguments() { public function parseArguments() {
$have_count = count($this->rawArguments); $have_count = count($this->rawArguments);
$want_count = count($this->argumentMap); $want_count = count($this->argumentMap);
if ($this->haveAllArguments) { if ($this->haveAllArguments) {
if ($want_count !== $have_count) { if ($this->repeatableArgument) {
if ($want_count > $have_count) {
throw new Exception(
pht(
'Function "%s" expects %s or more argument(s), but only %s '.
'argument(s) were provided.',
$this->getFunctionArgumentSignature(),
$want_count,
$have_count));
}
} else if ($want_count !== $have_count) {
throw new Exception( throw new Exception(
pht( pht(
'Function "%s" expects %s argument(s), but %s argument(s) were '. 'Function "%s" expects %s argument(s), but %s argument(s) were '.
@ -105,6 +150,14 @@ final class PhabricatorChartFunctionArgumentParser
$raw_argument = array_shift($this->unconsumedArguments); $raw_argument = array_shift($this->unconsumedArguments);
$this->argumentPosition++; $this->argumentPosition++;
$is_repeatable = $argument->getRepeatable();
// If this argument is repeatable and we have more arguments, add it
// back to the end of the list so we can continue parsing.
if ($is_repeatable && $this->unconsumedArguments) {
$this->unparsedArguments[] = $argument;
}
try { try {
$value = $argument->newValue($raw_argument); $value = $argument->newValue($raw_argument);
} catch (Exception $ex) { } catch (Exception $ex) {
@ -118,9 +171,16 @@ final class PhabricatorChartFunctionArgumentParser
$ex->getMessage())); $ex->getMessage()));
} }
if ($is_repeatable) {
if (!isset($this->argumentValues[$name])) {
$this->argumentValues[$name] = array();
}
$this->argumentValues[$name][] = $value;
} else {
$this->argumentValues[$name] = $value; $this->argumentValues[$name] = $value;
} }
} }
}
public function getArgumentValue($key) { public function getArgumentValue($key) {
if (!array_key_exists($key, $this->argumentValues)) { if (!array_key_exists($key, $this->argumentValues)) {
@ -141,7 +201,7 @@ final class PhabricatorChartFunctionArgumentParser
$argument_list[] = $key; $argument_list[] = $key;
} }
if (!$this->haveAllArguments) { if (!$this->haveAllArguments || $this->repeatableArgument) {
$argument_list[] = '...'; $argument_list[] = '...';
} }
@ -151,43 +211,4 @@ final class PhabricatorChartFunctionArgumentParser
implode(', ', $argument_list)); implode(', ', $argument_list));
} }
public function getSourceFunctionArgument() {
$required_type = 'function';
$sources = array();
foreach ($this->argumentMap as $key => $argument) {
if (!$argument->getIsSourceFunction()) {
continue;
}
if ($argument->getType() !== $required_type) {
throw new Exception(
pht(
'Function "%s" defines an argument "%s" which is marked as a '.
'source function, but the type of this argument is not "%s".',
$this->getFunctionArgumentSignature(),
$argument->getName(),
$required_type));
}
$sources[$key] = $argument;
}
if (!$sources) {
return null;
}
if (count($sources) > 1) {
throw new Exception(
pht(
'Function "%s" defines more than one argument as a source '.
'function (arguments: %s). Functions must have zero or one '.
'source function.',
$this->getFunctionArgumentSignature(),
implode(', ', array_keys($sources))));
}
return head($sources);
}
} }

View file

@ -0,0 +1,56 @@
<?php
final class PhabricatorChartFunctionLabel
extends Phobject {
private $name;
private $color;
private $icon;
private $fillColor;
public function setName($name) {
$this->name = $name;
return $this;
}
public function getName() {
return $this->name;
}
public function setColor($color) {
$this->color = $color;
return $this;
}
public function getColor() {
return $this->color;
}
public function setIcon($icon) {
$this->icon = $icon;
return $this;
}
public function getIcon() {
return $this->icon;
}
public function setFillColor($fill_color) {
$this->fillColor = $fill_color;
return $this;
}
public function getFillColor() {
return $this->fillColor;
}
public function toWireFormat() {
return array(
'name' => $this->getName(),
'color' => $this->getColor(),
'icon' => $this->getIcon(),
'fillColor' => $this->getFillColor(),
);
}
}

View file

@ -0,0 +1,62 @@
<?php
final class PhabricatorChartInterval
extends Phobject {
private $min;
private $max;
public function __construct($min, $max) {
$this->min = $min;
$this->max = $max;
}
public static function newFromIntervalList(array $intervals) {
$min = null;
$max = null;
foreach ($intervals as $interval) {
if ($interval === null) {
continue;
}
$interval_min = $interval->getMin();
if ($interval_min !== null) {
if ($min === null) {
$min = $interval_min;
} else {
$min = min($min, $interval_min);
}
}
$interval_max = $interval->getMax();
if ($interval_max !== null) {
if ($max === null) {
$max = $interval_max;
} else {
$max = max($max, $interval_max);
}
}
}
return new self($min, $max);
}
public function setMin($min) {
$this->min = $min;
return $this;
}
public function getMin() {
return $this->min;
}
public function setMax($max) {
$this->max = $max;
return $this;
}
public function getMax() {
return $this->max;
}
}

View file

@ -0,0 +1,171 @@
<?php
final class PhabricatorChartStackedAreaDataset
extends PhabricatorChartDataset {
const DATASETKEY = 'stacked-area';
protected function newChartDisplayData(
PhabricatorChartDataQuery $data_query) {
$functions = $this->getFunctions();
$reversed_functions = array_reverse($functions, true);
$function_points = array();
foreach ($reversed_functions as $function_idx => $function) {
$function_points[$function_idx] = array();
$datapoints = $function->newDatapoints($data_query);
foreach ($datapoints as $point) {
$x = $point['x'];
$function_points[$function_idx][$x] = $point;
}
}
$raw_points = $function_points;
// We need to define every function we're drawing at every point where
// any of the functions we're drawing are defined. If we don't, we'll
// end up with weird gaps or overlaps between adjacent areas, and won't
// know how much we need to lift each point above the baseline when
// stacking the functions on top of one another.
$must_define = array();
foreach ($function_points as $function_idx => $points) {
foreach ($points as $x => $point) {
$must_define[$x] = $x;
}
}
ksort($must_define);
foreach ($reversed_functions as $function_idx => $function) {
$missing = array();
foreach ($must_define as $x) {
if (!isset($function_points[$function_idx][$x])) {
$missing[$x] = true;
}
}
if (!$missing) {
continue;
}
$points = $function_points[$function_idx];
$values = array_keys($points);
$cursor = -1;
$length = count($values);
foreach ($missing as $x => $ignored) {
// Move the cursor forward until we find the last point before "x"
// which is defined.
while ($cursor + 1 < $length && $values[$cursor + 1] < $x) {
$cursor++;
}
// If this new point is to the left of all defined points, we'll
// assume the value is 0. If the point is to the right of all defined
// points, we assume the value is the same as the last known value.
// If it's between two defined points, we average them.
if ($cursor < 0) {
$y = 0;
} else if ($cursor + 1 < $length) {
$xmin = $values[$cursor];
$xmax = $values[$cursor + 1];
$ymin = $points[$xmin]['y'];
$ymax = $points[$xmax]['y'];
// Fill in the missing point by creating a linear interpolation
// between the two adjacent points.
$distance = ($x - $xmin) / ($xmax - $xmin);
$y = $ymin + (($ymax - $ymin) * $distance);
} else {
$xmin = $values[$cursor];
$y = $function_points[$function_idx][$xmin]['y'];
}
$function_points[$function_idx][$x] = array(
'x' => $x,
'y' => $y,
);
}
ksort($function_points[$function_idx]);
}
$range_min = null;
$range_max = null;
$series = array();
$baseline = array();
foreach ($function_points as $function_idx => $points) {
$below = idx($function_points, $function_idx - 1);
$bounds = array();
foreach ($points as $x => $point) {
if (!isset($baseline[$x])) {
$baseline[$x] = 0;
}
$y0 = $baseline[$x];
$baseline[$x] += $point['y'];
$y1 = $baseline[$x];
$bounds[] = array(
'x' => $x,
'y0' => $y0,
'y1' => $y1,
);
if (isset($raw_points[$function_idx][$x])) {
$raw_points[$function_idx][$x]['y1'] = $y1;
}
if ($range_min === null) {
$range_min = $y0;
}
$range_min = min($range_min, $y0, $y1);
if ($range_max === null) {
$range_max = $y1;
}
$range_max = max($range_max, $y0, $y1);
}
$series[] = $bounds;
}
$series = array_reverse($series);
$events = array();
foreach ($raw_points as $function_idx => $points) {
$event_list = array();
foreach ($points as $point) {
$event_list[] = $point;
}
$events[] = $event_list;
}
$wire_labels = array();
foreach ($functions as $function_key => $function) {
$label = $function->getFunctionLabel();
$wire_labels[] = $label->toWireFormat();
}
$result = array(
'type' => $this->getDatasetTypeKey(),
'data' => $series,
'events' => $events,
'labels' => $wire_labels,
);
return id(new PhabricatorChartDisplayData())
->setWireData($result)
->setRange(new PhabricatorChartInterval($range_min, $range_max));
}
}

View file

@ -0,0 +1,73 @@
<?php
final class PhabricatorComposeChartFunction
extends PhabricatorHigherOrderChartFunction {
const FUNCTIONKEY = 'compose';
protected function newArguments() {
return array(
$this->newArgument()
->setName('f')
->setType('function')
->setRepeatable(true),
);
}
public function evaluateFunction(array $xv) {
$original_positions = array_keys($xv);
$remaining_positions = $original_positions;
foreach ($this->getFunctionArguments() as $function) {
$xv = $function->evaluateFunction($xv);
// If a function evaluates to "null" at some positions, we want to return
// "null" at those positions and stop evaluating the function.
// We also want to pass "evaluateFunction()" a natural list containing
// only values it should evaluate: keys should not be important and we
// should not pass "null". This simplifies implementation of functions.
// To do this, first create a map from original input positions to
// function return values.
$xv = array_combine($remaining_positions, $xv);
// If a function evaluated to "null" at any position where we evaluated
// it, the result will be "null". We remove the position from the
// vector so we stop evaluating it.
foreach ($xv as $x => $y) {
if ($y !== null) {
continue;
}
unset($xv[$x]);
}
// Store the remaining original input positions for the next round, then
// throw away the array keys so we're passing the next function a natural
// list with only non-"null" values.
$remaining_positions = array_keys($xv);
$xv = array_values($xv);
// If we have no more inputs to evaluate, we can bail out early rather
// than passing empty vectors to functions for evaluation.
if (!$xv) {
break;
}
}
$yv = array();
$xv = array_combine($remaining_positions, $xv);
foreach ($original_positions as $position) {
if (isset($xv[$position])) {
$y = $xv[$position];
} else {
$y = null;
}
$yv[$position] = $y;
}
return $yv;
}
}

View file

@ -13,12 +13,16 @@ final class PhabricatorConstantChartFunction
); );
} }
protected function canEvaluateFunction() { public function evaluateFunction(array $xv) {
return true; $n = $this->getArgument('n');
$yv = array();
foreach ($xv as $x) {
$yv[] = $n;
} }
protected function evaluateFunction($x) { return $yv;
return $this->getArgument('n');
} }
} }

View file

@ -6,20 +6,17 @@ final class PhabricatorCosChartFunction
const FUNCTIONKEY = 'cos'; const FUNCTIONKEY = 'cos';
protected function newArguments() { protected function newArguments() {
return array( return array();
$this->newArgument()
->setName('x')
->setType('function')
->setIsSourceFunction(true),
);
} }
protected function canEvaluateFunction() { public function evaluateFunction(array $xv) {
return true; $yv = array();
foreach ($xv as $x) {
$yv[] = cos(deg2rad($x));
} }
protected function evaluateFunction($x) { return $yv;
return cos(deg2rad($x));
} }
} }

View file

@ -6,7 +6,7 @@ final class PhabricatorFactChartFunction
const FUNCTIONKEY = 'fact'; const FUNCTIONKEY = 'fact';
private $fact; private $fact;
private $datapoints; private $map;
protected function newArguments() { protected function newArguments() {
$key_argument = $this->newArgument() $key_argument = $this->newArgument()
@ -35,82 +35,68 @@ final class PhabricatorFactChartFunction
$conn = $table->establishConnection('r'); $conn = $table->establishConnection('r');
$table_name = $table->getTableName(); $table_name = $table->getTableName();
$where = array();
$where[] = qsprintf(
$conn,
'keyID = %d',
$key_id);
$parser = $this->getArgumentParser();
$parts = $fact->buildWhereClauseParts($conn, $parser);
foreach ($parts as $part) {
$where[] = $part;
}
$data = queryfx_all( $data = queryfx_all(
$conn, $conn,
'SELECT value, epoch FROM %T WHERE keyID = %d ORDER BY epoch ASC', 'SELECT value, epoch FROM %T WHERE %LA ORDER BY epoch ASC',
$table_name, $table_name,
$key_id); $where);
if (!$data) {
return; $map = array();
if ($data) {
foreach ($data as $row) {
$value = (int)$row['value'];
$epoch = (int)$row['epoch'];
if (!isset($map[$epoch])) {
$map[$epoch] = 0;
} }
$points = array(); $map[$epoch] += $value;
$sum = 0;
foreach ($data as $key => $row) {
$sum += (int)$row['value'];
$points[] = array(
'x' => (int)$row['epoch'],
'y' => $sum,
);
}
$this->datapoints = $points;
}
public function getDatapoints(PhabricatorChartDataQuery $query) {
$points = $this->datapoints;
if (!$points) {
return array();
}
$x_min = $query->getMinimumValue();
$x_max = $query->getMaximumValue();
$limit = $query->getLimit();
if ($x_min !== null) {
foreach ($points as $key => $point) {
if ($point['x'] < $x_min) {
unset($points[$key]);
}
} }
} }
if ($x_max !== null) { $this->map = $map;
foreach ($points as $key => $point) {
if ($point['x'] > $x_max) {
unset($points[$key]);
}
}
}
// If we have too many data points, throw away some of the data.
if ($limit !== null) {
$count = count($points);
if ($count > $limit) {
$ii = 0;
$every = ceil($count / $limit);
foreach ($points as $key => $point) {
$ii++;
if (($ii % $every) && ($ii != $count)) {
unset($points[$key]);
}
}
}
}
return $points;
}
public function hasDomain() {
return true;
} }
public function getDomain() { public function getDomain() {
// TODO: We can examine the data to fit a better domain. $min = head_key($this->map);
$max = last_key($this->map);
$now = PhabricatorTime::getNow(); return new PhabricatorChartInterval($min, $max);
return array($now - phutil_units('90 days in seconds'), $now); }
public function newInputValues(PhabricatorChartDataQuery $query) {
return array_keys($this->map);
}
public function evaluateFunction(array $xv) {
$map = $this->map;
$yv = array();
foreach ($xv as $x) {
if (isset($map[$x])) {
$yv[] = $map[$x];
} else {
$yv[] = null;
}
}
return $yv;
} }
} }

View file

@ -0,0 +1,35 @@
<?php
abstract class PhabricatorHigherOrderChartFunction
extends PhabricatorChartFunction {
public function getDomain() {
$domains = array();
foreach ($this->getFunctionArguments() as $function) {
$domains[] = $function->getDomain();
}
return PhabricatorChartInterval::newFromIntervalList($domains);
}
public function newInputValues(PhabricatorChartDataQuery $query) {
$map = array();
foreach ($this->getFunctionArguments() as $function) {
$xv = $function->newInputValues($query);
if ($xv !== null) {
foreach ($xv as $x) {
$map[$x] = true;
}
}
}
if (!$map) {
return null;
}
ksort($map);
return array_keys($map);
}
}

View file

@ -0,0 +1,40 @@
<?php
final class PhabricatorMaxChartFunction
extends PhabricatorChartFunction {
const FUNCTIONKEY = 'max';
protected function newArguments() {
return array(
$this->newArgument()
->setName('x')
->setType('function'),
$this->newArgument()
->setName('max')
->setType('number'),
);
}
public function getDomain() {
return $this->getArgument('x')->getDomain();
}
public function newInputValues(PhabricatorChartDataQuery $query) {
return $this->getArgument('x')->newInputValues($query);
}
public function evaluateFunction(array $xv) {
$yv = $this->getArgument('x')->evaluateFunction($xv);
$max = $this->getArgument('max');
foreach ($yv as $k => $y) {
if ($y > $max) {
$yv[$k] = null;
}
}
return $yv;
}
}

View file

@ -0,0 +1,40 @@
<?php
final class PhabricatorMinChartFunction
extends PhabricatorChartFunction {
const FUNCTIONKEY = 'min';
protected function newArguments() {
return array(
$this->newArgument()
->setName('x')
->setType('function'),
$this->newArgument()
->setName('min')
->setType('number'),
);
}
public function getDomain() {
return $this->getArgument('x')->getDomain();
}
public function newInputValues(PhabricatorChartDataQuery $query) {
return $this->getArgument('x')->newInputValues($query);
}
public function evaluateFunction(array $xv) {
$yv = $this->getArgument('x')->evaluateFunction($xv);
$min = $this->getArgument('min');
foreach ($yv as $k => $y) {
if ($y < $min) {
$yv[$k] = null;
}
}
return $yv;
}
}

View file

@ -7,22 +7,22 @@ final class PhabricatorScaleChartFunction
protected function newArguments() { protected function newArguments() {
return array( return array(
$this->newArgument()
->setName('x')
->setType('function')
->setIsSourceFunction(true),
$this->newArgument() $this->newArgument()
->setName('scale') ->setName('scale')
->setType('number'), ->setType('number'),
); );
} }
protected function canEvaluateFunction() { public function evaluateFunction(array $xv) {
return true; $scale = $this->getArgument('scale');
$yv = array();
foreach ($xv as $x) {
$yv[] = $x * $scale;
} }
protected function evaluateFunction($x) { return $yv;
return $x * $this->getArgument('scale');
} }
} }

View file

@ -7,22 +7,22 @@ final class PhabricatorShiftChartFunction
protected function newArguments() { protected function newArguments() {
return array( return array(
$this->newArgument()
->setName('x')
->setType('function')
->setIsSourceFunction(true),
$this->newArgument() $this->newArgument()
->setName('shift') ->setName('shift')
->setType('number'), ->setType('number'),
); );
} }
protected function canEvaluateFunction() { public function evaluateFunction(array $xv) {
return true; $shift = $this->getArgument('shift');
$yv = array();
foreach ($xv as $x) {
$yv[] = $x + $shift;
} }
protected function evaluateFunction($x) { return $yv;
return $x * $this->getArgument('shift');
} }
} }

View file

@ -6,20 +6,17 @@ final class PhabricatorSinChartFunction
const FUNCTIONKEY = 'sin'; const FUNCTIONKEY = 'sin';
protected function newArguments() { protected function newArguments() {
return array( return array();
$this->newArgument()
->setName('x')
->setType('function')
->setIsSourceFunction(true),
);
} }
protected function canEvaluateFunction() { public function evaluateFunction(array $xv) {
return true; $yv = array();
foreach ($xv as $x) {
$yv[] = sin(deg2rad($x));
} }
protected function evaluateFunction($x) { return $yv;
return sin(deg2rad($x));
} }
} }

View file

@ -0,0 +1,40 @@
<?php
final class PhabricatorSumChartFunction
extends PhabricatorHigherOrderChartFunction {
const FUNCTIONKEY = 'sum';
protected function newArguments() {
return array(
$this->newArgument()
->setName('f')
->setType('function')
->setRepeatable(true),
);
}
public function evaluateFunction(array $xv) {
$fv = array();
foreach ($this->getFunctionArguments() as $function) {
$fv[] = $function->evaluateFunction($xv);
}
$n = count($xv);
$yv = array_fill(0, $n, null);
foreach ($fv as $f) {
for ($ii = 0; $ii < $n; $ii++) {
if ($f[$ii] !== null) {
if (!isset($yv[$ii])) {
$yv[$ii] = 0;
}
$yv[$ii] += $f[$ii];
}
}
}
return $yv;
}
}

View file

@ -1,20 +0,0 @@
<?php
final class PhabricatorXChartFunction
extends PhabricatorChartFunction {
const FUNCTIONKEY = 'x';
protected function newArguments() {
return array();
}
protected function canEvaluateFunction() {
return true;
}
protected function evaluateFunction($x) {
return $x;
}
}

View file

@ -5,138 +5,38 @@ final class PhabricatorFactChartController extends PhabricatorFactController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer(); $viewer = $request->getViewer();
$chart_key = $request->getURIData('chartKey');
if ($chart_key === null) {
return $this->newDemoChart();
}
$engine = id(new PhabricatorChartRenderingEngine())
->setViewer($viewer);
$chart = $engine->loadChart($chart_key);
if (!$chart) {
return new Aphront404Response();
}
// When drawing a chart, we send down a placeholder piece of HTML first, // When drawing a chart, we send down a placeholder piece of HTML first,
// then fetch the data via async request. Determine if we're drawing // then fetch the data via async request. Determine if we're drawing
// the structure or actually pulling the data. // the structure or actually pulling the data.
$mode = $request->getURIData('mode'); $mode = $request->getURIData('mode');
$is_chart_mode = ($mode === 'chart');
$is_draw_mode = ($mode === 'draw'); $is_draw_mode = ($mode === 'draw');
$functions = array(); // TODO: For now, always pull the data. We'll throw it away if we're just
// drawing the frame, but this makes errors easier to debug.
$functions[] = id(new PhabricatorFactChartFunction()) $chart_data = $engine->newChartData();
->setArguments(array('tasks.count.create'));
$functions[] = id(new PhabricatorFactChartFunction())
->setArguments(array('tasks.open-count.create'));
$x_function = id(new PhabricatorXChartFunction())
->setArguments(array());
$functions[] = id(new PhabricatorConstantChartFunction())
->setArguments(array(360));
$functions[] = id(new PhabricatorSinChartFunction())
->setArguments(array($x_function));
$cos_function = id(new PhabricatorCosChartFunction())
->setArguments(array($x_function));
$functions[] = id(new PhabricatorShiftChartFunction())
->setArguments(
array(
array(
'scale',
array(
'cos',
array(
'scale',
array('x'),
0.001,
),
),
10,
),
200,
));
list($domain_min, $domain_max) = $this->getDomain($functions);
$axis = id(new PhabricatorChartAxis())
->setMinimumValue($domain_min)
->setMaximumValue($domain_max);
$data_query = id(new PhabricatorChartDataQuery())
->setMinimumValue($domain_min)
->setMaximumValue($domain_max)
->setLimit(2000);
$datasets = array();
foreach ($functions as $function) {
$function->setXAxis($axis);
$function->loadData();
$points = $function->getDatapoints($data_query);
$x = array();
$y = array();
foreach ($points as $point) {
$x[] = $point['x'];
$y[] = $point['y'];
}
$datasets[] = array(
'x' => $x,
'y' => $y,
'color' => '#ff00ff',
);
}
$y_min = 0;
$y_max = 0;
foreach ($datasets as $dataset) {
if (!$dataset['y']) {
continue;
}
$y_min = min($y_min, min($dataset['y']));
$y_max = max($y_max, max($dataset['y']));
}
$chart_data = array(
'datasets' => $datasets,
'xMin' => $domain_min,
'xMax' => $domain_max,
'yMin' => $y_min,
'yMax' => $y_max,
);
// TODO: Move this back up, it's just down here for now to make
// debugging easier so the main page throws a more visible exception when
// something goes wrong.
if ($is_chart_mode) {
return $this->newChartResponse();
}
if ($is_draw_mode) {
return id(new AphrontAjaxResponse())->setContent($chart_data); return id(new AphrontAjaxResponse())->setContent($chart_data);
} }
private function newChartResponse() { $chart_view = $engine->newChartView();
$request = $this->getRequest(); return $this->newChartResponse($chart_view);
$chart_node_id = celerity_generate_unique_node_id(); }
$chart_view = phutil_tag(
'div',
array(
'id' => $chart_node_id,
'style' => 'background: #ffffff; '.
'height: 480px; ',
),
'');
$data_uri = $request->getRequestURI();
$data_uri->setPath('/fact/draw/');
Javelin::initBehavior(
'line-chart',
array(
'chartNodeID' => $chart_node_id,
'dataURI' => (string)$data_uri,
));
private function newChartResponse($chart_view) {
$box = id(new PHUIObjectBoxView()) $box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Chart')) ->setHeaderText(pht('Chart'))
->appendChild($chart_view); ->appendChild($chart_view);
@ -151,52 +51,57 @@ final class PhabricatorFactChartController extends PhabricatorFactController {
->setTitle($title) ->setTitle($title)
->setCrumbs($crumbs) ->setCrumbs($crumbs)
->appendChild($box); ->appendChild($box);
} }
private function getDomain(array $functions) { private function newDemoChart() {
$domain_min_list = null; $viewer = $this->getViewer();
$domain_max_list = null;
foreach ($functions as $function) {
if ($function->hasDomain()) {
$domain = $function->getDomain();
list($domain_min, $domain_max) = $domain; $argvs = array();
if ($domain_min !== null) { $argvs[] = array('fact', 'tasks.count.create');
$domain_min_list[] = $domain_min;
$argvs[] = array('constant', 360);
$argvs[] = array('fact', 'tasks.open-count.create');
$argvs[] = array(
'sum',
array(
'accumulate',
array('fact', 'tasks.count.create'),
),
array(
'accumulate',
array('fact', 'tasks.open-count.create'),
),
);
$argvs[] = array(
'compose',
array('scale', 0.001),
array('cos'),
array('scale', 100),
array('shift', 800),
);
$datasets = array();
foreach ($argvs as $argv) {
$datasets[] = PhabricatorChartDataset::newFromDictionary(
array(
'function' => $argv,
));
} }
if ($domain_max !== null) { $chart = id(new PhabricatorFactChart())
$domain_max_list[] = $domain_max; ->setDatasets($datasets);
}
}
}
$domain_min = null; $engine = id(new PhabricatorChartRenderingEngine())
$domain_max = null; ->setViewer($viewer)
->setChart($chart);
if ($domain_min_list) { $chart = $engine->getStoredChart();
$domain_min = min($domain_min_list);
return id(new AphrontRedirectResponse())->setURI($chart->getURI());
} }
if ($domain_max_list) {
$domain_max = max($domain_max_list);
}
// If we don't have any domain data from the actual functions, pick a
// plausible domain automatically.
if ($domain_max === null) {
$domain_max = PhabricatorTime::getNow();
}
if ($domain_min === null) {
$domain_min = $domain_max - phutil_units('365 days in seconds');
}
return array($domain_min, $domain_max);
}
} }

View file

@ -15,7 +15,7 @@ final class PhabricatorFactDaemon extends PhabricatorDaemon {
} }
$this->log(pht('Zzz...')); $this->log(pht('Zzz...'));
$this->sleep(60 * 5); $this->sleep(15);
} }
} }

View file

@ -0,0 +1,97 @@
<?php
abstract class PhabricatorChartEngine
extends Phobject {
private $viewer;
private $engineParameters = array();
const KEY_ENGINE = 'engineKey';
const KEY_PARAMETERS = 'engineParameters';
final public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
final public function getViewer() {
return $this->viewer;
}
final protected function setEngineParameter($key, $value) {
$this->engineParameters[$key] = $value;
return $this;
}
final protected function getEngineParameter($key, $default = null) {
return idx($this->engineParameters, $key, $default);
}
final protected function getEngineParameters() {
return $this->engineParameters;
}
final public static function newFromChart(PhabricatorFactChart $chart) {
$engine_key = $chart->getChartParameter(self::KEY_ENGINE);
$engine_map = self::getAllChartEngines();
if (!isset($engine_map[$engine_key])) {
throw new Exception(
pht(
'Chart uses unknown engine key ("%s") and can not be rendered.',
$engine_key));
}
return clone id($engine_map[$engine_key]);
}
final public static function getAllChartEngines() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getChartEngineKey')
->execute();
}
final public function getChartEngineKey() {
return $this->getPhobjectClassConstant('CHARTENGINEKEY', 32);
}
final public function buildChart(PhabricatorFactChart $chart) {
$map = $chart->getChartParameter(self::KEY_PARAMETERS, array());
return $this->newChart($chart, $map);
}
abstract protected function newChart(PhabricatorFactChart $chart, array $map);
final public function buildChartPanel() {
$viewer = $this->getViewer();
$parameters = $this->getEngineParameters();
$chart = id(new PhabricatorFactChart())
->setChartParameter(self::KEY_ENGINE, $this->getChartEngineKey())
->setChartParameter(self::KEY_PARAMETERS, $this->getEngineParameters());
$rendering_engine = id(new PhabricatorChartRenderingEngine())
->setViewer($viewer)
->setChart($chart);
$chart = $rendering_engine->getStoredChart();
$panel_type = id(new PhabricatorDashboardChartPanelType())
->getPanelTypeKey();
$chart_panel = id(new PhabricatorDashboardPanel())
->setPanelType($panel_type)
->setProperty('chartKey', $chart->getChartKey());
return $chart_panel;
}
final protected function newFunction($name /* , ... */) {
$argv = func_get_args();
return id(new PhabricatorComposeChartFunction())
->setArguments(array($argv));
}
}

View file

@ -0,0 +1,213 @@
<?php
final class PhabricatorChartRenderingEngine
extends Phobject {
private $viewer;
private $chart;
private $storedChart;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function setChart(PhabricatorFactChart $chart) {
$this->chart = $chart;
return $this;
}
public function getChart() {
return $this->chart;
}
public function loadChart($chart_key) {
$chart = id(new PhabricatorFactChart())->loadOneWhere(
'chartKey = %s',
$chart_key);
if ($chart) {
$this->setChart($chart);
}
return $chart;
}
public static function getChartURI($chart_key) {
return id(new PhabricatorFactChart())
->setChartKey($chart_key)
->getURI();
}
public function getStoredChart() {
if (!$this->storedChart) {
$chart = $this->getChart();
$chart_key = $chart->getChartKey();
if (!$chart_key) {
$chart_key = $chart->newChartKey();
$stored_chart = id(new PhabricatorFactChart())->loadOneWhere(
'chartKey = %s',
$chart_key);
if ($stored_chart) {
$chart = $stored_chart;
} else {
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
try {
$chart->save();
} catch (AphrontDuplicateKeyQueryException $ex) {
$chart = id(new PhabricatorFactChart())->loadOneWhere(
'chartKey = %s',
$chart_key);
if (!$chart) {
throw new Exception(
pht(
'Failed to load chart with key "%s" after key collision. '.
'This should not be possible.',
$chart_key));
}
}
unset($unguarded);
}
$this->setChart($chart);
}
$this->storedChart = $chart;
}
return $this->storedChart;
}
public function newChartView() {
$chart = $this->getStoredChart();
$chart_key = $chart->getChartKey();
$chart_node_id = celerity_generate_unique_node_id();
$chart_view = phutil_tag(
'div',
array(
'id' => $chart_node_id,
'class' => 'chart-hardpoint',
));
$data_uri = urisprintf('/fact/chart/%s/draw/', $chart_key);
Javelin::initBehavior(
'line-chart',
array(
'chartNodeID' => $chart_node_id,
'dataURI' => (string)$data_uri,
));
return $chart_view;
}
public function newChartData() {
$chart = $this->getStoredChart();
$chart_key = $chart->getChartKey();
$chart_engine = PhabricatorChartEngine::newFromChart($chart)
->setViewer($this->getViewer());
$chart_engine->buildChart($chart);
$datasets = $chart->getDatasets();
$functions = array();
foreach ($datasets as $dataset) {
foreach ($dataset->getFunctions() as $function) {
$functions[] = $function;
}
}
$subfunctions = array();
foreach ($functions as $function) {
foreach ($function->getSubfunctions() as $subfunction) {
$subfunctions[] = $subfunction;
}
}
foreach ($subfunctions as $subfunction) {
$subfunction->loadData();
}
$domain = $this->getDomain($functions);
$axis = id(new PhabricatorChartAxis())
->setMinimumValue($domain->getMin())
->setMaximumValue($domain->getMax());
$data_query = id(new PhabricatorChartDataQuery())
->setMinimumValue($domain->getMin())
->setMaximumValue($domain->getMax())
->setLimit(2000);
$wire_datasets = array();
$ranges = array();
foreach ($datasets as $dataset) {
$display_data = $dataset->getChartDisplayData($data_query);
$ranges[] = $display_data->getRange();
$wire_datasets[] = $display_data->getWireData();
}
$range = $this->getRange($ranges);
$chart_data = array(
'datasets' => $wire_datasets,
'xMin' => $domain->getMin(),
'xMax' => $domain->getMax(),
'yMin' => $range->getMin(),
'yMax' => $range->getMax(),
);
return $chart_data;
}
private function getDomain(array $functions) {
$domains = array();
foreach ($functions as $function) {
$domains[] = $function->getDomain();
}
$domain = PhabricatorChartInterval::newFromIntervalList($domains);
// If we don't have any domain data from the actual functions, pick a
// plausible domain automatically.
if ($domain->getMax() === null) {
$domain->setMax(PhabricatorTime::getNow());
}
if ($domain->getMin() === null) {
$domain->setMin($domain->getMax() - phutil_units('365 days in seconds'));
}
return $domain;
}
private function getRange(array $ranges) {
$range = PhabricatorChartInterval::newFromIntervalList($ranges);
// Start the Y axis at 0 unless the chart has negative values.
$min = $range->getMin();
if ($min === null || $min >= 0) {
$range->setMin(0);
}
// If there's no maximum value, just pick a plausible default.
$max = $range->getMax();
if ($max === null) {
$range->setMax($range->getMin() + 100);
}
return $range;
}
}

View file

@ -38,7 +38,46 @@ abstract class PhabricatorFact extends Phobject {
abstract protected function newTemplateDatapoint(); abstract protected function newTemplateDatapoint();
final public function getFunctionArguments() { final public function getFunctionArguments() {
return array(); $key = $this->getKey();
$argv = array();
if (preg_match('/\.project\z/', $key)) {
$argv[] = id(new PhabricatorChartFunctionArgument())
->setName('phid')
->setType('phid');
} }
if (preg_match('/\.owner\z/', $key)) {
$argv[] = id(new PhabricatorChartFunctionArgument())
->setName('phid')
->setType('phid');
}
return $argv;
}
final public function buildWhereClauseParts(
AphrontDatabaseConnection $conn,
PhabricatorChartFunctionArgumentParser $arguments) {
$where = array();
$has_phid = $this->getFunctionArguments();
if ($has_phid) {
$phid = $arguments->getArgumentValue('phid');
$dimension_id = id(new PhabricatorFactObjectDimension())
->newDimensionID($phid);
$where[] = qsprintf(
$conn,
'dimensionID = %d',
$dimension_id);
}
return $where;
}
} }

View file

@ -7,6 +7,8 @@ final class PhabricatorFactChart
protected $chartKey; protected $chartKey;
protected $chartParameters = array(); protected $chartParameters = array();
private $datasets = self::ATTACHABLE;
protected function getConfiguration() { protected function getConfiguration() {
return array( return array(
self::CONFIG_SERIALIZATION => array( self::CONFIG_SERIALIZATION => array(
@ -33,6 +35,12 @@ final class PhabricatorFactChart
return idx($this->chartParameters, $key, $default); return idx($this->chartParameters, $key, $default);
} }
public function newChartKey() {
$digest = serialize($this->chartParameters);
$digest = PhabricatorHash::digestForIndex($digest);
return $digest;
}
public function save() { public function save() {
if ($this->getID()) { if ($this->getID()) {
throw new Exception( throw new Exception(
@ -41,14 +49,25 @@ final class PhabricatorFactChart
'overwrite an existing chart configuration.')); 'overwrite an existing chart configuration.'));
} }
$digest = serialize($this->chartParameters); $this->chartKey = $this->newChartKey();
$digest = PhabricatorHash::digestForIndex($digest);
$this->chartKey = $digest;
return parent::save(); return parent::save();
} }
public function attachDatasets(array $datasets) {
assert_instances_of($datasets, 'PhabricatorChartDataset');
$this->datasets = $datasets;
return $this;
}
public function getDatasets() {
return $this->assertAttached($this->datasets);
}
public function getURI() {
return urisprintf('/fact/chart/%s/', $this->getChartKey());
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */ /* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() { public function getCapabilities() {

View file

@ -31,6 +31,10 @@ final class PhabricatorFeedApplication extends PhabricatorApplication {
'/feed/' => array( '/feed/' => array(
'(?P<id>\d+)/' => 'PhabricatorFeedDetailController', '(?P<id>\d+)/' => 'PhabricatorFeedDetailController',
'(?:query/(?P<queryKey>[^/]+)/)?' => 'PhabricatorFeedListController', '(?:query/(?P<queryKey>[^/]+)/)?' => 'PhabricatorFeedListController',
'transactions/' => array(
$this->getQueryRoutePattern()
=> 'PhabricatorFeedTransactionListController',
),
), ),
); );
} }

View file

@ -1,24 +1,4 @@
<?php <?php
abstract class PhabricatorFeedController extends PhabricatorController { abstract class PhabricatorFeedController
extends PhabricatorController {}
protected function buildSideNavView() {
$user = $this->getRequest()->getUser();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
id(new PhabricatorFeedSearchEngine())
->setViewer($user)
->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
}
public function buildApplicationMenu() {
return $this->buildSideNavView()->getMenu();
}
}

View file

@ -1,20 +1,27 @@
<?php <?php
final class PhabricatorFeedListController extends PhabricatorFeedController { final class PhabricatorFeedListController
extends PhabricatorFeedController {
public function shouldAllowPublic() { public function shouldAllowPublic() {
return true; return true;
} }
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$querykey = $request->getURIData('queryKey'); $navigation = array();
$controller = id(new PhabricatorApplicationSearchController()) $navigation[] = id(new PHUIListItemView())
->setQueryKey($querykey) ->setType(PHUIListItemView::TYPE_LABEL)
->setSearchEngine(new PhabricatorFeedSearchEngine()) ->setName(pht('Transactions'));
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller); $navigation[] = id(new PHUIListItemView())
->setName(pht('Transaction Logs'))
->setHref($this->getApplicationURI('transactions/'));
return id(new PhabricatorFeedSearchEngine())
->setController($this)
->setNavigationItems($navigation)
->buildResponse();
} }
} }

View file

@ -0,0 +1,16 @@
<?php
final class PhabricatorFeedTransactionListController
extends PhabricatorFeedController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
return id(new PhabricatorFeedTransactionSearchEngine())
->setController($this)
->buildResponse();
}
}

View file

@ -0,0 +1,215 @@
<?php
final class PhabricatorFeedTransactionQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $phids;
private $authorPHIDs;
private $objectTypes;
private $createdMin;
private $createdMax;
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withAuthorPHIDs(array $phids) {
$this->authorPHIDs = $phids;
return $this;
}
public function withObjectTypes(array $types) {
$this->objectTypes = $types;
return $this;
}
public function withDateCreatedBetween($min, $max) {
$this->createdMin = $min;
$this->createdMax = $max;
return $this;
}
public function newResultObject() {
// Return an arbitrary valid transaction object. The actual query may
// return objects of any subclass of "ApplicationTransaction" when it is
// executed, but we need to pick something concrete here to make some
// integrations work (like automatic handling of PHIDs in data export).
return new PhabricatorUserTransaction();
}
protected function loadPage() {
$queries = $this->newTransactionQueries();
$xactions = array();
if ($this->shouldLimitResults()) {
$limit = $this->getRawResultLimit();
if (!$limit) {
$limit = null;
}
} else {
$limit = null;
}
// We're doing a bit of manual work to get paging working, because this
// query aggregates the results of a large number of subqueries.
// Overall, we're ordering transactions by "<dateCreated, phid>". Ordering
// by PHID is not very meaningful, but we don't need the ordering to be
// especially meaningful, just consistent. Using PHIDs is easy and does
// everything we need it to technically.
// To actually configure paging, if we have an external cursor, we load
// the internal cursor first. Then we pass it to each subquery and the
// subqueries pretend they just loaded a page where it was the last object.
// This configures their queries properly and we can aggregate a cohesive
// set of results by combining all the queries.
$cursor = $this->getExternalCursorString();
if ($cursor !== null) {
$cursor_object = $this->newInternalCursorFromExternalCursor($cursor);
} else {
$cursor_object = null;
}
$is_reversed = $this->getIsQueryOrderReversed();
$created_min = $this->createdMin;
$created_max = $this->createdMax;
$xaction_phids = $this->phids;
$author_phids = $this->authorPHIDs;
foreach ($queries as $query) {
$query->withDateCreatedBetween($created_min, $created_max);
if ($xaction_phids !== null) {
$query->withPHIDs($xaction_phids);
}
if ($author_phids !== null) {
$query->withAuthorPHIDs($author_phids);
}
if ($limit !== null) {
$query->setLimit($limit);
}
if ($cursor_object !== null) {
$query
->setAggregatePagingCursor($cursor_object)
->setIsQueryOrderReversed($is_reversed);
}
$query->setOrder('global');
$query_xactions = $query->execute();
foreach ($query_xactions as $query_xaction) {
$xactions[] = $query_xaction;
}
$xactions = msortv($xactions, 'newGlobalSortVector');
if ($is_reversed) {
$xactions = array_reverse($xactions);
}
if ($limit !== null) {
$xactions = array_slice($xactions, 0, $limit);
// If we've found enough transactions to fill up the entire requested
// page size, we can narrow the search window: transactions after the
// last transaction we've found so far can't possibly be part of the
// result set.
if (count($xactions) === $limit) {
$last_date = last($xactions)->getDateCreated();
if ($is_reversed) {
if ($created_max === null) {
$created_max = $last_date;
} else {
$created_max = min($created_max, $last_date);
}
} else {
if ($created_min === null) {
$created_min = $last_date;
} else {
$created_min = max($created_min, $last_date);
}
}
}
}
}
return $xactions;
}
public function getQueryApplicationClass() {
return 'PhabricatorFeedApplication';
}
private function newTransactionQueries() {
$viewer = $this->getViewer();
$queries = id(new PhutilClassMapQuery())
->setAncestorClass('PhabricatorApplicationTransactionQuery')
->execute();
$type_map = array();
// If we're querying for specific transaction PHIDs, we only need to
// consider queries which may load transactions with subtypes present
// in the list.
// For example, if we're loading Maniphest Task transaction PHIDs, we know
// we only have to look at Maniphest Task transactions, since other types
// of objects will never have the right transaction PHIDs.
$xaction_phids = $this->phids;
if ($xaction_phids) {
foreach ($xaction_phids as $xaction_phid) {
$type_map[phid_get_subtype($xaction_phid)] = true;
}
}
$object_types = $this->objectTypes;
if ($object_types) {
$object_types = array_fuse($object_types);
}
$results = array();
foreach ($queries as $query) {
$query_type = $query->getTemplateApplicationTransaction()
->getApplicationTransactionType();
if ($type_map) {
if (!isset($type_map[$query_type])) {
continue;
}
}
if ($object_types) {
if (!isset($object_types[$query_type])) {
continue;
}
}
$results[] = id(clone $query)
->setViewer($viewer)
->setParentQuery($this);
}
return $results;
}
protected function newExternalCursorStringForResult($object) {
return (string)$object->getPHID();
}
protected function applyExternalCursorConstraintsToQuery(
PhabricatorCursorPagedPolicyAwareQuery $subquery,
$cursor) {
$subquery->withPHIDs(array($cursor));
}
}

View file

@ -0,0 +1,230 @@
<?php
final class PhabricatorFeedTransactionSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Transactions');
}
public function getApplicationClassName() {
return 'PhabricatorFeedApplication';
}
public function newQuery() {
return new PhabricatorFeedTransactionQuery();
}
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorUsersSearchField())
->setLabel(pht('Authors'))
->setKey('authorPHIDs')
->setAliases(array('author', 'authors')),
id(new PhabricatorSearchDatasourceField())
->setLabel(pht('Object Types'))
->setKey('objectTypes')
->setAliases(array('objectType'))
->setDatasource(new PhabricatorTransactionsObjectTypeDatasource()),
id(new PhabricatorSearchDateField())
->setLabel(pht('Created After'))
->setKey('createdStart'),
id(new PhabricatorSearchDateField())
->setLabel(pht('Created Before'))
->setKey('createdEnd'),
);
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
if ($map['authorPHIDs']) {
$query->withAuthorPHIDs($map['authorPHIDs']);
}
if ($map['objectTypes']) {
$query->withObjectTypes($map['objectTypes']);
}
$created_min = $map['createdStart'];
$created_max = $map['createdEnd'];
if ($created_min && $created_max) {
if ($created_min > $created_max) {
throw new PhabricatorSearchConstraintException(
pht(
'The specified "Created Before" date is earlier in time than the '.
'specified "Created After" date, so this query can never match '.
'any results.'));
}
}
if ($created_min || $created_max) {
$query->withDateCreatedBetween($created_min, $created_max);
}
return $query;
}
protected function getURI($path) {
return '/feed/transactions/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'all' => pht('All Transactions'),
);
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery()
->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $objects,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($objects, 'PhabricatorApplicationTransaction');
$viewer = $this->requireViewer();
$handle_phids = array();
foreach ($objects as $object) {
$author_phid = $object->getAuthorPHID();
if ($author_phid !== null) {
$handle_phids[] = $author_phid;
}
$object_phid = $object->getObjectPHID();
if ($object_phid !== null) {
$handle_phids[] = $object_phid;
}
}
$handles = $viewer->loadHandles($handle_phids);
$rows = array();
foreach ($objects as $object) {
$author_phid = $object->getAuthorPHID();
$object_phid = $object->getObjectPHID();
try {
$title = $object->getTitle();
} catch (Exception $ex) {
$title = null;
}
$rows[] = array(
$handles[$author_phid]->renderLink(),
$handles[$object_phid]->renderLink(),
AphrontTableView::renderSingleDisplayLine($title),
phabricator_datetime($object->getDateCreated(), $viewer),
);
}
$table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Author'),
pht('Object'),
pht('Transaction'),
pht('Date'),
))
->setColumnClasses(
array(
null,
null,
'wide',
'right',
));
return id(new PhabricatorApplicationSearchResultView())
->setTable($table);
}
protected function newExportFields() {
$fields = array(
id(new PhabricatorPHIDExportField())
->setKey('authorPHID')
->setLabel(pht('Author PHID')),
id(new PhabricatorStringExportField())
->setKey('author')
->setLabel(pht('Author')),
id(new PhabricatorStringExportField())
->setKey('objectType')
->setLabel(pht('Object Type')),
id(new PhabricatorPHIDExportField())
->setKey('objectPHID')
->setLabel(pht('Object PHID')),
id(new PhabricatorStringExportField())
->setKey('objectName')
->setLabel(pht('Object Name')),
id(new PhabricatorStringExportField())
->setKey('description')
->setLabel(pht('Description')),
);
return $fields;
}
protected function newExportData(array $xactions) {
$viewer = $this->requireViewer();
$phids = array();
foreach ($xactions as $xaction) {
$phids[] = $xaction->getAuthorPHID();
$phids[] = $xaction->getObjectPHID();
}
$handles = $viewer->loadHandles($phids);
$export = array();
foreach ($xactions as $xaction) {
$xaction_phid = $xaction->getPHID();
$author_phid = $xaction->getAuthorPHID();
if ($author_phid) {
$author_name = $handles[$author_phid]->getName();
} else {
$author_name = null;
}
$object_phid = $xaction->getObjectPHID();
if ($object_phid) {
$object_name = $handles[$object_phid]->getName();
} else {
$object_name = null;
}
$old_target = $xaction->getRenderingTarget();
try {
$description = $xaction
->setRenderingTarget(PhabricatorApplicationTransaction::TARGET_TEXT)
->getTitle();
} catch (Exception $ex) {
$description = null;
}
$xaction->setRenderingTarget($old_target);
$export[] = array(
'authorPHID' => $author_phid,
'author' => $author_name,
'objectType' => phid_get_subtype($xaction_phid),
'objectPHID' => $object_phid,
'objectName' => $object_name,
'description' => $description,
);
}
return $export;
}
}

View file

@ -260,24 +260,58 @@ final class HeraldTestConsoleController extends HeraldController {
$query = PhabricatorApplicationTransactionQuery::newQueryForObject( $query = PhabricatorApplicationTransactionQuery::newQueryForObject(
$object); $object);
$xactions = $query $query
->withObjectPHIDs(array($object->getPHID())) ->withObjectPHIDs(array($object->getPHID()))
->setViewer($viewer) ->setViewer($viewer);
->setLimit(100)
->execute(); $xactions = new PhabricatorQueryIterator($query);
$applied = array(); $applied = array();
// Pick the most recent group of transactions. This may not be exactly the $recent_id = null;
// same as what Herald acted on: for example, we may select a single group $hard_limit = 1000;
// of transactions here which were really applied across two or more edits.
// Since this is relatively rare and we show you what we picked, it's okay
// that we just do roughly the right thing.
foreach ($xactions as $xaction) { foreach ($xactions as $xaction) {
if (!$xaction->shouldDisplayGroupWith($applied)) {
// If this transaction has Herald transcript metadata, it was applied by
// Herald. Exclude it from the list because the Herald rule engine always
// runs before Herald transactions apply, so there's no way that real
// rules would have seen this transaction.
$transcript_id = $xaction->getMetadataValue('herald:transcriptID');
if ($transcript_id !== null) {
continue;
}
$group_id = $xaction->getTransactionGroupID();
// If this is the first transaction, save the group ID: we want to
// select all transactions in the same group.
if (!$applied) {
$recent_id = $group_id;
if ($recent_id === null) {
// If the first transaction has no group ID, it is likely an older
// transaction from before the introduction of group IDs. In this
// case, select only the most recent transaction and bail out.
$applied[] = $xaction;
break; break;
} }
}
// If this transaction is from a different transaction group, we've
// found all the transactions applied in the most recent group.
if ($group_id !== $recent_id) {
break;
}
$applied[] = $xaction; $applied[] = $xaction;
if (count($applied) > $hard_limit) {
throw new Exception(
pht(
'This object ("%s") has more than %s transactions in its most '.
'recent transaction group; this is too many.',
$object->getPHID(),
new PhutilNumber($hard_limit)));
}
} }
return $applied; return $applied;

View file

@ -337,7 +337,8 @@ final class ManiphestReportController extends ManiphestController {
'the project recently, it is counted on the day it was '. 'the project recently, it is counted on the day it was '.
'opened, not the day it was categorized. If a task was part '. 'opened, not the day it was categorized. If a task was part '.
'of this project in the past but no longer is, it is not '. 'of this project in the past but no longer is, it is not '.
'counted at all.'); 'counted at all. This table may not agree exactly with the chart '.
'above.');
$header = pht('Task Burn Rate for Project %s', $handle->renderLink()); $header = pht('Task Burn Rate for Project %s', $handle->renderLink());
$caption = phutil_tag('p', array(), $inst); $caption = phutil_tag('p', array(), $inst);
} else { } else {
@ -379,26 +380,29 @@ final class ManiphestReportController extends ManiphestController {
list($burn_x, $burn_y) = $this->buildSeries($data); list($burn_x, $burn_y) = $this->buildSeries($data);
require_celerity_resource('d3'); if ($project_phid) {
require_celerity_resource('phui-chart-css'); $projects = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withPHIDs(array($project_phid))
->execute();
} else {
$projects = array();
}
Javelin::initBehavior('line-chart-legacy', array( $panel = id(new PhabricatorProjectBurndownChartEngine())
'hardpoint' => $id, ->setViewer($viewer)
'x' => array( ->setProjects($projects)
$burn_x, ->buildChartPanel();
),
'y' => array(
$burn_y,
),
'xformat' => 'epoch',
'yformat' => 'int',
));
$box = id(new PHUIObjectBoxView()) $chart_panel = $panel->setName(pht('Burnup Rate'));
->setHeaderText(pht('Burnup Rate'))
->appendChild($chart);
return array($filter, $box, $panel); $chart_view = id(new PhabricatorDashboardPanelRenderingEngine())
->setViewer($viewer)
->setPanel($chart_panel)
->setParentPanelPHIDs(array())
->renderPanel();
return array($filter, $chart_view, $panel);
} }
private function renderReportFilters(array $tokens, $has_window) { private function renderReportFilters(array $tokens, $has_window) {

View file

@ -31,7 +31,7 @@ final class ManiphestTaskOwnerTransaction
} }
public function getActionStrength() { public function getActionStrength() {
return 1.2; return 120;
} }
public function getActionName() { public function getActionName() {

View file

@ -27,7 +27,7 @@ final class ManiphestTaskPriorityTransaction
} }
public function getActionStrength() { public function getActionStrength() {
return 1.1; return 110;
} }
public function getActionName() { public function getActionName() {

View file

@ -22,7 +22,7 @@ final class ManiphestTaskStatusTransaction
} }
public function getActionStrength() { public function getActionStrength() {
return 1.3; return 130;
} }
public function getActionName() { public function getActionName() {

View file

@ -14,7 +14,7 @@ final class ManiphestTaskTitleTransaction
} }
public function getActionStrength() { public function getActionStrength() {
return 1.4; return 140;
} }
public function getActionName() { public function getActionName() {

View file

@ -1,7 +1,7 @@
<?php <?php
final class PhabricatorSelfHyperlinkEngineExtension final class PhabricatorSelfHyperlinkEngineExtension
extends PhutilRemarkupHyperlinkEngineExtension { extends PhabricatorRemarkupHyperlinkEngineExtension {
const LINKENGINEKEY = 'phabricator-self'; const LINKENGINEKEY = 'phabricator-self';
@ -15,15 +15,7 @@ final class PhabricatorSelfHyperlinkEngineExtension
return; return;
} }
// Find links which point to resources on the Phabricator install itself. $self_links = $this->getSelfLinks($hyperlinks);
// We're going to try to enhance these.
$self_links = array();
foreach ($hyperlinks as $link) {
$uri = $link->getURI();
if (PhabricatorEnv::isSelfURI($uri)) {
$self_links[] = $link;
}
}
// For links in the form "/X123", we can reasonably guess that they are // For links in the form "/X123", we can reasonably guess that they are
// fairly likely to be object names. Try to look them up. // fairly likely to be object names. Try to look them up.
@ -53,11 +45,13 @@ final class PhabricatorSelfHyperlinkEngineExtension
} }
if ($object_map) { if ($object_map) {
$handles = $viewer->loadHandles(mpull($object_map, 'getPHID')); $object_phids = mpull($object_map, 'getPHID');
} else { } else {
$handles = array(); $object_phids = array();
} }
$handles = $viewer->loadHandles($object_phids);
foreach ($object_names as $key => $object_name) { foreach ($object_names as $key => $object_name) {
$object = idx($object_map, $object_name); $object = idx($object_map, $object_name);
if (!$object) { if (!$object) {
@ -83,6 +77,13 @@ final class PhabricatorSelfHyperlinkEngineExtension
unset($self_links[$key]); unset($self_links[$key]);
} }
$key_mentioned = PhabricatorObjectRemarkupRule::KEY_MENTIONED_OBJECTS;
$mentioned_phids = $engine->getTextMetadata($key_mentioned, array());
foreach ($object_phids as $object_phid) {
$mentioned_phids[$object_phid] = $object_phid;
}
$engine->setTextMetadata($key_mentioned, $mentioned_phids);
} }
} }

View file

@ -10,7 +10,7 @@ final class PholioMockNameTransaction
} }
public function getActionStrength() { public function getActionStrength() {
return 1.4; return 140;
} }
public function applyInternalEffects($object, $value) { public function applyInternalEffects($object, $value) {

View file

@ -97,6 +97,10 @@ final class PhrictionEditController
$content_text = $content->getContent(); $content_text = $content->getContent();
$is_draft_mode = ($document->getContent()->getVersion() != $max_version); $is_draft_mode = ($document->getContent()->getVersion() != $max_version);
$default_view = $document->getViewPolicy();
$default_edit = $document->getEditPolicy();
$default_space = $document->getSpacePHID();
if ($request->isFormPost()) { if ($request->isFormPost()) {
if ($is_new) { if ($is_new) {
$save_as_draft = false; $save_as_draft = false;
@ -122,6 +126,11 @@ final class PhrictionEditController
$xactions = array(); $xactions = array();
if ($is_new) {
$xactions[] = id(new PhrictionTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_CREATE);
}
$xactions[] = id(new PhrictionTransaction()) $xactions[] = id(new PhrictionTransaction())
->setTransactionType(PhrictionDocumentTitleTransaction::TRANSACTIONTYPE) ->setTransactionType(PhrictionDocumentTitleTransaction::TRANSACTIONTYPE)
->setNewValue($title); ->setNewValue($title);
@ -130,13 +139,16 @@ final class PhrictionEditController
->setNewValue($content_text); ->setNewValue($content_text);
$xactions[] = id(new PhrictionTransaction()) $xactions[] = id(new PhrictionTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
->setNewValue($v_view); ->setNewValue($v_view)
->setIsDefaultTransaction($is_new && ($v_view === $default_view));
$xactions[] = id(new PhrictionTransaction()) $xactions[] = id(new PhrictionTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY) ->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
->setNewValue($v_edit); ->setNewValue($v_edit)
->setIsDefaultTransaction($is_new && ($v_edit === $default_edit));
$xactions[] = id(new PhrictionTransaction()) $xactions[] = id(new PhrictionTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_SPACE) ->setTransactionType(PhabricatorTransactions::TYPE_SPACE)
->setNewValue($v_space); ->setNewValue($v_space)
->setIsDefaultTransaction($is_new && ($v_space === $default_space));
$xactions[] = id(new PhrictionTransaction()) $xactions[] = id(new PhrictionTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)
->setNewValue(array('=' => $v_cc)); ->setNewValue(array('=' => $v_cc));

View file

@ -78,10 +78,13 @@ final class PhrictionDocument extends PhrictionDAO
} }
if ($parent_doc) { if ($parent_doc) {
$space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID(
$parent_doc);
$document $document
->setViewPolicy($parent_doc->getViewPolicy()) ->setViewPolicy($parent_doc->getViewPolicy())
->setEditPolicy($parent_doc->getEditPolicy()) ->setEditPolicy($parent_doc->getEditPolicy())
->setSpacePHID($parent_doc->getSpacePHID()); ->setSpacePHID($space_phid);
} else { } else {
$default_view_policy = PhabricatorPolicies::getMostOpenPolicy(); $default_view_policy = PhabricatorPolicies::getMostOpenPolicy();
$document $document

View file

@ -19,7 +19,7 @@ final class PhrictionDocumentDeleteTransaction
} }
public function getActionStrength() { public function getActionStrength() {
return 1.5; return 150;
} }
public function getActionName() { public function getActionName() {

View file

@ -32,7 +32,7 @@ abstract class PhrictionDocumentEditTransaction
} }
public function getActionStrength() { public function getActionStrength() {
return 1.3; return 130;
} }
public function getActionName() { public function getActionName() {

View file

@ -37,7 +37,7 @@ final class PhrictionDocumentMoveToTransaction
} }
public function getActionStrength() { public function getActionStrength() {
return 1.0; return 100;
} }
public function getActionName() { public function getActionName() {

View file

@ -21,7 +21,7 @@ final class PhrictionDocumentTitleTransaction
} }
public function getActionStrength() { public function getActionStrength() {
return 1.4; return 140;
} }
public function getActionName() { public function getActionName() {

View file

@ -90,6 +90,29 @@ final class PhabricatorPolicyFilter extends Phobject {
PhabricatorUser $user, PhabricatorUser $user,
PhabricatorPolicyInterface $object) { PhabricatorPolicyInterface $object) {
$capabilities = self::getRequiredInteractCapabilities($object);
foreach ($capabilities as $capability) {
if (!self::hasCapability($user, $object, $capability)) {
return false;
}
}
return true;
}
public static function requireCanInteract(
PhabricatorUser $user,
PhabricatorPolicyInterface $object) {
$capabilities = self::getRequiredInteractCapabilities($object);
foreach ($capabilities as $capability) {
self::requireCapability($user, $object, $capability);
}
}
private static function getRequiredInteractCapabilities(
PhabricatorPolicyInterface $object) {
$capabilities = $object->getCapabilities(); $capabilities = $object->getCapabilities();
$capabilities = array_fuse($capabilities); $capabilities = array_fuse($capabilities);
@ -107,13 +130,7 @@ final class PhabricatorPolicyFilter extends Phobject {
$require[] = $can_interact; $require[] = $can_interact;
} }
foreach ($require as $capability) { return $require;
if (!self::hasCapability($user, $object, $capability)) {
return false;
}
}
return true;
} }
public function setViewer(PhabricatorUser $user) { public function setViewer(PhabricatorUser $user) {

Some files were not shown because too many files have changed in this diff Show more