1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-02-24 20:49:06 +01:00

(stable) Promote 2020 Week 6

This commit is contained in:
epriestley 2020-02-12 11:34:20 -08:00
commit ff6f24db2b
61 changed files with 1761 additions and 965 deletions

View file

@ -9,7 +9,7 @@ return array(
'names' => array( 'names' => array(
'conpherence.pkg.css' => '3c8a0668', 'conpherence.pkg.css' => '3c8a0668',
'conpherence.pkg.js' => '020aebcf', 'conpherence.pkg.js' => '020aebcf',
'core.pkg.css' => 'ad8fc332', 'core.pkg.css' => '5edb4679',
'core.pkg.js' => '705aec2c', 'core.pkg.js' => '705aec2c',
'differential.pkg.css' => '607c84be', 'differential.pkg.css' => '607c84be',
'differential.pkg.js' => '1b97518d', 'differential.pkg.js' => '1b97518d',
@ -146,6 +146,7 @@ return array(
'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',
'rsrc/css/phui/phui-crumbs-view.css' => '614f43cf', 'rsrc/css/phui/phui-crumbs-view.css' => '614f43cf',
'rsrc/css/phui/phui-curtain-object-ref-view.css' => '12404744',
'rsrc/css/phui/phui-curtain-view.css' => '68c5efb6', 'rsrc/css/phui/phui-curtain-view.css' => '68c5efb6',
'rsrc/css/phui/phui-document-pro.css' => 'b9613a10', 'rsrc/css/phui/phui-document-pro.css' => 'b9613a10',
'rsrc/css/phui/phui-document-summary.css' => 'b068eed1', 'rsrc/css/phui/phui-document-summary.css' => 'b068eed1',
@ -165,7 +166,7 @@ return array(
'rsrc/css/phui/phui-left-right.css' => '68513c34', 'rsrc/css/phui/phui-left-right.css' => '68513c34',
'rsrc/css/phui/phui-lightbox.css' => '4ebf22da', 'rsrc/css/phui/phui-lightbox.css' => '4ebf22da',
'rsrc/css/phui/phui-list.css' => 'b05144dd', 'rsrc/css/phui/phui-list.css' => 'b05144dd',
'rsrc/css/phui/phui-object-box.css' => 'f434b6be', 'rsrc/css/phui/phui-object-box.css' => 'b8d7eea0',
'rsrc/css/phui/phui-pager.css' => 'd022c7ad', 'rsrc/css/phui/phui-pager.css' => 'd022c7ad',
'rsrc/css/phui/phui-pinboard-view.css' => '1f08f5d8', 'rsrc/css/phui/phui-pinboard-view.css' => '1f08f5d8',
'rsrc/css/phui/phui-policy-section-view.css' => '139fdc64', 'rsrc/css/phui/phui-policy-section-view.css' => '139fdc64',
@ -176,7 +177,7 @@ return array(
'rsrc/css/phui/phui-status.css' => 'e5ff8be0', 'rsrc/css/phui/phui-status.css' => 'e5ff8be0',
'rsrc/css/phui/phui-tag-view.css' => '8519160a', 'rsrc/css/phui/phui-tag-view.css' => '8519160a',
'rsrc/css/phui/phui-timeline-view.css' => '1e348e4b', 'rsrc/css/phui/phui-timeline-view.css' => '1e348e4b',
'rsrc/css/phui/phui-two-column-view.css' => '0a876b9e', 'rsrc/css/phui/phui-two-column-view.css' => 'f96d319f',
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'e86de308', 'rsrc/css/phui/workboards/phui-workboard-color.css' => 'e86de308',
'rsrc/css/phui/workboards/phui-workboard.css' => '74fc9d98', 'rsrc/css/phui/workboards/phui-workboard.css' => '74fc9d98',
'rsrc/css/phui/workboards/phui-workcard.css' => '913441b6', 'rsrc/css/phui/workboards/phui-workcard.css' => '913441b6',
@ -833,6 +834,7 @@ return array(
'phui-comment-form-css' => '68a2d99a', 'phui-comment-form-css' => '68a2d99a',
'phui-comment-panel-css' => 'ec4e31c0', 'phui-comment-panel-css' => 'ec4e31c0',
'phui-crumbs-view-css' => '614f43cf', 'phui-crumbs-view-css' => '614f43cf',
'phui-curtain-object-ref-view-css' => '12404744',
'phui-curtain-view-css' => '68c5efb6', 'phui-curtain-view-css' => '68c5efb6',
'phui-document-summary-view-css' => 'b068eed1', 'phui-document-summary-view-css' => 'b068eed1',
'phui-document-view-css' => '52b748a5', 'phui-document-view-css' => '52b748a5',
@ -855,7 +857,7 @@ return array(
'phui-left-right-css' => '68513c34', 'phui-left-right-css' => '68513c34',
'phui-lightbox-css' => '4ebf22da', 'phui-lightbox-css' => '4ebf22da',
'phui-list-view-css' => 'b05144dd', 'phui-list-view-css' => 'b05144dd',
'phui-object-box-css' => 'f434b6be', 'phui-object-box-css' => 'b8d7eea0',
'phui-oi-big-ui-css' => 'fa74cc35', 'phui-oi-big-ui-css' => 'fa74cc35',
'phui-oi-color-css' => 'b517bfa0', 'phui-oi-color-css' => 'b517bfa0',
'phui-oi-drag-ui-css' => 'da15d3dc', 'phui-oi-drag-ui-css' => 'da15d3dc',
@ -873,7 +875,7 @@ return array(
'phui-tag-view-css' => '8519160a', 'phui-tag-view-css' => '8519160a',
'phui-theme-css' => '35883b37', 'phui-theme-css' => '35883b37',
'phui-timeline-view-css' => '1e348e4b', 'phui-timeline-view-css' => '1e348e4b',
'phui-two-column-view-css' => '0a876b9e', 'phui-two-column-view-css' => 'f96d319f',
'phui-workboard-color-css' => 'e86de308', 'phui-workboard-color-css' => 'e86de308',
'phui-workboard-view-css' => '74fc9d98', 'phui-workboard-view-css' => '74fc9d98',
'phui-workcard-view-css' => '913441b6', 'phui-workcard-view-css' => '913441b6',

View file

@ -1991,6 +1991,8 @@ phutil_register_library_map(array(
'PHUICrumbView' => 'view/phui/PHUICrumbView.php', 'PHUICrumbView' => 'view/phui/PHUICrumbView.php',
'PHUICrumbsView' => 'view/phui/PHUICrumbsView.php', 'PHUICrumbsView' => 'view/phui/PHUICrumbsView.php',
'PHUICurtainExtension' => 'view/extension/PHUICurtainExtension.php', 'PHUICurtainExtension' => 'view/extension/PHUICurtainExtension.php',
'PHUICurtainObjectRefListView' => 'view/phui/PHUICurtainObjectRefListView.php',
'PHUICurtainObjectRefView' => 'view/phui/PHUICurtainObjectRefView.php',
'PHUICurtainPanelView' => 'view/layout/PHUICurtainPanelView.php', 'PHUICurtainPanelView' => 'view/layout/PHUICurtainPanelView.php',
'PHUICurtainView' => 'view/layout/PHUICurtainView.php', 'PHUICurtainView' => 'view/layout/PHUICurtainView.php',
'PHUIDiffGraphView' => 'infrastructure/diff/view/PHUIDiffGraphView.php', 'PHUIDiffGraphView' => 'infrastructure/diff/view/PHUIDiffGraphView.php',
@ -2037,8 +2039,10 @@ phutil_register_library_map(array(
'PHUIInfoView' => 'view/phui/PHUIInfoView.php', 'PHUIInfoView' => 'view/phui/PHUIInfoView.php',
'PHUIInvisibleCharacterTestCase' => 'view/phui/__tests__/PHUIInvisibleCharacterTestCase.php', 'PHUIInvisibleCharacterTestCase' => 'view/phui/__tests__/PHUIInvisibleCharacterTestCase.php',
'PHUIInvisibleCharacterView' => 'view/phui/PHUIInvisibleCharacterView.php', 'PHUIInvisibleCharacterView' => 'view/phui/PHUIInvisibleCharacterView.php',
'PHUILauncherView' => 'view/phui/PHUILauncherView.php',
'PHUILeftRightExample' => 'applications/uiexample/examples/PHUILeftRightExample.php', 'PHUILeftRightExample' => 'applications/uiexample/examples/PHUILeftRightExample.php',
'PHUILeftRightView' => 'view/phui/PHUILeftRightView.php', 'PHUILeftRightView' => 'view/phui/PHUILeftRightView.php',
'PHUILinkView' => 'view/phui/PHUILinkView.php',
'PHUIListExample' => 'applications/uiexample/examples/PHUIListExample.php', 'PHUIListExample' => 'applications/uiexample/examples/PHUIListExample.php',
'PHUIListItemView' => 'view/phui/PHUIListItemView.php', 'PHUIListItemView' => 'view/phui/PHUIListItemView.php',
'PHUIListView' => 'view/phui/PHUIListView.php', 'PHUIListView' => 'view/phui/PHUIListView.php',
@ -2798,46 +2802,42 @@ phutil_register_library_map(array(
'PhabricatorConduitTokenQuery' => 'applications/conduit/query/PhabricatorConduitTokenQuery.php', 'PhabricatorConduitTokenQuery' => 'applications/conduit/query/PhabricatorConduitTokenQuery.php',
'PhabricatorConduitTokenTerminateController' => 'applications/conduit/controller/PhabricatorConduitTokenTerminateController.php', 'PhabricatorConduitTokenTerminateController' => 'applications/conduit/controller/PhabricatorConduitTokenTerminateController.php',
'PhabricatorConduitTokensSettingsPanel' => 'applications/conduit/settings/PhabricatorConduitTokensSettingsPanel.php', 'PhabricatorConduitTokensSettingsPanel' => 'applications/conduit/settings/PhabricatorConduitTokensSettingsPanel.php',
'PhabricatorConfigAllController' => 'applications/config/controller/PhabricatorConfigAllController.php',
'PhabricatorConfigApplication' => 'applications/config/application/PhabricatorConfigApplication.php', 'PhabricatorConfigApplication' => 'applications/config/application/PhabricatorConfigApplication.php',
'PhabricatorConfigApplicationController' => 'applications/config/controller/PhabricatorConfigApplicationController.php', 'PhabricatorConfigCacheController' => 'applications/config/controller/services/PhabricatorConfigCacheController.php',
'PhabricatorConfigCacheController' => 'applications/config/controller/PhabricatorConfigCacheController.php', 'PhabricatorConfigClusterDatabasesController' => 'applications/config/controller/services/PhabricatorConfigClusterDatabasesController.php',
'PhabricatorConfigClusterDatabasesController' => 'applications/config/controller/PhabricatorConfigClusterDatabasesController.php', 'PhabricatorConfigClusterNotificationsController' => 'applications/config/controller/services/PhabricatorConfigClusterNotificationsController.php',
'PhabricatorConfigClusterNotificationsController' => 'applications/config/controller/PhabricatorConfigClusterNotificationsController.php', 'PhabricatorConfigClusterRepositoriesController' => 'applications/config/controller/services/PhabricatorConfigClusterRepositoriesController.php',
'PhabricatorConfigClusterRepositoriesController' => 'applications/config/controller/PhabricatorConfigClusterRepositoriesController.php', 'PhabricatorConfigClusterSearchController' => 'applications/config/controller/services/PhabricatorConfigClusterSearchController.php',
'PhabricatorConfigClusterSearchController' => 'applications/config/controller/PhabricatorConfigClusterSearchController.php',
'PhabricatorConfigCollectorsModule' => 'applications/config/module/PhabricatorConfigCollectorsModule.php', 'PhabricatorConfigCollectorsModule' => 'applications/config/module/PhabricatorConfigCollectorsModule.php',
'PhabricatorConfigColumnSchema' => 'applications/config/schema/PhabricatorConfigColumnSchema.php', 'PhabricatorConfigColumnSchema' => 'applications/config/schema/PhabricatorConfigColumnSchema.php',
'PhabricatorConfigConfigPHIDType' => 'applications/config/phid/PhabricatorConfigConfigPHIDType.php', 'PhabricatorConfigConfigPHIDType' => 'applications/config/phid/PhabricatorConfigConfigPHIDType.php',
'PhabricatorConfigConsoleController' => 'applications/config/controller/PhabricatorConfigConsoleController.php',
'PhabricatorConfigConstants' => 'applications/config/constants/PhabricatorConfigConstants.php', 'PhabricatorConfigConstants' => 'applications/config/constants/PhabricatorConfigConstants.php',
'PhabricatorConfigController' => 'applications/config/controller/PhabricatorConfigController.php', 'PhabricatorConfigController' => 'applications/config/controller/PhabricatorConfigController.php',
'PhabricatorConfigCoreSchemaSpec' => 'applications/config/schema/PhabricatorConfigCoreSchemaSpec.php', 'PhabricatorConfigCoreSchemaSpec' => 'applications/config/schema/PhabricatorConfigCoreSchemaSpec.php',
'PhabricatorConfigDatabaseController' => 'applications/config/controller/PhabricatorConfigDatabaseController.php', 'PhabricatorConfigDatabaseController' => 'applications/config/controller/services/PhabricatorConfigDatabaseController.php',
'PhabricatorConfigDatabaseIssueController' => 'applications/config/controller/PhabricatorConfigDatabaseIssueController.php', 'PhabricatorConfigDatabaseIssueController' => 'applications/config/controller/services/PhabricatorConfigDatabaseIssueController.php',
'PhabricatorConfigDatabaseSchema' => 'applications/config/schema/PhabricatorConfigDatabaseSchema.php', 'PhabricatorConfigDatabaseSchema' => 'applications/config/schema/PhabricatorConfigDatabaseSchema.php',
'PhabricatorConfigDatabaseSource' => 'infrastructure/env/PhabricatorConfigDatabaseSource.php', 'PhabricatorConfigDatabaseSource' => 'infrastructure/env/PhabricatorConfigDatabaseSource.php',
'PhabricatorConfigDatabaseStatusController' => 'applications/config/controller/PhabricatorConfigDatabaseStatusController.php', 'PhabricatorConfigDatabaseStatusController' => 'applications/config/controller/services/PhabricatorConfigDatabaseStatusController.php',
'PhabricatorConfigDefaultSource' => 'infrastructure/env/PhabricatorConfigDefaultSource.php', 'PhabricatorConfigDefaultSource' => 'infrastructure/env/PhabricatorConfigDefaultSource.php',
'PhabricatorConfigDictionarySource' => 'infrastructure/env/PhabricatorConfigDictionarySource.php', 'PhabricatorConfigDictionarySource' => 'infrastructure/env/PhabricatorConfigDictionarySource.php',
'PhabricatorConfigEdgeModule' => 'applications/config/module/PhabricatorConfigEdgeModule.php', 'PhabricatorConfigEdgeModule' => 'applications/config/module/PhabricatorConfigEdgeModule.php',
'PhabricatorConfigEditController' => 'applications/config/controller/PhabricatorConfigEditController.php', 'PhabricatorConfigEditController' => 'applications/config/controller/settings/PhabricatorConfigEditController.php',
'PhabricatorConfigEditor' => 'applications/config/editor/PhabricatorConfigEditor.php', 'PhabricatorConfigEditor' => 'applications/config/editor/PhabricatorConfigEditor.php',
'PhabricatorConfigEntry' => 'applications/config/storage/PhabricatorConfigEntry.php', 'PhabricatorConfigEntry' => 'applications/config/storage/PhabricatorConfigEntry.php',
'PhabricatorConfigEntryDAO' => 'applications/config/storage/PhabricatorConfigEntryDAO.php', 'PhabricatorConfigEntryDAO' => 'applications/config/storage/PhabricatorConfigEntryDAO.php',
'PhabricatorConfigEntryQuery' => 'applications/config/query/PhabricatorConfigEntryQuery.php', 'PhabricatorConfigEntryQuery' => 'applications/config/query/PhabricatorConfigEntryQuery.php',
'PhabricatorConfigFileSource' => 'infrastructure/env/PhabricatorConfigFileSource.php', 'PhabricatorConfigFileSource' => 'infrastructure/env/PhabricatorConfigFileSource.php',
'PhabricatorConfigGroupConstants' => 'applications/config/constants/PhabricatorConfigGroupConstants.php', 'PhabricatorConfigGroupConstants' => 'applications/config/constants/PhabricatorConfigGroupConstants.php',
'PhabricatorConfigGroupController' => 'applications/config/controller/PhabricatorConfigGroupController.php',
'PhabricatorConfigHTTPParameterTypesModule' => 'applications/config/module/PhabricatorConfigHTTPParameterTypesModule.php', 'PhabricatorConfigHTTPParameterTypesModule' => 'applications/config/module/PhabricatorConfigHTTPParameterTypesModule.php',
'PhabricatorConfigHistoryController' => 'applications/config/controller/PhabricatorConfigHistoryController.php', 'PhabricatorConfigIgnoreController' => 'applications/config/controller/issue/PhabricatorConfigIgnoreController.php',
'PhabricatorConfigIgnoreController' => 'applications/config/controller/PhabricatorConfigIgnoreController.php', 'PhabricatorConfigIssueListController' => 'applications/config/controller/issue/PhabricatorConfigIssueListController.php',
'PhabricatorConfigIssueListController' => 'applications/config/controller/PhabricatorConfigIssueListController.php', 'PhabricatorConfigIssuePanelController' => 'applications/config/controller/issue/PhabricatorConfigIssuePanelController.php',
'PhabricatorConfigIssuePanelController' => 'applications/config/controller/PhabricatorConfigIssuePanelController.php', 'PhabricatorConfigIssueViewController' => 'applications/config/controller/issue/PhabricatorConfigIssueViewController.php',
'PhabricatorConfigIssueViewController' => 'applications/config/controller/PhabricatorConfigIssueViewController.php',
'PhabricatorConfigJSON' => 'applications/config/json/PhabricatorConfigJSON.php', 'PhabricatorConfigJSON' => 'applications/config/json/PhabricatorConfigJSON.php',
'PhabricatorConfigJSONOptionType' => 'applications/config/custom/PhabricatorConfigJSONOptionType.php', 'PhabricatorConfigJSONOptionType' => 'applications/config/custom/PhabricatorConfigJSONOptionType.php',
'PhabricatorConfigKeySchema' => 'applications/config/schema/PhabricatorConfigKeySchema.php', 'PhabricatorConfigKeySchema' => 'applications/config/schema/PhabricatorConfigKeySchema.php',
'PhabricatorConfigListController' => 'applications/config/controller/PhabricatorConfigListController.php',
'PhabricatorConfigLocalSource' => 'infrastructure/env/PhabricatorConfigLocalSource.php', 'PhabricatorConfigLocalSource' => 'infrastructure/env/PhabricatorConfigLocalSource.php',
'PhabricatorConfigManagementDeleteWorkflow' => 'applications/config/management/PhabricatorConfigManagementDeleteWorkflow.php', 'PhabricatorConfigManagementDeleteWorkflow' => 'applications/config/management/PhabricatorConfigManagementDeleteWorkflow.php',
'PhabricatorConfigManagementDoneWorkflow' => 'applications/config/management/PhabricatorConfigManagementDoneWorkflow.php', 'PhabricatorConfigManagementDoneWorkflow' => 'applications/config/management/PhabricatorConfigManagementDoneWorkflow.php',
@ -2848,12 +2848,12 @@ phutil_register_library_map(array(
'PhabricatorConfigManagementWorkflow' => 'applications/config/management/PhabricatorConfigManagementWorkflow.php', 'PhabricatorConfigManagementWorkflow' => 'applications/config/management/PhabricatorConfigManagementWorkflow.php',
'PhabricatorConfigManualActivity' => 'applications/config/storage/PhabricatorConfigManualActivity.php', 'PhabricatorConfigManualActivity' => 'applications/config/storage/PhabricatorConfigManualActivity.php',
'PhabricatorConfigModule' => 'applications/config/module/PhabricatorConfigModule.php', 'PhabricatorConfigModule' => 'applications/config/module/PhabricatorConfigModule.php',
'PhabricatorConfigModuleController' => 'applications/config/controller/PhabricatorConfigModuleController.php', 'PhabricatorConfigModuleController' => 'applications/config/controller/module/PhabricatorConfigModuleController.php',
'PhabricatorConfigOption' => 'applications/config/option/PhabricatorConfigOption.php', 'PhabricatorConfigOption' => 'applications/config/option/PhabricatorConfigOption.php',
'PhabricatorConfigOptionType' => 'applications/config/custom/PhabricatorConfigOptionType.php', 'PhabricatorConfigOptionType' => 'applications/config/custom/PhabricatorConfigOptionType.php',
'PhabricatorConfigPHIDModule' => 'applications/config/module/PhabricatorConfigPHIDModule.php', 'PhabricatorConfigPHIDModule' => 'applications/config/module/PhabricatorConfigPHIDModule.php',
'PhabricatorConfigProxySource' => 'infrastructure/env/PhabricatorConfigProxySource.php', 'PhabricatorConfigProxySource' => 'infrastructure/env/PhabricatorConfigProxySource.php',
'PhabricatorConfigPurgeCacheController' => 'applications/config/controller/PhabricatorConfigPurgeCacheController.php', 'PhabricatorConfigPurgeCacheController' => 'applications/config/controller/services/PhabricatorConfigPurgeCacheController.php',
'PhabricatorConfigRegexOptionType' => 'applications/config/custom/PhabricatorConfigRegexOptionType.php', 'PhabricatorConfigRegexOptionType' => 'applications/config/custom/PhabricatorConfigRegexOptionType.php',
'PhabricatorConfigRemarkupRule' => 'infrastructure/markup/rule/PhabricatorConfigRemarkupRule.php', 'PhabricatorConfigRemarkupRule' => 'infrastructure/markup/rule/PhabricatorConfigRemarkupRule.php',
'PhabricatorConfigRequestExceptionHandlerModule' => 'applications/config/module/PhabricatorConfigRequestExceptionHandlerModule.php', 'PhabricatorConfigRequestExceptionHandlerModule' => 'applications/config/module/PhabricatorConfigRequestExceptionHandlerModule.php',
@ -2861,6 +2861,10 @@ phutil_register_library_map(array(
'PhabricatorConfigSchemaQuery' => 'applications/config/schema/PhabricatorConfigSchemaQuery.php', 'PhabricatorConfigSchemaQuery' => 'applications/config/schema/PhabricatorConfigSchemaQuery.php',
'PhabricatorConfigSchemaSpec' => 'applications/config/schema/PhabricatorConfigSchemaSpec.php', 'PhabricatorConfigSchemaSpec' => 'applications/config/schema/PhabricatorConfigSchemaSpec.php',
'PhabricatorConfigServerSchema' => 'applications/config/schema/PhabricatorConfigServerSchema.php', 'PhabricatorConfigServerSchema' => 'applications/config/schema/PhabricatorConfigServerSchema.php',
'PhabricatorConfigServicesController' => 'applications/config/controller/services/PhabricatorConfigServicesController.php',
'PhabricatorConfigSettingsController' => 'applications/config/controller/settings/PhabricatorConfigSettingsController.php',
'PhabricatorConfigSettingsHistoryController' => 'applications/config/controller/settings/PhabricatorConfigSettingsHistoryController.php',
'PhabricatorConfigSettingsListController' => 'applications/config/controller/settings/PhabricatorConfigSettingsListController.php',
'PhabricatorConfigSetupCheckModule' => 'applications/config/module/PhabricatorConfigSetupCheckModule.php', 'PhabricatorConfigSetupCheckModule' => 'applications/config/module/PhabricatorConfigSetupCheckModule.php',
'PhabricatorConfigSiteModule' => 'applications/config/module/PhabricatorConfigSiteModule.php', 'PhabricatorConfigSiteModule' => 'applications/config/module/PhabricatorConfigSiteModule.php',
'PhabricatorConfigSiteSource' => 'infrastructure/env/PhabricatorConfigSiteSource.php', 'PhabricatorConfigSiteSource' => 'infrastructure/env/PhabricatorConfigSiteSource.php',
@ -2872,7 +2876,6 @@ phutil_register_library_map(array(
'PhabricatorConfigTransactionQuery' => 'applications/config/query/PhabricatorConfigTransactionQuery.php', 'PhabricatorConfigTransactionQuery' => 'applications/config/query/PhabricatorConfigTransactionQuery.php',
'PhabricatorConfigType' => 'applications/config/type/PhabricatorConfigType.php', 'PhabricatorConfigType' => 'applications/config/type/PhabricatorConfigType.php',
'PhabricatorConfigValidationException' => 'applications/config/exception/PhabricatorConfigValidationException.php', 'PhabricatorConfigValidationException' => 'applications/config/exception/PhabricatorConfigValidationException.php',
'PhabricatorConfigVersionController' => 'applications/config/controller/PhabricatorConfigVersionController.php',
'PhabricatorConpherenceApplication' => 'applications/conpherence/application/PhabricatorConpherenceApplication.php', 'PhabricatorConpherenceApplication' => 'applications/conpherence/application/PhabricatorConpherenceApplication.php',
'PhabricatorConpherenceColumnMinimizeSetting' => 'applications/settings/setting/PhabricatorConpherenceColumnMinimizeSetting.php', 'PhabricatorConpherenceColumnMinimizeSetting' => 'applications/settings/setting/PhabricatorConpherenceColumnMinimizeSetting.php',
'PhabricatorConpherenceColumnVisibleSetting' => 'applications/settings/setting/PhabricatorConpherenceColumnVisibleSetting.php', 'PhabricatorConpherenceColumnVisibleSetting' => 'applications/settings/setting/PhabricatorConpherenceColumnVisibleSetting.php',
@ -8189,6 +8192,8 @@ phutil_register_library_map(array(
'PHUICrumbView' => 'AphrontView', 'PHUICrumbView' => 'AphrontView',
'PHUICrumbsView' => 'AphrontView', 'PHUICrumbsView' => 'AphrontView',
'PHUICurtainExtension' => 'Phobject', 'PHUICurtainExtension' => 'Phobject',
'PHUICurtainObjectRefListView' => 'AphrontTagView',
'PHUICurtainObjectRefView' => 'AphrontTagView',
'PHUICurtainPanelView' => 'AphrontTagView', 'PHUICurtainPanelView' => 'AphrontTagView',
'PHUICurtainView' => 'AphrontTagView', 'PHUICurtainView' => 'AphrontTagView',
'PHUIDiffGraphView' => 'Phobject', 'PHUIDiffGraphView' => 'Phobject',
@ -8235,8 +8240,10 @@ phutil_register_library_map(array(
'PHUIInfoView' => 'AphrontTagView', 'PHUIInfoView' => 'AphrontTagView',
'PHUIInvisibleCharacterTestCase' => 'PhabricatorTestCase', 'PHUIInvisibleCharacterTestCase' => 'PhabricatorTestCase',
'PHUIInvisibleCharacterView' => 'AphrontView', 'PHUIInvisibleCharacterView' => 'AphrontView',
'PHUILauncherView' => 'AphrontTagView',
'PHUILeftRightExample' => 'PhabricatorUIExample', 'PHUILeftRightExample' => 'PhabricatorUIExample',
'PHUILeftRightView' => 'AphrontTagView', 'PHUILeftRightView' => 'AphrontTagView',
'PHUILinkView' => 'AphrontTagView',
'PHUIListExample' => 'PhabricatorUIExample', 'PHUIListExample' => 'PhabricatorUIExample',
'PHUIListItemView' => 'AphrontTagView', 'PHUIListItemView' => 'AphrontTagView',
'PHUIListView' => 'AphrontTagView', 'PHUIListView' => 'AphrontTagView',
@ -9131,21 +9138,20 @@ phutil_register_library_map(array(
'PhabricatorConduitTokenQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorConduitTokenQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorConduitTokenTerminateController' => 'PhabricatorConduitController', 'PhabricatorConduitTokenTerminateController' => 'PhabricatorConduitController',
'PhabricatorConduitTokensSettingsPanel' => 'PhabricatorSettingsPanel', 'PhabricatorConduitTokensSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorConfigAllController' => 'PhabricatorConfigController',
'PhabricatorConfigApplication' => 'PhabricatorApplication', 'PhabricatorConfigApplication' => 'PhabricatorApplication',
'PhabricatorConfigApplicationController' => 'PhabricatorConfigController', 'PhabricatorConfigCacheController' => 'PhabricatorConfigServicesController',
'PhabricatorConfigCacheController' => 'PhabricatorConfigController', 'PhabricatorConfigClusterDatabasesController' => 'PhabricatorConfigServicesController',
'PhabricatorConfigClusterDatabasesController' => 'PhabricatorConfigController', 'PhabricatorConfigClusterNotificationsController' => 'PhabricatorConfigServicesController',
'PhabricatorConfigClusterNotificationsController' => 'PhabricatorConfigController', 'PhabricatorConfigClusterRepositoriesController' => 'PhabricatorConfigServicesController',
'PhabricatorConfigClusterRepositoriesController' => 'PhabricatorConfigController', 'PhabricatorConfigClusterSearchController' => 'PhabricatorConfigServicesController',
'PhabricatorConfigClusterSearchController' => 'PhabricatorConfigController',
'PhabricatorConfigCollectorsModule' => 'PhabricatorConfigModule', 'PhabricatorConfigCollectorsModule' => 'PhabricatorConfigModule',
'PhabricatorConfigColumnSchema' => 'PhabricatorConfigStorageSchema', 'PhabricatorConfigColumnSchema' => 'PhabricatorConfigStorageSchema',
'PhabricatorConfigConfigPHIDType' => 'PhabricatorPHIDType', 'PhabricatorConfigConfigPHIDType' => 'PhabricatorPHIDType',
'PhabricatorConfigConsoleController' => 'PhabricatorConfigController',
'PhabricatorConfigConstants' => 'Phobject', 'PhabricatorConfigConstants' => 'Phobject',
'PhabricatorConfigController' => 'PhabricatorController', 'PhabricatorConfigController' => 'PhabricatorController',
'PhabricatorConfigCoreSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'PhabricatorConfigCoreSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'PhabricatorConfigDatabaseController' => 'PhabricatorConfigController', 'PhabricatorConfigDatabaseController' => 'PhabricatorConfigServicesController',
'PhabricatorConfigDatabaseIssueController' => 'PhabricatorConfigDatabaseController', 'PhabricatorConfigDatabaseIssueController' => 'PhabricatorConfigDatabaseController',
'PhabricatorConfigDatabaseSchema' => 'PhabricatorConfigStorageSchema', 'PhabricatorConfigDatabaseSchema' => 'PhabricatorConfigStorageSchema',
'PhabricatorConfigDatabaseSource' => 'PhabricatorConfigProxySource', 'PhabricatorConfigDatabaseSource' => 'PhabricatorConfigProxySource',
@ -9153,7 +9159,7 @@ phutil_register_library_map(array(
'PhabricatorConfigDefaultSource' => 'PhabricatorConfigProxySource', 'PhabricatorConfigDefaultSource' => 'PhabricatorConfigProxySource',
'PhabricatorConfigDictionarySource' => 'PhabricatorConfigSource', 'PhabricatorConfigDictionarySource' => 'PhabricatorConfigSource',
'PhabricatorConfigEdgeModule' => 'PhabricatorConfigModule', 'PhabricatorConfigEdgeModule' => 'PhabricatorConfigModule',
'PhabricatorConfigEditController' => 'PhabricatorConfigController', 'PhabricatorConfigEditController' => 'PhabricatorConfigSettingsController',
'PhabricatorConfigEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorConfigEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorConfigEntry' => array( 'PhabricatorConfigEntry' => array(
'PhabricatorConfigEntryDAO', 'PhabricatorConfigEntryDAO',
@ -9164,9 +9170,7 @@ phutil_register_library_map(array(
'PhabricatorConfigEntryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorConfigEntryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorConfigFileSource' => 'PhabricatorConfigProxySource', 'PhabricatorConfigFileSource' => 'PhabricatorConfigProxySource',
'PhabricatorConfigGroupConstants' => 'PhabricatorConfigConstants', 'PhabricatorConfigGroupConstants' => 'PhabricatorConfigConstants',
'PhabricatorConfigGroupController' => 'PhabricatorConfigController',
'PhabricatorConfigHTTPParameterTypesModule' => 'PhabricatorConfigModule', 'PhabricatorConfigHTTPParameterTypesModule' => 'PhabricatorConfigModule',
'PhabricatorConfigHistoryController' => 'PhabricatorConfigController',
'PhabricatorConfigIgnoreController' => 'PhabricatorConfigController', 'PhabricatorConfigIgnoreController' => 'PhabricatorConfigController',
'PhabricatorConfigIssueListController' => 'PhabricatorConfigController', 'PhabricatorConfigIssueListController' => 'PhabricatorConfigController',
'PhabricatorConfigIssuePanelController' => 'PhabricatorConfigController', 'PhabricatorConfigIssuePanelController' => 'PhabricatorConfigController',
@ -9174,7 +9178,6 @@ phutil_register_library_map(array(
'PhabricatorConfigJSON' => 'Phobject', 'PhabricatorConfigJSON' => 'Phobject',
'PhabricatorConfigJSONOptionType' => 'PhabricatorConfigOptionType', 'PhabricatorConfigJSONOptionType' => 'PhabricatorConfigOptionType',
'PhabricatorConfigKeySchema' => 'PhabricatorConfigStorageSchema', 'PhabricatorConfigKeySchema' => 'PhabricatorConfigStorageSchema',
'PhabricatorConfigListController' => 'PhabricatorConfigController',
'PhabricatorConfigLocalSource' => 'PhabricatorConfigProxySource', 'PhabricatorConfigLocalSource' => 'PhabricatorConfigProxySource',
'PhabricatorConfigManagementDeleteWorkflow' => 'PhabricatorConfigManagementWorkflow', 'PhabricatorConfigManagementDeleteWorkflow' => 'PhabricatorConfigManagementWorkflow',
'PhabricatorConfigManagementDoneWorkflow' => 'PhabricatorConfigManagementWorkflow', 'PhabricatorConfigManagementDoneWorkflow' => 'PhabricatorConfigManagementWorkflow',
@ -9198,6 +9201,10 @@ phutil_register_library_map(array(
'PhabricatorConfigSchemaQuery' => 'Phobject', 'PhabricatorConfigSchemaQuery' => 'Phobject',
'PhabricatorConfigSchemaSpec' => 'Phobject', 'PhabricatorConfigSchemaSpec' => 'Phobject',
'PhabricatorConfigServerSchema' => 'PhabricatorConfigStorageSchema', 'PhabricatorConfigServerSchema' => 'PhabricatorConfigStorageSchema',
'PhabricatorConfigServicesController' => 'PhabricatorConfigController',
'PhabricatorConfigSettingsController' => 'PhabricatorConfigController',
'PhabricatorConfigSettingsHistoryController' => 'PhabricatorConfigSettingsController',
'PhabricatorConfigSettingsListController' => 'PhabricatorConfigSettingsController',
'PhabricatorConfigSetupCheckModule' => 'PhabricatorConfigModule', 'PhabricatorConfigSetupCheckModule' => 'PhabricatorConfigModule',
'PhabricatorConfigSiteModule' => 'PhabricatorConfigModule', 'PhabricatorConfigSiteModule' => 'PhabricatorConfigModule',
'PhabricatorConfigSiteSource' => 'PhabricatorConfigProxySource', 'PhabricatorConfigSiteSource' => 'PhabricatorConfigProxySource',
@ -9209,7 +9216,6 @@ phutil_register_library_map(array(
'PhabricatorConfigTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorConfigTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorConfigType' => 'Phobject', 'PhabricatorConfigType' => 'Phobject',
'PhabricatorConfigValidationException' => 'Exception', 'PhabricatorConfigValidationException' => 'Exception',
'PhabricatorConfigVersionController' => 'PhabricatorConfigController',
'PhabricatorConpherenceApplication' => 'PhabricatorApplication', 'PhabricatorConpherenceApplication' => 'PhabricatorApplication',
'PhabricatorConpherenceColumnMinimizeSetting' => 'PhabricatorInternalSetting', 'PhabricatorConpherenceColumnMinimizeSetting' => 'PhabricatorInternalSetting',
'PhabricatorConpherenceColumnVisibleSetting' => 'PhabricatorInternalSetting', 'PhabricatorConpherenceColumnVisibleSetting' => 'PhabricatorInternalSetting',

View file

@ -10,13 +10,15 @@ final class AlmanacConsoleController extends AlmanacController {
$viewer = $request->getViewer(); $viewer = $request->getViewer();
$menu = id(new PHUIObjectItemListView()) $menu = id(new PHUIObjectItemListView())
->setUser($viewer); ->setViewer($viewer)
->setBig(true);
$menu->addItem( $menu->addItem(
id(new PHUIObjectItemView()) id(new PHUIObjectItemView())
->setHeader(pht('Devices')) ->setHeader(pht('Devices'))
->setHref($this->getApplicationURI('device/')) ->setHref($this->getApplicationURI('device/'))
->setImageIcon('fa-server') ->setImageIcon('fa-server')
->setClickable(true)
->addAttribute( ->addAttribute(
pht( pht(
'Create an inventory of physical and virtual hosts and '. 'Create an inventory of physical and virtual hosts and '.
@ -27,6 +29,7 @@ final class AlmanacConsoleController extends AlmanacController {
->setHeader(pht('Services')) ->setHeader(pht('Services'))
->setHref($this->getApplicationURI('service/')) ->setHref($this->getApplicationURI('service/'))
->setImageIcon('fa-plug') ->setImageIcon('fa-plug')
->setClickable(true)
->addAttribute( ->addAttribute(
pht( pht(
'Create and update services, and map them to interfaces on '. 'Create and update services, and map them to interfaces on '.
@ -37,6 +40,7 @@ final class AlmanacConsoleController extends AlmanacController {
->setHeader(pht('Networks')) ->setHeader(pht('Networks'))
->setHref($this->getApplicationURI('network/')) ->setHref($this->getApplicationURI('network/'))
->setImageIcon('fa-globe') ->setImageIcon('fa-globe')
->setClickable(true)
->addAttribute( ->addAttribute(
pht( pht(
'Manage public and private networks.'))); 'Manage public and private networks.')));
@ -46,6 +50,7 @@ final class AlmanacConsoleController extends AlmanacController {
->setHeader(pht('Namespaces')) ->setHeader(pht('Namespaces'))
->setHref($this->getApplicationURI('namespace/')) ->setHref($this->getApplicationURI('namespace/'))
->setImageIcon('fa-asterisk') ->setImageIcon('fa-asterisk')
->setClickable(true)
->addAttribute( ->addAttribute(
pht('Control who can create new named services and devices.'))); pht('Control who can create new named services and devices.')));
@ -57,6 +62,7 @@ final class AlmanacConsoleController extends AlmanacController {
->setHeader(pht('Documentation')) ->setHeader(pht('Documentation'))
->setHref($docs_uri) ->setHref($docs_uri)
->setImageIcon('fa-book') ->setImageIcon('fa-book')
->setClickable(true)
->addAttribute(pht('Browse documentation for Almanac.'))); ->addAttribute(pht('Browse documentation for Almanac.')));
$crumbs = $this->buildApplicationCrumbs(); $crumbs = $this->buildApplicationCrumbs();
@ -64,23 +70,20 @@ final class AlmanacConsoleController extends AlmanacController {
$crumbs->setBorder(true); $crumbs->setBorder(true);
$box = id(new PHUIObjectBoxView()) $box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Almanac Console'))
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
->setObjectList($menu); ->setObjectList($menu);
$header = id(new PHUIHeaderView()) $launcher_view = id(new PHUILauncherView())
->setHeader(pht('Almanac Console')) ->appendChild($box);
->setHeaderIcon('fa-server');
$view = id(new PHUITwoColumnView()) $view = id(new PHUITwoColumnView())
->setHeader($header) ->setFooter($launcher_view);
->setFooter(array(
$box,
));
return $this->newPage() return $this->newPage()
->setTitle(pht('Almanac Console')) ->setTitle(pht('Almanac Console'))
->setCrumbs($crumbs) ->setCrumbs($crumbs)
->appendChild($view); ->appendChild($view);
} }
} }

View file

@ -405,6 +405,31 @@ final class PhabricatorAuditEditor
$phid_map[] = $reverted_phids; $phid_map[] = $reverted_phids;
} }
// See T13463. Copy "related task" edges from the associated revision, if
// one exists.
$revision = DiffusionCommitRevisionQuery::loadRevisionForCommit(
$actor,
$object);
if ($revision) {
$task_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$revision->getPHID(),
DifferentialRevisionHasTaskEdgeType::EDGECONST);
$task_phids = array_fuse($task_phids);
if ($task_phids) {
$related_edge = DiffusionCommitHasTaskEdgeType::EDGECONST;
$result[] = id(new PhabricatorAuditTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $related_edge)
->setNewValue(array('+' => $task_phids));
}
// Mark these objects as unmentionable, since the explicit relationship
// is stronger and any mentions are redundant.
$phid_map[] = $task_phids;
}
$phid_map = array_mergev($phid_map); $phid_map = array_mergev($phid_map);
$this->addUnmentionablePHIDs($phid_map); $this->addUnmentionablePHIDs($phid_map);

View file

@ -51,13 +51,17 @@ final class PhutilGitHubAuthAdapter extends PhutilOAuthAuthAdapter {
protected function loadOAuthAccountData() { protected function loadOAuthAccountData() {
$uri = new PhutilURI('https://api.github.com/user'); $uri = new PhutilURI('https://api.github.com/user');
$uri->replaceQueryParam('access_token', $this->getAccessToken());
$future = new HTTPSFuture($uri); $future = new HTTPSFuture($uri);
// NOTE: GitHub requires a User-Agent string. // NOTE: GitHub requires a User-Agent string.
$future->addHeader('User-Agent', __CLASS__); $future->addHeader('User-Agent', __CLASS__);
// See T13485. Circa early 2020, GitHub has deprecated use of the
// "access_token" URI parameter.
$token_header = sprintf('token %s', $this->getAccessToken());
$future->addHeader('Authorization', $token_header);
list($body) = $future->resolvex(); list($body) = $future->resolvex();
try { try {

View file

@ -37,13 +37,12 @@ final class PhabricatorConfigApplication extends PhabricatorApplication {
public function getRoutes() { public function getRoutes() {
return array( return array(
'/config/' => array( '/config/' => array(
'' => 'PhabricatorConfigListController', '' => 'PhabricatorConfigConsoleController',
'application/' => 'PhabricatorConfigApplicationController', 'application/' => 'PhabricatorConfigApplicationController',
'all/' => 'PhabricatorConfigAllController', 'all/' => 'PhabricatorConfigAllController',
'history/' => 'PhabricatorConfigHistoryController', 'history/' => 'PhabricatorConfigHistoryController',
'edit/(?P<key>[\w\.\-]+)/' => 'PhabricatorConfigEditController', 'edit/(?P<key>[\w\.\-]+)/' => 'PhabricatorConfigEditController',
'group/(?P<key>[^/]+)/' => 'PhabricatorConfigGroupController', 'group/(?P<key>[^/]+)/' => 'PhabricatorConfigGroupController',
'version/' => 'PhabricatorConfigVersionController',
'database/'. 'database/'.
'(?:(?P<ref>[^/]+)/'. '(?:(?P<ref>[^/]+)/'.
'(?:(?P<database>[^/]+)/'. '(?:(?P<database>[^/]+)/'.
@ -63,7 +62,7 @@ final class PhabricatorConfigApplication extends PhabricatorApplication {
'purge/' => 'PhabricatorConfigPurgeCacheController', 'purge/' => 'PhabricatorConfigPurgeCacheController',
), ),
'module/' => array( 'module/' => array(
'(?P<module>[^/]+)/' => 'PhabricatorConfigModuleController', '(?:(?P<module>[^/]+)/)?' => 'PhabricatorConfigModuleController',
), ),
'cluster/' => array( 'cluster/' => array(
'databases/' => 'PhabricatorConfigClusterDatabasesController', 'databases/' => 'PhabricatorConfigClusterDatabasesController',
@ -71,6 +70,12 @@ final class PhabricatorConfigApplication extends PhabricatorApplication {
'repositories/' => 'PhabricatorConfigClusterRepositoriesController', 'repositories/' => 'PhabricatorConfigClusterRepositoriesController',
'search/' => 'PhabricatorConfigClusterSearchController', 'search/' => 'PhabricatorConfigClusterSearchController',
), ),
'settings/' => array(
'' => 'PhabricatorConfigSettingsListController',
'(?P<filter>advanced|all)/'
=> 'PhabricatorConfigSettingsListController',
'history/' => 'PhabricatorConfigSettingsHistoryController',
),
), ),
); );
} }

View file

@ -53,20 +53,23 @@ final class PhabricatorAuthSetupCheck extends PhabricatorSetupCheck {
"\n\n". "\n\n".
'Leaving your authentication provider configuration unlocked '. 'Leaving your authentication provider configuration unlocked '.
'increases the damage that a compromised administrator account can '. 'increases the damage that a compromised administrator account can '.
'do to your install, by, for example, changing the authentication '. 'do to your install. For example, an attacker who compromises an '.
'provider to a server they control and intercepting usernames and '. 'administrator account can change authentication providers to point '.
'at a server they control and attempt to intercept usernames and '.
'passwords.'. 'passwords.'.
"\n\n". "\n\n".
'To prevent this attack, you should configure your authentication '. 'To prevent this attack, you should configure authentication, and '.
'providers, and then lock the configuration by doing `%s` '. 'then lock the configuration by running "bin/auth lock" from the '.
'from the command line. This will prevent changing the '. 'command line. This will prevent changing the authentication config '.
'authentication provider config without first doing `%s`.', 'without first running "bin/auth unlock".');
'bin/auth lock',
'bin/auth unlock');
$this $this
->newIssue('auth.config-unlocked') ->newIssue('auth.config-unlocked')
->setShortName(pht('Auth Config Unlocked')) ->setShortName(pht('Auth Config Unlocked'))
->setName(pht('Authenticaton Provider Configuration Unlocked')) ->setName(pht('Authenticaton Configuration Unlocked'))
->setSummary(
pht(
'Authentication configuration is currently unlocked. Once you '.
'finish configuring authentication, you should lock it.'))
->setMessage($message) ->setMessage($message)
->addRelatedPhabricatorConfig('auth.lock-config') ->addRelatedPhabricatorConfig('auth.lock-config')
->addCommand( ->addCommand(

View file

@ -58,8 +58,7 @@ final class PhabricatorSecuritySetupCheck extends PhabricatorSetupCheck {
->setName(pht('Alternate File Domain Not Configured')) ->setName(pht('Alternate File Domain Not Configured'))
->setSummary( ->setSummary(
pht( pht(
'Increase security (and improve performance) by configuring '. 'Improve security by configuring an alternate file domain.'))
'a CDN or alternate file domain.'))
->setMessage( ->setMessage(
pht( pht(
'Phabricator is currently configured to serve user uploads '. 'Phabricator is currently configured to serve user uploads '.

View file

@ -1,77 +0,0 @@
<?php
final class PhabricatorConfigAllController
extends PhabricatorConfigController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$db_values = id(new PhabricatorConfigEntry())
->loadAllWhere('namespace = %s', 'default');
$db_values = mpull($db_values, null, 'getConfigKey');
$rows = array();
$options = PhabricatorApplicationConfigOptions::loadAllOptions();
ksort($options);
foreach ($options as $option) {
$key = $option->getKey();
if ($option->getHidden()) {
$value = phutil_tag('em', array(), pht('Hidden'));
} else {
$value = PhabricatorEnv::getEnvConfig($key);
$value = PhabricatorConfigJSON::prettyPrintJSON($value);
}
$db_value = idx($db_values, $key);
$rows[] = array(
phutil_tag(
'a',
array(
'href' => $this->getApplicationURI('edit/'.$key.'/'),
),
$key),
$value,
$db_value && !$db_value->getIsDeleted() ? pht('Customized') : '',
);
}
$table = id(new AphrontTableView($rows))
->setColumnClasses(
array(
'',
'wide',
))
->setHeaders(
array(
pht('Key'),
pht('Value'),
pht('Customized'),
));
$title = pht('Current Settings');
$header = $this->buildHeaderView($title);
$nav = $this->buildSideNavView();
$nav->selectFilter('all/');
$view = $this->buildConfigBoxView(
pht('All Settings'),
$table);
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb($title)
->setBorder(true);
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter($view);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}
}

View file

@ -1,57 +0,0 @@
<?php
final class PhabricatorConfigApplicationController
extends PhabricatorConfigController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$nav = $this->buildSideNavView();
$nav->selectFilter('application/');
$groups = PhabricatorApplicationConfigOptions::loadAll();
$apps_list = $this->buildConfigOptionsList($groups, 'apps');
$apps_list = $this->buildConfigBoxView(pht('Applications'), $apps_list);
$title = pht('Application Settings');
$header = $this->buildHeaderView($title);
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter($apps_list);
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb($title)
->setBorder(true);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}
private function buildConfigOptionsList(array $groups, $type) {
assert_instances_of($groups, 'PhabricatorApplicationConfigOptions');
$list = new PHUIObjectItemListView();
$list->setBig(true);
$groups = msort($groups, 'getName');
foreach ($groups as $group) {
if ($group->getGroup() == $type) {
$icon = id(new PHUIIconView())
->setIcon($group->getIcon())
->setBackground('bg-violet');
$item = id(new PHUIObjectItemView())
->setHeader($group->getName())
->setHref('/config/group/'.$group->getKey().'/')
->addAttribute($group->getDescription())
->setImageIcon($icon);
$list->addItem($item);
}
}
return $list;
}
}

View file

@ -1,92 +1,139 @@
<?php <?php
final class PhabricatorConfigVersionController final class PhabricatorConfigConsoleController
extends PhabricatorConfigController { extends PhabricatorConfigController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer(); $viewer = $request->getViewer();
$title = pht('Version Information'); $menu = id(new PHUIObjectItemListView())
$versions = $this->renderModuleStatus($viewer); ->setViewer($viewer)
->setBig(true);
$nav = $this->buildSideNavView(); $menu->addItem(
$nav->selectFilter('version/'); id(new PHUIObjectItemView())
$header = $this->buildHeaderView($title); ->setHeader(pht('Settings'))
->setHref($this->getApplicationURI('settings/'))
->setImageIcon('fa-wrench')
->setClickable(true)
->addAttribute(
pht(
'Review and modify configuration settings.')));
$view = $this->buildConfigBoxView( $menu->addItem(
pht('Installed Versions'), id(new PHUIObjectItemView())
$versions); ->setHeader(pht('Setup Issues'))
->setHref($this->getApplicationURI('issue/'))
->setImageIcon('fa-exclamation-triangle')
->setClickable(true)
->addAttribute(
pht(
'Show unresolved issues with setup and configuration.')));
$menu->addItem(
id(new PHUIObjectItemView())
->setHeader(pht('Services'))
->setHref($this->getApplicationURI('cluster/databases/'))
->setImageIcon('fa-server')
->setClickable(true)
->addAttribute(
pht(
'View status information for databases, caches, repositories, '.
'and other services.')));
$menu->addItem(
id(new PHUIObjectItemView())
->setHeader(pht('Extensions/Modules'))
->setHref($this->getApplicationURI('module/'))
->setImageIcon('fa-gear')
->setClickable(true)
->addAttribute(
pht(
'Show installed extensions and modules.')));
$crumbs = $this->buildApplicationCrumbs() $crumbs = $this->buildApplicationCrumbs()
->addTextCrumb($title) ->addTextCrumb(pht('Console'))
->setBorder(true); ->setBorder(true);
$content = id(new PHUITwoColumnView()) $box = id(new PHUIObjectBoxView())
->setHeader($header) ->setHeaderText(pht('Phabricator Configuation'))
->setFooter($view); ->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
->setObjectList($menu);
$versions = $this->newLibraryVersionTable($viewer);
$binary_versions = $this->newBinaryVersionTable();
$launcher_view = id(new PHUILauncherView())
->appendChild($box)
->appendChild($versions)
->appendChild($binary_versions);
$view = id(new PHUITwoColumnView())
->setFooter($launcher_view);
return $this->newPage() return $this->newPage()
->setTitle($title) ->setTitle(pht('Phabricator Configuation'))
->setCrumbs($crumbs) ->setCrumbs($crumbs)
->setNavigation($nav) ->appendChild($view);
->appendChild($content);
} }
public function renderModuleStatus($viewer) { public function newLibraryVersionTable() {
$viewer = $this->getViewer();
$versions = $this->loadVersions($viewer); $versions = $this->loadVersions($viewer);
$version_property_list = id(new PHUIPropertyListView()); $rows = array();
foreach ($versions as $name => $info) { foreach ($versions as $name => $info) {
$version = $info['version']; $branchpoint = $info['branchpoint'];
if (strlen($branchpoint)) {
$branchpoint = substr($branchpoint, 0, 12);
} else {
$branchpoint = null;
}
if ($info['branchpoint']) { $version = $info['hash'];
$display = pht( if (strlen($version)) {
'%s (branched from %s on %s)', $version = substr($version, 0, 12);
} else {
$version = pht('Unknown');
}
$epoch = $info['epoch'];
if ($epoch) {
$epoch = phabricator_date($epoch, $viewer);
} else {
$epoch = null;
}
$rows[] = array(
$name,
$version, $version,
$info['branchpoint'], $epoch,
$info['upstream']); $branchpoint,
} else { );
$display = $version;
} }
$version_property_list->addProperty($name, $display); $table_view = id(new AphrontTableView($rows))
} ->setHeaders(
array(
pht('Library'),
pht('Version'),
pht('Date'),
pht('Branchpoint'),
))
->setColumnClasses(
array(
'pri',
null,
null,
'wide',
));
$phabricator_root = dirname(phutil_get_library_root('phabricator')); return id(new PHUIObjectBoxView())
$version_path = $phabricator_root.'/conf/local/VERSION'; ->setHeaderText(pht('Phabricator Version Information'))
if (Filesystem::pathExists($version_path)) { ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
$version_from_file = Filesystem::readFile($version_path); ->appendChild($table_view);
$version_property_list->addProperty(
pht('Local Version'),
$version_from_file);
}
$version_property_list->addProperty('php', phpversion());
$binaries = PhutilBinaryAnalyzer::getAllBinaries();
foreach ($binaries as $binary) {
if (!$binary->isBinaryAvailable()) {
$binary_info = pht('Not Available');
} else {
$version = $binary->getBinaryVersion();
$path = $binary->getBinaryPath();
if ($path === null && $version === null) {
$binary_info = pht('-');
} else if ($path === null) {
$binary_info = $version;
} else if ($version === null) {
$binary_info = pht('- at %s', $path);
} else {
$binary_info = pht('%s at %s', $version, $path);
}
}
$version_property_list->addProperty(
$binary->getBinaryName(),
$binary_info);
}
return $version_property_list;
} }
private function loadVersions(PhabricatorUser $viewer) { private function loadVersions(PhabricatorUser $viewer) {
@ -207,13 +254,14 @@ final class PhabricatorConfigVersionController
list($err, $stdout) = $future->resolve(); list($err, $stdout) = $future->resolve();
if (!$err) { if (!$err) {
list($hash, $epoch) = explode(' ', $stdout); list($hash, $epoch) = explode(' ', $stdout);
$version = pht('%s (%s)', $hash, phabricator_date($epoch, $viewer));
} else { } else {
$version = pht('Unknown'); $hash = null;
$epoch = null;
} }
$result = array( $result = array(
'version' => $version, 'hash' => $hash,
'epoch' => $epoch,
'upstream' => null, 'upstream' => null,
'branchpoint' => null, 'branchpoint' => null,
); );
@ -239,4 +287,51 @@ final class PhabricatorConfigVersionController
return $results; return $results;
} }
private function newBinaryVersionTable() {
$rows = array();
$rows[] = array(
'php',
phpversion(),
php_sapi_name(),
);
$binaries = PhutilBinaryAnalyzer::getAllBinaries();
foreach ($binaries as $binary) {
if (!$binary->isBinaryAvailable()) {
$binary_version = pht('Not Available');
$binary_path = null;
} else {
$binary_version = $binary->getBinaryVersion();
$binary_path = $binary->getBinaryPath();
}
$rows[] = array(
$binary->getBinaryName(),
$binary_version,
$binary_path,
);
}
$table_view = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Binary'),
pht('Version'),
pht('Path'),
))
->setColumnClasses(
array(
'pri',
null,
'wide',
));
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Other Version Information'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($table_view);
}
} }

View file

@ -6,57 +6,6 @@ abstract class PhabricatorConfigController extends PhabricatorController {
return true; return true;
} }
public function buildSideNavView($filter = null, $for_app = false) {
$guide_href = new PhutilURI('/guides/');
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
$nav->addFilter('/',
pht('Core Settings'), null, 'fa-gear');
$nav->addFilter('application/',
pht('Application Settings'), null, 'fa-globe');
$nav->addFilter('history/',
pht('Settings History'), null, 'fa-history');
$nav->addFilter('version/',
pht('Version Information'), null, 'fa-download');
$nav->addFilter('all/',
pht('All Settings'), null, 'fa-list-ul');
$nav->addLabel(pht('Setup'));
$nav->addFilter('issue/',
pht('Setup Issues'), null, 'fa-warning');
$nav->addFilter(null,
pht('Installation Guide'), $guide_href, 'fa-book');
$nav->addLabel(pht('Database'));
$nav->addFilter('database/',
pht('Database Status'), null, 'fa-heartbeat');
$nav->addFilter('dbissue/',
pht('Database Issues'), null, 'fa-exclamation-circle');
$nav->addLabel(pht('Cache'));
$nav->addFilter('cache/',
pht('Cache Status'), null, 'fa-home');
$nav->addLabel(pht('Cluster'));
$nav->addFilter('cluster/databases/',
pht('Database Servers'), null, 'fa-database');
$nav->addFilter('cluster/notifications/',
pht('Notification Servers'), null, 'fa-bell-o');
$nav->addFilter('cluster/repositories/',
pht('Repository Servers'), null, 'fa-code');
$nav->addFilter('cluster/search/',
pht('Search Servers'), null, 'fa-search');
$nav->addLabel(pht('Modules'));
$modules = PhabricatorConfigModule::getAllModules();
foreach ($modules as $key => $module) {
$nav->addFilter('module/'.$key.'/',
$module->getModuleName(), null, 'fa-puzzle-piece');
}
return $nav;
}
public function buildApplicationMenu() {
return $this->buildSideNavView(null, true)->getMenu();
}
public function buildHeaderView($text, $action = null) { public function buildHeaderView($text, $action = null) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();

View file

@ -1,112 +0,0 @@
<?php
final class PhabricatorConfigGroupController
extends PhabricatorConfigController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$group_key = $request->getURIData('key');
$groups = PhabricatorApplicationConfigOptions::loadAll();
$options = idx($groups, $group_key);
if (!$options) {
return new Aphront404Response();
}
$group_uri = PhabricatorConfigGroupConstants::getGroupFullURI(
$options->getGroup());
$group_name = PhabricatorConfigGroupConstants::getGroupShortName(
$options->getGroup());
$nav = $this->buildSideNavView();
$nav->selectFilter($group_uri);
$title = pht('%s Configuration', $options->getName());
$header = $this->buildHeaderView($title);
$list = $this->buildOptionList($options->getOptions());
$group_url = phutil_tag('a', array('href' => $group_uri), $group_name);
$box_header = pht("%s \xC2\xBB %s", $group_url, $options->getName());
$view = $this->buildConfigBoxView($box_header, $list);
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb($group_name, $group_uri)
->addTextCrumb($options->getName())
->setBorder(true);
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter($view);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}
private function buildOptionList(array $options) {
assert_instances_of($options, 'PhabricatorConfigOption');
require_celerity_resource('config-options-css');
$db_values = array();
if ($options) {
$db_values = id(new PhabricatorConfigEntry())->loadAllWhere(
'configKey IN (%Ls) AND namespace = %s',
mpull($options, 'getKey'),
'default');
$db_values = mpull($db_values, null, 'getConfigKey');
}
$list = new PHUIObjectItemListView();
$list->setBig(true);
foreach ($options as $option) {
$summary = $option->getSummary();
$item = id(new PHUIObjectItemView())
->setHeader($option->getKey())
->setHref('/config/edit/'.$option->getKey().'/')
->addAttribute($summary);
$color = null;
$db_value = idx($db_values, $option->getKey());
if ($db_value && !$db_value->getIsDeleted()) {
$item->setEffect('visited');
$color = 'violet';
}
if ($option->getHidden()) {
$item->setStatusIcon('fa-eye-slash grey', pht('Hidden'));
$item->setDisabled(true);
} else if ($option->getLocked()) {
$item->setStatusIcon('fa-lock '.$color, pht('Locked'));
} else if ($color) {
$item->setStatusIcon('fa-pencil '.$color, pht('Editable'));
} else {
$item->setStatusIcon('fa-pencil-square-o '.$color, pht('Editable'));
}
if (!$option->getHidden()) {
$current_value = PhabricatorEnv::getEnvConfig($option->getKey());
$current_value = PhabricatorConfigJSON::prettyPrintJSON(
$current_value);
$current_value = phutil_tag(
'div',
array(
'class' => 'config-options-current-value '.$color,
),
array(
$current_value,
));
$item->setSideColumn($current_value);
}
$list->addItem($item);
}
return $list;
}
}

View file

@ -1,57 +0,0 @@
<?php
final class PhabricatorConfigListController
extends PhabricatorConfigController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$nav = $this->buildSideNavView();
$nav->selectFilter('/');
$groups = PhabricatorApplicationConfigOptions::loadAll();
$core_list = $this->buildConfigOptionsList($groups, 'core');
$core_list = $this->buildConfigBoxView(pht('Core'), $core_list);
$title = pht('Core Settings');
$header = $this->buildHeaderView($title);
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb($title)
->setBorder(true);
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter($core_list);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}
private function buildConfigOptionsList(array $groups, $type) {
assert_instances_of($groups, 'PhabricatorApplicationConfigOptions');
$list = new PHUIObjectItemListView();
$list->setBig(true);
$groups = msort($groups, 'getName');
foreach ($groups as $group) {
if ($group->getGroup() == $type) {
$icon = id(new PHUIIconView())
->setIcon($group->getIcon())
->setBackground('bg-blue');
$item = id(new PHUIObjectItemView())
->setHeader($group->getName())
->setHref('/config/group/'.$group->getKey().'/')
->addAttribute($group->getDescription())
->setImageIcon($icon);
$list->addItem($item);
}
}
return $list;
}
}

View file

@ -6,9 +6,6 @@ final class PhabricatorConfigIssueListController
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer(); $viewer = $request->getViewer();
$nav = $this->buildSideNavView();
$nav->selectFilter('issue/');
$engine = new PhabricatorSetupEngine(); $engine = new PhabricatorSetupEngine();
$response = $engine->execute(); $response = $engine->execute();
if ($response) { if ($response) {
@ -34,7 +31,6 @@ final class PhabricatorConfigIssueListController
'fa-question-circle'); 'fa-question-circle');
$title = pht('Setup Issues'); $title = pht('Setup Issues');
$header = $this->buildHeaderView($title);
if (!$issues) { if (!$issues) {
$issue_list = id(new PHUIInfoView()) $issue_list = id(new PHUIInfoView())
@ -50,21 +46,24 @@ final class PhabricatorConfigIssueListController
$other, $other,
); );
$issue_list = $this->buildConfigBoxView(pht('Issues'), $issue_list); $issue_list = $this->buildConfigBoxView(
pht('Unresolved Setup Issues'),
$issue_list);
} }
$crumbs = $this->buildApplicationCrumbs() $crumbs = $this->buildApplicationCrumbs()
->addTextCrumb($title) ->addTextCrumb($title)
->setBorder(true); ->setBorder(true);
$launcher_view = id(new PHUILauncherView())
->appendChild($issue_list);
$content = id(new PHUITwoColumnView()) $content = id(new PHUITwoColumnView())
->setHeader($header) ->setFooter($launcher_view);
->setFooter($issue_list);
return $this->newPage() return $this->newPage()
->setTitle($title) ->setTitle($title)
->setCrumbs($crumbs) ->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content); ->appendChild($content);
} }
@ -76,12 +75,16 @@ final class PhabricatorConfigIssueListController
$items = 0; $items = 0;
foreach ($issues as $issue) { foreach ($issues as $issue) {
if ($issue->getGroup() == $group) { if ($issue->getGroup() != $group) {
continue;
}
$items++; $items++;
$href = $this->getApplicationURI('/issue/'.$issue->getIssueKey().'/'); $href = $this->getApplicationURI('/issue/'.$issue->getIssueKey().'/');
$item = id(new PHUIObjectItemView()) $item = id(new PHUIObjectItemView())
->setHeader($issue->getName()) ->setHeader($issue->getName())
->setHref($href) ->setHref($href)
->setClickable(true)
->addAttribute($issue->getSummary()); ->addAttribute($issue->getSummary());
if (!$issue->getIsIgnored()) { if (!$issue->getIsIgnored()) {
$icon = id(new PHUIIconView()) $icon = id(new PHUIIconView())
@ -98,7 +101,6 @@ final class PhabricatorConfigIssueListController
$ignored_items[] = $item; $ignored_items[] = $item;
} }
} }
}
foreach ($ignored_items as $item) { foreach ($ignored_items as $item) {
$list->addItem($item); $list->addItem($item);

View file

@ -14,9 +14,6 @@ final class PhabricatorConfigIssueViewController
} }
$issues = $engine->getIssues(); $issues = $engine->getIssues();
$nav = $this->buildSideNavView();
$nav->selectFilter('issue/');
if (empty($issues[$issue_key])) { if (empty($issues[$issue_key])) {
$content = id(new PHUIInfoView()) $content = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE) ->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
@ -36,23 +33,21 @@ final class PhabricatorConfigIssueViewController
$title = $issue->getShortName(); $title = $issue->getShortName();
} }
$header = $this->buildHeaderView($title);
$crumbs = $this $crumbs = $this
->buildApplicationCrumbs() ->buildApplicationCrumbs()
->setBorder(true)
->addTextCrumb(pht('Setup Issues'), $this->getApplicationURI('issue/')) ->addTextCrumb(pht('Setup Issues'), $this->getApplicationURI('issue/'))
->addTextCrumb($title, $request->getRequestURI()) ->addTextCrumb($title, $request->getRequestURI())
->setBorder(true); ->setBorder(true);
$launcher_view = id(new PHUILauncherView())
->appendChild($content);
$content = id(new PHUITwoColumnView()) $content = id(new PHUITwoColumnView())
->setHeader($header) ->setFooter($launcher_view);
->setFooter($content);
return $this->newPage() return $this->newPage()
->setTitle($title) ->setTitle($title)
->setCrumbs($crumbs) ->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content); ->appendChild($content);
} }

View file

@ -8,6 +8,11 @@ final class PhabricatorConfigModuleController
$key = $request->getURIData('module'); $key = $request->getURIData('module');
$all_modules = PhabricatorConfigModule::getAllModules(); $all_modules = PhabricatorConfigModule::getAllModules();
if (!strlen($key)) {
$key = head_key($all_modules);
}
if (empty($all_modules[$key])) { if (empty($all_modules[$key])) {
return new Aphront404Response(); return new Aphront404Response();
} }
@ -16,13 +21,27 @@ final class PhabricatorConfigModuleController
$content = $module->renderModuleStatus($request); $content = $module->renderModuleStatus($request);
$title = $module->getModuleName(); $title = $module->getModuleName();
$nav = $this->buildSideNavView(); $nav = new AphrontSideNavFilterView();
$nav->selectFilter('module/'.$key.'/'); $nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
$modules_uri = $this->getApplicationURI('module/');
$modules = PhabricatorConfigModule::getAllModules();
foreach ($modules as $module_key => $module) {
$nav->newLink($module_key)
->setName($module->getModuleName())
->setHref(urisprintf('%s%s/', $modules_uri, $module_key))
->setIcon('fa-puzzle-piece');
}
$nav->selectFilter($key);
$header = $this->buildHeaderView($title); $header = $this->buildHeaderView($title);
$view = $this->buildConfigBoxView($title, $content); $view = $this->buildConfigBoxView($title, $content);
$crumbs = $this->buildApplicationCrumbs() $crumbs = $this->buildApplicationCrumbs()
->addTextCrumb(pht('Extensions/Modules'), $modules_uri)
->addTextCrumb($title) ->addTextCrumb($title)
->setBorder(true); ->setBorder(true);

View file

@ -1,13 +1,11 @@
<?php <?php
final class PhabricatorConfigCacheController final class PhabricatorConfigCacheController
extends PhabricatorConfigController { extends PhabricatorConfigServicesController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();
$nav = $this->buildSideNavView();
$nav->selectFilter('cache/');
$purge_button = id(new PHUIButtonView()) $purge_button = id(new PHUIButtonView())
->setText(pht('Purge Caches')) ->setText(pht('Purge Caches'))
@ -27,14 +25,15 @@ final class PhabricatorConfigCacheController
$data_box, $data_box,
); );
$crumbs = $this->buildApplicationCrumbs() $crumbs = $this->newCrumbs()
->addTextCrumb($title) ->addTextCrumb($title);
->setBorder(true);
$content = id(new PHUITwoColumnView()) $content = id(new PHUITwoColumnView())
->setHeader($header) ->setHeader($header)
->setFooter($page); ->setFooter($page);
$nav = $this->newNavigation('cache');
return $this->newPage() return $this->newPage()
->setTitle($title) ->setTitle($title)
->setCrumbs($crumbs) ->setCrumbs($crumbs)
@ -92,10 +91,12 @@ final class PhabricatorConfigCacheController
'n', 'n',
'n', 'n',
)); ));
$table = $this->buildConfigBoxView(pht('Cache Storage'), $table);
} }
$properties = $this->buildConfigBoxView(pht('Data Cache'), $properties); $properties = $this->buildConfigBoxView(pht('Data Cache'), $properties);
$table = $this->buildConfigBoxView(pht('Cache Storage'), $table);
return array($properties, $table); return array($properties, $table);
} }

View file

@ -1,13 +1,12 @@
<?php <?php
final class PhabricatorConfigClusterDatabasesController final class PhabricatorConfigClusterDatabasesController
extends PhabricatorConfigController { extends PhabricatorConfigServicesController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$nav = $this->buildSideNavView(); $nav = $this->newNavigation('database-servers');
$nav->selectFilter('cluster/databases/');
$title = pht('Cluster Database Status'); $title = pht('Database Servers');
$doc_href = PhabricatorEnv::getDoclink('Cluster: Databases'); $doc_href = PhabricatorEnv::getDoclink('Cluster: Databases');
$button = id(new PHUIButtonView()) $button = id(new PHUIButtonView())
->setIcon('fa-book') ->setIcon('fa-book')
@ -20,9 +19,8 @@ final class PhabricatorConfigClusterDatabasesController
$database_status = $this->buildClusterDatabaseStatus(); $database_status = $this->buildClusterDatabaseStatus();
$status = $this->buildConfigBoxView(pht('Status'), $database_status); $status = $this->buildConfigBoxView(pht('Status'), $database_status);
$crumbs = $this->buildApplicationCrumbs() $crumbs = $this->newCrumbs()
->addTextCrumb($title) ->addTextCrumb($title);
->setBorder(true);
$content = id(new PHUITwoColumnView()) $content = id(new PHUITwoColumnView())
->setHeader($header) ->setHeader($header)

View file

@ -1,13 +1,10 @@
<?php <?php
final class PhabricatorConfigClusterNotificationsController final class PhabricatorConfigClusterNotificationsController
extends PhabricatorConfigController { extends PhabricatorConfigServicesController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$nav = $this->buildSideNavView(); $title = pht('Notification Servers');
$nav->selectFilter('cluster/notifications/');
$title = pht('Cluster Notifications');
$doc_href = PhabricatorEnv::getDoclink('Cluster: Notifications'); $doc_href = PhabricatorEnv::getDoclink('Cluster: Notifications');
$button = id(new PHUIButtonView()) $button = id(new PHUIButtonView())
->setIcon('fa-book') ->setIcon('fa-book')
@ -22,14 +19,15 @@ final class PhabricatorConfigClusterNotificationsController
pht('Notifications Status'), pht('Notifications Status'),
$notification_status); $notification_status);
$crumbs = $this->buildApplicationCrumbs() $crumbs = $this->newCrumbs()
->addTextCrumb($title) ->addTextCrumb($title);
->setBorder(true);
$content = id(new PHUITwoColumnView()) $content = id(new PHUITwoColumnView())
->setHeader($header) ->setHeader($header)
->setFooter($status); ->setFooter($status);
$nav = $this->newNavigation('notification-servers');
return $this->newPage() return $this->newPage()
->setTitle($title) ->setTitle($title)
->setCrumbs($crumbs) ->setCrumbs($crumbs)

View file

@ -1,13 +1,10 @@
<?php <?php
final class PhabricatorConfigClusterRepositoriesController final class PhabricatorConfigClusterRepositoriesController
extends PhabricatorConfigController { extends PhabricatorConfigServicesController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$nav = $this->buildSideNavView(); $title = pht('Repository Services');
$nav->selectFilter('cluster/repositories/');
$title = pht('Cluster Repository Status');
$doc_href = PhabricatorEnv::getDoclink('Cluster: Repositories'); $doc_href = PhabricatorEnv::getDoclink('Cluster: Repositories');
$button = id(new PHUIButtonView()) $button = id(new PHUIButtonView())
@ -26,9 +23,8 @@ final class PhabricatorConfigClusterRepositoriesController
$repo_errors = $this->buildConfigBoxView( $repo_errors = $this->buildConfigBoxView(
pht('Repository Errors'), $repository_errors); pht('Repository Errors'), $repository_errors);
$crumbs = $this->buildApplicationCrumbs() $crumbs = $this->newCrumbs()
->addTextCrumb($title) ->addTextCrumb($title);
->setBorder(true);
$content = id(new PHUITwoColumnView()) $content = id(new PHUITwoColumnView())
->setHeader($header) ->setHeader($header)
@ -38,6 +34,8 @@ final class PhabricatorConfigClusterRepositoriesController
$repo_errors, $repo_errors,
)); ));
$nav = $this->newNavigation('repository-servers');
return $this->newPage() return $this->newPage()
->setTitle($title) ->setTitle($title)
->setCrumbs($crumbs) ->setCrumbs($crumbs)

View file

@ -1,13 +1,10 @@
<?php <?php
final class PhabricatorConfigClusterSearchController final class PhabricatorConfigClusterSearchController
extends PhabricatorConfigController { extends PhabricatorConfigServicesController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$nav = $this->buildSideNavView(); $title = pht('Search Servers');
$nav->selectFilter('cluster/search/');
$title = pht('Cluster Search');
$doc_href = PhabricatorEnv::getDoclink('Cluster: Search'); $doc_href = PhabricatorEnv::getDoclink('Cluster: Search');
$button = id(new PHUIButtonView()) $button = id(new PHUIButtonView())
@ -20,14 +17,15 @@ final class PhabricatorConfigClusterSearchController
$search_status = $this->buildClusterSearchStatus(); $search_status = $this->buildClusterSearchStatus();
$crumbs = $this->buildApplicationCrumbs() $crumbs = $this->newCrumbs()
->addTextCrumb($title) ->addTextCrumb($title);
->setBorder(true);
$content = id(new PHUITwoColumnView()) $content = id(new PHUITwoColumnView())
->setHeader($header) ->setHeader($header)
->setFooter($search_status); ->setFooter($search_status);
$nav = $this->newNavigation('search-servers');
return $this->newPage() return $this->newPage()
->setTitle($title) ->setTitle($title)
->setCrumbs($crumbs) ->setCrumbs($crumbs)

View file

@ -1,7 +1,7 @@
<?php <?php
abstract class PhabricatorConfigDatabaseController abstract class PhabricatorConfigDatabaseController
extends PhabricatorConfigController { extends PhabricatorConfigServicesController {
protected function renderIcon($status) { protected function renderIcon($status) {
switch ($status) { switch ($status) {

View file

@ -153,15 +153,14 @@ final class PhabricatorConfigDatabaseIssueController
new PhutilNumber($counts[PhabricatorConfigStorageSchema::STATUS_WARN])); new PhutilNumber($counts[PhabricatorConfigStorageSchema::STATUS_WARN]));
} }
$title = pht('Database Issues'); $title = pht('Schemata Issues');
$header = $this->buildHeaderView($title); $header = $this->buildHeaderView($title);
$nav = $this->buildSideNavView(); $nav = $this->newNavigation('schemata-issues');
$nav->selectFilter('dbissue/');
$view = $this->buildConfigBoxView(pht('Issues'), $table); $view = $this->buildConfigBoxView(pht('Issues'), $table);
$crumbs = $this->buildApplicationCrumbs() $crumbs = $this->newCrumbs()
->addTextCrumb($title) ->addTextCrumb($title)
->setBorder(true); ->setBorder(true);

View file

@ -71,8 +71,7 @@ final class PhabricatorConfigDatabaseStatusController
} }
private function buildResponse($title, $body) { private function buildResponse($title, $body) {
$nav = $this->buildSideNavView(); $nav = $this->newNavigation('schemata');
$nav->selectFilter('database/');
if (!$title) { if (!$title) {
$title = pht('Database Status'); $title = pht('Database Status');
@ -118,8 +117,7 @@ final class PhabricatorConfigDatabaseStatusController
); );
} }
$crumbs = $this->buildApplicationCrumbs(); $crumbs = $this->newCrumbs();
$crumbs->setBorder(true);
$last_key = last_key($links); $last_key = last_key($links);
foreach ($links as $link_key => $link) { foreach ($links as $link_key => $link) {

View file

@ -0,0 +1,69 @@
<?php
abstract class PhabricatorConfigServicesController
extends PhabricatorConfigController {
public function newNavigation($select_filter) {
$services_uri = $this->getApplicationURI();
$nav = id(new AphrontSideNavFilterView())
->setBaseURI(new PhutilURI($services_uri));
$nav->addLabel(pht('Databases'));
$nav->newLink('database-servers')
->setName(pht('Database Servers'))
->setIcon('fa-database')
->setHref(urisprintf('%s%s/', $services_uri, 'cluster/databases'));
$nav->newLink('schemata')
->setName(pht('Database Schemata'))
->setIcon('fa-table')
->setHref(urisprintf('%s%s/', $services_uri, 'database'));
$nav->newLink('schemata-issues')
->setName(pht('Schemata Issues'))
->setIcon('fa-exclamation-circle')
->setHref(urisprintf('%s%s/', $services_uri, 'dbissue'));
$nav->addLabel(pht('Cache'));
$nav->newLink('cache')
->setName(pht('Cache Status'))
->setIcon('fa-archive')
->setHref(urisprintf('%s%s/', $services_uri, 'cache'));
$nav->addLabel(pht('Other Services'));
$nav->newLink('notification-servers')
->setName(pht('Notification Servers'))
->setIcon('fa-bell-o')
->setHref(urisprintf('%s%s/', $services_uri, 'cluster/notifications'));
$nav->newLink('repository-servers')
->setName(pht('Repository Servers'))
->setIcon('fa-code')
->setHref(urisprintf('%s%s/', $services_uri, 'cluster/repositories'));
$nav->newLink('search-servers')
->setName(pht('Search Servers'))
->setIcon('fa-search')
->setHref(urisprintf('%s%s/', $services_uri, 'cluster/search'));
if ($select_filter) {
$nav->selectFilter($select_filter);
}
return $nav;
}
public function newCrumbs() {
$services_uri = $this->getApplicationURI('cluster/databases/');
return $this->buildApplicationCrumbs()
->addTextCrumb(pht('Services'))
->setBorder(true);
}
}

View file

@ -1,7 +1,7 @@
<?php <?php
final class PhabricatorConfigEditController final class PhabricatorConfigEditController
extends PhabricatorConfigController { extends PhabricatorConfigSettingsController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer(); $viewer = $request->getViewer();
@ -30,11 +30,9 @@ final class PhabricatorConfigEditController
->setDefault(null) ->setDefault(null)
->setDescription($desc); ->setDescription($desc);
$group = null; $group = null;
$group_uri = $this->getApplicationURI();
} else { } else {
$option = $options[$key]; $option = $options[$key];
$group = $option->getGroup(); $group = $option->getGroup();
$group_uri = $this->getApplicationURI('group/'.$group->getKey().'/');
} }
$issue = $request->getStr('issue'); $issue = $request->getStr('issue');
@ -42,7 +40,7 @@ final class PhabricatorConfigEditController
// If the user came here from an open setup issue, send them back. // If the user came here from an open setup issue, send them back.
$done_uri = $this->getApplicationURI('issue/'.$issue.'/'); $done_uri = $this->getApplicationURI('issue/'.$issue.'/');
} else { } else {
$done_uri = $group_uri; $done_uri = $this->getApplicationURI('settings/');
} }
// Check if the config key is already stored in the database. // Check if the config key is already stored in the database.
@ -205,23 +203,10 @@ final class PhabricatorConfigEditController
$title = $key; $title = $key;
$box_header = array(); $box_header = array();
if ($group) {
$box_header[] = phutil_tag(
'a',
array(
'href' => $group_uri,
),
$group->getName());
$box_header[] = " \xC2\xBB ";
}
$box_header[] = $key; $box_header[] = $key;
$crumbs = $this->buildApplicationCrumbs(); $crumbs = $this->newCrumbs()
if ($group) { ->addTextCrumb($key, '/config/edit/'.$key);
$crumbs->addTextCrumb($group->getName(), $group_uri);
}
$crumbs->addTextCrumb($key, '/config/edit/'.$key);
$crumbs->setBorder(true);
$form_box = $this->buildConfigBoxView($box_header, $form, $tag); $form_box = $this->buildConfigBoxView($box_header, $form, $tag);
@ -230,9 +215,6 @@ final class PhabricatorConfigEditController
new PhabricatorConfigTransactionQuery()); new PhabricatorConfigTransactionQuery());
$timeline->setShouldTerminate(true); $timeline->setShouldTerminate(true);
$nav = $this->buildSideNavView();
$nav->selectFilter($group_uri);
$header = $this->buildHeaderView($title); $header = $this->buildHeaderView($title);
$view = id(new PHUITwoColumnView()) $view = id(new PHUITwoColumnView())
@ -249,7 +231,6 @@ final class PhabricatorConfigEditController
return $this->newPage() return $this->newPage()
->setTitle($title) ->setTitle($title)
->setCrumbs($crumbs) ->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($view); ->appendChild($view);
} }

View file

@ -0,0 +1,51 @@
<?php
abstract class PhabricatorConfigSettingsController
extends PhabricatorConfigController {
public function newNavigation($select_filter) {
$settings_uri = $this->getApplicationURI('settings/');
$nav = id(new AphrontSideNavFilterView())
->setBaseURI(new PhutilURI($settings_uri));
$nav->addLabel(pht('Configuration'));
$nav->newLink('settings')
->setName(pht('Core Settings'))
->setIcon('fa-wrench')
->setHref($settings_uri);
$nav->newLink('advanced')
->setName(pht('Advanced Settings'))
->setIcon('fa-cogs')
->setHref(urisprintf('%s%s/', $settings_uri, 'advanced'));
$nav->newLink('all')
->setName(pht('All Settings'))
->setIcon('fa-list')
->setHref(urisprintf('%s%s/', $settings_uri, 'all'));
$nav->addLabel(pht('History'));
$nav->newLink('history')
->setName(pht('View History'))
->setIcon('fa-history')
->setHref(urisprintf('%s%s/', $settings_uri, 'history'));
if ($select_filter) {
$nav->selectFilter($select_filter);
}
return $nav;
}
public function newCrumbs() {
$settings_uri = $this->getApplicationURI('settings/');
return $this->buildApplicationCrumbs()
->addTextCrumb(pht('Settings'), $settings_uri)
->setBorder(true);
}
}

View file

@ -1,7 +1,7 @@
<?php <?php
final class PhabricatorConfigHistoryController final class PhabricatorConfigSettingsHistoryController
extends PhabricatorConfigController { extends PhabricatorConfigSettingsController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer(); $viewer = $request->getViewer();
@ -27,12 +27,10 @@ final class PhabricatorConfigHistoryController
$title = pht('Settings History'); $title = pht('Settings History');
$header = $this->buildHeaderView($title); $header = $this->buildHeaderView($title);
$nav = $this->buildSideNavView(); $nav = $this->newNavigation('history');
$nav->selectFilter('history/');
$crumbs = $this->buildApplicationCrumbs() $crumbs = $this->newCrumbs()
->addTextCrumb($title) ->addTextCrumb($title);
->setBorder(true);
$content = id(new PHUITwoColumnView()) $content = id(new PHUITwoColumnView())
->setHeader($header) ->setHeader($header)

View file

@ -0,0 +1,107 @@
<?php
final class PhabricatorConfigSettingsListController
extends PhabricatorConfigSettingsController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$filter = $request->getURIData('filter');
if (!strlen($filter)) {
$filter = 'settings';
}
$is_core = ($filter === 'settings');
$is_advanced = ($filter === 'advanced');
$is_all = ($filter === 'all');
$show_core = ($is_core || $is_all);
$show_advanced = ($is_advanced || $is_all);
if ($is_core) {
$title = pht('Core Settings');
} else if ($is_advanced) {
$title = pht('Advanced Settings');
} else {
$title = pht('All Settings');
}
$db_values = id(new PhabricatorConfigEntry())
->loadAllWhere('namespace = %s', 'default');
$db_values = mpull($db_values, null, 'getConfigKey');
$list = id(new PHUIObjectItemListView())
->setBig(true)
->setFlush(true);
$rows = array();
$options = PhabricatorApplicationConfigOptions::loadAllOptions();
ksort($options);
foreach ($options as $option) {
$key = $option->getKey();
$is_advanced = (bool)$option->getLocked();
if ($is_advanced && !$show_advanced) {
continue;
}
if (!$is_advanced && !$show_core) {
continue;
}
$db_value = idx($db_values, $key);
$item = $this->newConfigOptionView($option, $db_value);
$list->addItem($item);
}
$header = $this->buildHeaderView($title);
$crumbs = $this->newCrumbs()
->addTextCrumb($title);
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter($list);
$nav = $this->newNavigation($filter);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}
private function newConfigOptionView(
PhabricatorConfigOption $option,
PhabricatorConfigEntry $stored_value = null) {
$summary = $option->getSummary();
$item = id(new PHUIObjectItemView())
->setHeader($option->getKey())
->setClickable(true)
->setHref('/config/edit/'.$option->getKey().'/')
->addAttribute($summary);
$color = null;
if ($stored_value && !$stored_value->getIsDeleted()) {
$item->setEffect('visited');
$color = 'violet';
}
if ($option->getHidden()) {
$item->setStatusIcon('fa-eye-slash', pht('Hidden'));
} else if ($option->getLocked()) {
$item->setStatusIcon('fa-lock '.$color, pht('Locked'));
} else if ($color) {
$item->setStatusIcon('fa-pencil '.$color, pht('Editable'));
} else {
$item->setStatusIcon('fa-circle-o grey', pht('Default'));
}
return $item;
}
}

View file

@ -59,9 +59,11 @@ final class PhabricatorDashboardConsoleController
->setBackground(PHUIObjectBoxView::WHITE_CONFIG) ->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
->setObjectList($menu); ->setObjectList($menu);
$launch_view = id(new PHUILauncherView())
->appendChild($box);
$view = id(new PHUITwoColumnView()) $view = id(new PHUITwoColumnView())
->setFixed(true) ->setFooter($launch_view);
->setFooter($box);
return $this->newPage() return $this->newPage()
->setTitle($title) ->setTitle($title)

View file

@ -78,14 +78,16 @@ final class DiffusionRepositoryEditController
->addClass('diffusion-create-repo') ->addClass('diffusion-create-repo')
->appendChild($layout); ->appendChild($layout);
$view = id(new PHUITwoColumnView()) $launcher_view = id(new PHUILauncherView())
->setFixed(true) ->appendChild(
->setFooter(
array( array(
$layout, $layout,
$hints, $hints,
)); ));
$view = id(new PHUITwoColumnView())
->setFooter($launcher_view);
return $this->newPage() return $this->newPage()
->setTitle($title) ->setTitle($title)
->setCrumbs($crumbs) ->setCrumbs($crumbs)

View file

@ -76,9 +76,11 @@ final class DrydockConsoleController extends DrydockController {
->setBackground(PHUIObjectBoxView::WHITE_CONFIG) ->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
->setObjectList($menu); ->setObjectList($menu);
$launcher_view = id(new PHUILauncherView())
->appendChild($box);
$view = id(new PHUITwoColumnView()) $view = id(new PHUITwoColumnView())
->setFixed(true) ->setFooter($launcher_view);
->setFooter($box);
return $this->newPage() return $this->newPage()
->setTitle($title) ->setTitle($title)

View file

@ -179,18 +179,23 @@ final class HarbormasterBuildUnitMessage
$is_text = ($format !== self::FORMAT_REMARKUP); $is_text = ($format !== self::FORMAT_REMARKUP);
$is_remarkup = ($format === self::FORMAT_REMARKUP); $is_remarkup = ($format === self::FORMAT_REMARKUP);
$message = null;
$full_details = $this->getUnitMessageDetails(); $full_details = $this->getUnitMessageDetails();
$byte_length = strlen($full_details);
if (!strlen($full_details)) { $text_limit = 1024 * 2;
$remarkup_limit = 1024 * 8;
if (!$byte_length) {
if ($summarize) { if ($summarize) {
return null; return null;
} }
$details = phutil_tag('em', array(), pht('No details provided.')); $message = phutil_tag('em', array(), pht('No details provided.'));
} else if ($summarize) { } else if ($summarize) {
if ($is_text) { if ($is_text) {
$details = id(new PhutilUTF8StringTruncator()) $details = id(new PhutilUTF8StringTruncator())
->setMaximumBytes(2048) ->setMaximumBytes($text_limit)
->truncateString($full_details); ->truncateString($full_details);
$details = phutil_split_lines($details); $details = phutil_split_lines($details);
@ -200,9 +205,36 @@ final class HarbormasterBuildUnitMessage
} }
$details = implode('', $details); $details = implode('', $details);
} else {
if ($byte_length > $remarkup_limit) {
$uri = $this->getURI();
if ($uri) {
$link = phutil_tag(
'a',
array(
'href' => $uri,
'target' => '_blank',
),
pht('View Details'));
} else {
$link = null;
}
$message = array();
$message[] = phutil_tag(
'em',
array(),
pht('This test has too much data to display inline.'));
if ($link) {
$message[] = $link;
}
$message = phutil_implode_html(" \xC2\xB7 ", $message);
} else { } else {
$details = $full_details; $details = $full_details;
} }
}
} else { } else {
$details = $full_details; $details = $full_details;
} }
@ -212,19 +244,47 @@ final class HarbormasterBuildUnitMessage
$classes = array(); $classes = array();
$classes[] = 'harbormaster-unit-details'; $classes[] = 'harbormaster-unit-details';
if ($is_remarkup) { if ($message !== null) {
// If we have a message, show that instead of rendering any test details.
$details = $message;
} else if ($is_remarkup) {
$details = new PHUIRemarkupView($viewer, $details); $details = new PHUIRemarkupView($viewer, $details);
} else { } else {
$classes[] = 'harbormaster-unit-details-text'; $classes[] = 'harbormaster-unit-details-text';
$classes[] = 'PhabricatorMonospaced'; $classes[] = 'PhabricatorMonospaced';
} }
return phutil_tag( $warning = null;
if (!$summarize) {
$warnings = array();
if ($is_remarkup && ($byte_length > $remarkup_limit)) {
$warnings[] = pht(
'This test result has %s bytes of Remarkup test details. Remarkup '.
'blocks longer than %s bytes are not rendered inline when showing '.
'test summaries.',
new PhutilNumber($byte_length),
new PhutilNumber($remarkup_limit));
}
if ($warnings) {
$warning = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setErrors($warnings);
}
}
$content = phutil_tag(
'div', 'div',
array( array(
'class' => implode(' ', $classes), 'class' => implode(' ', $classes),
), ),
$details); $details);
return array(
$warning,
$content,
);
} }
public function getUnitMessageDisplayName() { public function getUnitMessageDisplayName() {
@ -262,6 +322,18 @@ final class HarbormasterBuildUnitMessage
return implode("\0", $parts); return implode("\0", $parts);
} }
public function getURI() {
$id = $this->getID();
if (!$id) {
return null;
}
return urisprintf(
'/harbormaster/unit/view/%d/',
$id);
}
public function save() { public function save() {
if ($this->nameIndex === null) { if ($this->nameIndex === null) {
$this->nameIndex = HarbormasterString::newIndex($this->getName()); $this->nameIndex = HarbormasterString::newIndex($this->getName());

View file

@ -72,13 +72,13 @@ final class HarbormasterUnitPropertyView extends AphrontView {
} }
$name = $message->getUnitMessageDisplayName(); $name = $message->getUnitMessageDisplayName();
$id = $message->getID(); $uri = $message->getURI();
if ($id) { if ($uri) {
$name = phutil_tag( $name = phutil_tag(
'a', 'a',
array( array(
'href' => "/harbormaster/unit/view/{$id}/", 'href' => $uri,
), ),
$name); $name);
} }

View file

@ -243,6 +243,12 @@ abstract class HeraldAdapter extends Phobject {
abstract public function getAdapterApplicationClass(); abstract public function getAdapterApplicationClass();
abstract public function getObject(); abstract public function getObject();
public function getAdapterContentIcon() {
$application_class = $this->getAdapterApplicationClass();
$application = newv($application_class, array());
return $application->getIcon();
}
/** /**
* Return a new characteristic object for this adapter. * Return a new characteristic object for this adapter.
* *

View file

@ -3,42 +3,39 @@
final class HeraldNewController extends HeraldController { final class HeraldNewController extends HeraldController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer(); $viewer = $this->getViewer();
$adapter_type_map = HeraldAdapter::getEnabledAdapterMap($viewer);
$adapter_type = $request->getStr('adapter');
if (!isset($adapter_type_map[$adapter_type])) {
$title = pht('Create Herald Rule');
$content = $this->newAdapterMenu($title);
} else {
$adapter = HeraldAdapter::getAdapterForContentType($adapter_type);
$content_type_map = HeraldAdapter::getEnabledAdapterMap($viewer);
$rule_type_map = HeraldRuleTypeConfig::getRuleTypeMap(); $rule_type_map = HeraldRuleTypeConfig::getRuleTypeMap();
$rule_type = $request->getStr('type');
if (!isset($rule_type_map[$rule_type])) {
$title = pht(
'Create Herald Rule: %s',
$adapter->getAdapterContentName());
$content = $this->newTypeMenu($adapter, $title);
} else {
if ($rule_type !== HeraldRuleTypeConfig::RULE_TYPE_OBJECT) {
$target_phid = null;
$target_okay = true;
} else {
$object_name = $request->getStr('objectName');
$target_okay = false;
$errors = array(); $errors = array();
$e_type = null;
$e_rule = null;
$e_object = null; $e_object = null;
$step = $request->getInt('step');
if ($request->isFormPost()) { if ($request->isFormPost()) {
$content_type = $request->getStr('content_type'); if (strlen($object_name)) {
if (empty($content_type_map[$content_type])) {
$errors[] = pht('You must choose a content type for this rule.');
$e_type = pht('Required');
$step = 0;
}
if (!$errors && $step > 1) {
$rule_type = $request->getStr('rule_type');
if (empty($rule_type_map[$rule_type])) {
$errors[] = pht('You must choose a rule type for this rule.');
$e_rule = pht('Required');
$step = 1;
}
}
if (!$errors && $step >= 2) {
$target_phid = null;
$object_name = $request->getStr('objectName');
$done = false;
if ($rule_type != HeraldRuleTypeConfig::RULE_TYPE_OBJECT) {
$done = true;
} else if (strlen($object_name)) {
$target_object = id(new PhabricatorObjectQuery()) $target_object = id(new PhabricatorObjectQuery())
->setViewer($viewer) ->setViewer($viewer)
->withNames(array($object_name)) ->withNames(array($object_name))
@ -50,99 +47,255 @@ final class HeraldNewController extends HeraldController {
PhabricatorPolicyCapability::CAN_EDIT); PhabricatorPolicyCapability::CAN_EDIT);
if (!$can_edit) { if (!$can_edit) {
$errors[] = pht( $errors[] = pht(
'You can not create a rule for that object, because you do '. 'You can not create a rule for that object, because you '.
'not have permission to edit it. You can only create rules '. 'do not have permission to edit it. You can only create '.
'for objects you can edit.'); 'rules for objects you can edit.');
$e_object = pht('Not Editable'); $e_object = pht('Not Editable');
$step = 2;
} else { } else {
$adapter = HeraldAdapter::getAdapterForContentType($content_type);
if (!$adapter->canTriggerOnObject($target_object)) { if (!$adapter->canTriggerOnObject($target_object)) {
$errors[] = pht( $errors[] = pht(
'This object is not of an allowed type for the rule. '. 'This object is not of an allowed type for the rule. '.
'Rules can only trigger on certain objects.'); 'Rules can only trigger on certain objects.');
$e_object = pht('Invalid'); $e_object = pht('Invalid');
$step = 2;
} else { } else {
$target_phid = $target_object->getPHID(); $target_phid = $target_object->getPHID();
$done = true;
} }
} }
} else { } else {
$errors[] = pht('No object exists by that name.'); $errors[] = pht('No object exists by that name.');
$e_object = pht('Invalid'); $e_object = pht('Invalid');
$step = 2;
} }
} else if ($step > 2) { } else {
$errors[] = pht( $errors[] = pht(
'You must choose an object to associate this rule with.'); 'You must choose an object to associate this rule with.');
$e_object = pht('Required'); $e_object = pht('Required');
$step = 2;
} }
if (!$errors && $done) { $target_okay = !$errors;
}
}
if (!$target_okay) {
$title = pht('Choose Object');
$content = $this->newTargetForm(
$adapter,
$rule_type,
$object_name,
$errors,
$e_object,
$title);
} else {
$params = array( $params = array(
'content_type' => $content_type, 'content_type' => $adapter_type,
'rule_type' => $rule_type, 'rule_type' => $rule_type,
'targetPHID' => $target_phid, 'targetPHID' => $target_phid,
); );
$uri = new PhutilURI('edit/', $params); $edit_uri = $this->getApplicationURI('edit/');
$uri = $this->getApplicationURI($uri); $edit_uri = new PhutilURI($edit_uri, $params);
return id(new AphrontRedirectResponse())->setURI($uri);
return id(new AphrontRedirectResponse())
->setURI($edit_uri);
} }
} }
} }
$content_type = $request->getStr('content_type'); $crumbs = $this
$rule_type = $request->getStr('rule_type'); ->buildApplicationCrumbs()
->addTextCrumb(pht('Create Rule'))
->setBorder(true);
$form = id(new AphrontFormView()) $view = id(new PHUITwoColumnView())
->setUser($viewer) ->setFooter($content);
->setAction($this->getApplicationURI('new/'));
switch ($step) { return $this->newPage()
case 0: ->setTitle($title)
default: ->setCrumbs($crumbs)
$content_types = $this->renderContentTypeControl( ->appendChild($view);
$content_type_map, }
$e_type);
$form private function newAdapterMenu($title) {
->addHiddenInput('step', 1) $viewer = $this->getViewer();
->appendChild($content_types);
$cancel_text = null; $types = HeraldAdapter::getEnabledAdapterMap($viewer);
$cancel_uri = $this->getApplicationURI();
$title = pht('Create Herald Rule');
break;
case 1:
$rule_types = $this->renderRuleTypeControl(
$rule_type_map,
$e_rule);
$form foreach ($types as $key => $type) {
->addHiddenInput('content_type', $content_type) $types[$key] = HeraldAdapter::getAdapterForContentType($key);
->addHiddenInput('step', 2) }
->appendChild($rule_types);
$params = array( $types = msort($types, 'getAdapterContentName');
'content_type' => $content_type,
'step' => '0', $base_uri = $this->getApplicationURI('create/');
$menu = id(new PHUIObjectItemListView())
->setViewer($viewer)
->setBig(true);
foreach ($types as $key => $adapter) {
$adapter_uri = id(new PhutilURI($base_uri))
->replaceQueryParam('adapter', $key);
$description = $adapter->getAdapterContentDescription();
$description = phutil_escape_html_newlines($description);
$item = id(new PHUIObjectItemView())
->setHeader($adapter->getAdapterContentName())
->setImageIcon($adapter->getAdapterContentIcon())
->addAttribute($description)
->setHref($adapter_uri)
->setClickable(true);
$menu->addItem($item);
}
$box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
->setObjectList($menu);
return id(new PHUILauncherView())
->appendChild($box);
}
private function newTypeMenu(HeraldAdapter $adapter, $title) {
$viewer = $this->getViewer();
$global_capability = HeraldManageGlobalRulesCapability::CAPABILITY;
$can_global = $this->hasApplicationCapability($global_capability);
if ($can_global) {
$global_note = pht(
'You have permission to create and manage global rules.');
} else {
$global_note = pht(
'You do not have permission to create or manage global rules.');
}
$global_note = phutil_tag('em', array(), $global_note);
$specs = array(
HeraldRuleTypeConfig::RULE_TYPE_PERSONAL => array(
'name' => pht('Personal Rule'),
'icon' => 'fa-user',
'help' => pht(
'Personal rules notify you about events. You own them, but they can '.
'only affect you. Personal rules only trigger for objects you have '.
'permission to see.'),
'enabled' => true,
),
HeraldRuleTypeConfig::RULE_TYPE_OBJECT => array(
'name' => pht('Object Rule'),
'icon' => 'fa-cube',
'help' => pht(
'Object rules notify anyone about events. They are bound to an '.
'object (like a repository) and can only act on that object. You '.
'must be able to edit an object to create object rules for it. '.
'Other users who can edit the object can edit its rules.'),
'enabled' => true,
),
HeraldRuleTypeConfig::RULE_TYPE_GLOBAL => array(
'name' => pht('Global Rule'),
'icon' => 'fa-globe',
'help' => array(
pht(
'Global rules notify anyone about events. Global rules can '.
'bypass access control policies and act on any object.'),
$global_note,
),
'enabled' => $can_global,
),
); );
$cancel_text = pht('Back'); $adapter_type = $adapter->getAdapterContentType();
$cancel_uri = new PhutilURI('new/', $params);
$cancel_uri = $this->getApplicationURI($cancel_uri); $base_uri = new PhutilURI($this->getApplicationURI('create/'));
$title = pht('Create Herald Rule: %s',
idx($content_type_map, $content_type)); $adapter_uri = id(clone $base_uri)
break; ->replaceQueryParam('adapter', $adapter_type);
case 2:
$adapter = HeraldAdapter::getAdapterForContentType($content_type); $menu = id(new PHUIObjectItemListView())
$form ->setUser($viewer)
->addHiddenInput('content_type', $content_type) ->setBig(true);
->addHiddenInput('rule_type', $rule_type)
->addHiddenInput('step', 3) foreach ($specs as $rule_type => $spec) {
$type_uri = id(clone $adapter_uri)
->replaceQueryParam('type', $rule_type);
$name = $spec['name'];
$icon = $spec['icon'];
$description = $spec['help'];
$description = (array)$description;
$enabled = $spec['enabled'];
if ($enabled) {
$enabled = $adapter->supportsRuleType($rule_type);
if (!$enabled) {
$description[] = phutil_tag(
'em',
array(),
pht(
'This rule type is not supported by the selected '.
'content type.'));
}
}
$description = phutil_implode_html(
array(
phutil_tag('br'),
phutil_tag('br'),
),
$description);
$item = id(new PHUIObjectItemView())
->setHeader($name)
->setImageIcon($icon)
->addAttribute($description);
if ($enabled) {
$item
->setHref($type_uri)
->setClickable(true);
} else {
$item->setDisabled(true);
}
$menu->addItem($item);
}
$box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
->setObjectList($menu);
$box->newTailButton()
->setText(pht('Back to Content Types'))
->setIcon('fa-chevron-left')
->setHref($base_uri);
return id(new PHUILauncherView())
->appendChild($box);
}
private function newTargetForm(
HeraldAdapter $adapter,
$rule_type,
$object_name,
$errors,
$e_object,
$title) {
$viewer = $this->getViewer();
$content_type = $adapter->getAdapterContentType();
$rule_type_map = HeraldRuleTypeConfig::getRuleTypeMap();
$params = array(
'adapter' => $content_type,
'type' => $rule_type,
);
$form = id(new AphrontFormView())
->setViewer($viewer)
->appendChild( ->appendChild(
id(new AphrontFormStaticControl()) id(new AphrontFormStaticControl())
->setLabel(pht('Rule for')) ->setLabel(pht('Rule for'))
@ -150,7 +303,7 @@ final class HeraldNewController extends HeraldController {
phutil_tag( phutil_tag(
'strong', 'strong',
array(), array(),
idx($content_type_map, $content_type)))) $adapter->getAdapterContentName())))
->appendChild( ->appendChild(
id(new AphrontFormStaticControl()) id(new AphrontFormStaticControl())
->setLabel(pht('Rule Type')) ->setLabel(pht('Rule Type'))
@ -170,28 +323,23 @@ final class HeraldNewController extends HeraldController {
id(new AphrontFormTextControl()) id(new AphrontFormTextControl())
->setName('objectName') ->setName('objectName')
->setError($e_object) ->setError($e_object)
->setValue($request->getStr('objectName')) ->setValue($object_name)
->setLabel(pht('Object'))); ->setLabel(pht('Object')));
$params = array( foreach ($params as $key => $value) {
'content_type' => $content_type, $form->addHiddenInput($key, $value);
'rule_type' => $rule_type,
'step' => 1,
);
$cancel_text = pht('Back');
$cancel_uri = new PhutilURI('new/', $params);
$cancel_uri = $this->getApplicationURI($cancel_uri);
$title = pht('Create Herald Rule: %s',
idx($content_type_map, $content_type));
break;
} }
$form $cancel_params = $params;
->appendChild( unset($cancel_params['type']);
$cancel_uri = $this->getApplicationURI('new/');
$cancel_uri = new PhutilURI($cancel_uri, $params);
$form->appendChild(
id(new AphrontFormSubmitControl()) id(new AphrontFormSubmitControl())
->setValue(pht('Continue')) ->setValue(pht('Continue'))
->addCancelButton($cancel_uri, $cancel_text)); ->addCancelButton($cancel_uri, pht('Back')));
$form_box = id(new PHUIObjectBoxView()) $form_box = id(new PHUIObjectBoxView())
->setHeaderText($title) ->setHeaderText($title)
@ -199,118 +347,7 @@ final class HeraldNewController extends HeraldController {
->setBackground(PHUIObjectBoxView::WHITE_CONFIG) ->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
->setForm($form); ->setForm($form);
$crumbs = $this return $form_box;
->buildApplicationCrumbs()
->addTextCrumb(pht('Create Rule'))
->setBorder(true);
$view = id(new PHUITwoColumnView())
->setFooter($form_box);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild(
array(
$view,
));
}
private function renderContentTypeControl(array $content_type_map, $e_type) {
$request = $this->getRequest();
$radio = id(new AphrontFormRadioButtonControl())
->setLabel(pht('New Rule for'))
->setName('content_type')
->setValue($request->getStr('content_type'))
->setError($e_type);
foreach ($content_type_map as $value => $name) {
$adapter = HeraldAdapter::getAdapterForContentType($value);
$radio->addButton(
$value,
$name,
phutil_escape_html_newlines($adapter->getAdapterContentDescription()));
}
return $radio;
}
private function renderRuleTypeControl(array $rule_type_map, $e_rule) {
$request = $this->getRequest();
// Reorder array to put less powerful rules first.
$rule_type_map = array_select_keys(
$rule_type_map,
array(
HeraldRuleTypeConfig::RULE_TYPE_PERSONAL,
HeraldRuleTypeConfig::RULE_TYPE_OBJECT,
HeraldRuleTypeConfig::RULE_TYPE_GLOBAL,
)) + $rule_type_map;
list($can_global, $global_link) = $this->explainApplicationCapability(
HeraldManageGlobalRulesCapability::CAPABILITY,
pht('You have permission to create and manage global rules.'),
pht('You do not have permission to create or manage global rules.'));
$captions = array(
HeraldRuleTypeConfig::RULE_TYPE_PERSONAL =>
pht(
'Personal rules notify you about events. You own them, but they can '.
'only affect you. Personal rules only trigger for objects you have '.
'permission to see.'),
HeraldRuleTypeConfig::RULE_TYPE_OBJECT =>
pht(
'Object rules notify anyone about events. They are bound to an '.
'object (like a repository) and can only act on that object. You '.
'must be able to edit an object to create object rules for it. '.
'Other users who can edit the object can edit its rules.'),
HeraldRuleTypeConfig::RULE_TYPE_GLOBAL =>
array(
pht(
'Global rules notify anyone about events. Global rules can '.
'bypass access control policies and act on any object.'),
$global_link,
),
);
$radio = id(new AphrontFormRadioButtonControl())
->setLabel(pht('Rule Type'))
->setName('rule_type')
->setValue($request->getStr('rule_type'))
->setError($e_rule);
$adapter = HeraldAdapter::getAdapterForContentType(
$request->getStr('content_type'));
foreach ($rule_type_map as $value => $name) {
$caption = idx($captions, $value);
$disabled = ($value == HeraldRuleTypeConfig::RULE_TYPE_GLOBAL) &&
(!$can_global);
if (!$adapter->supportsRuleType($value)) {
$disabled = true;
$caption = array(
$caption,
"\n\n",
phutil_tag(
'em',
array(),
pht(
'This rule type is not supported by the selected content type.')),
);
}
$radio->addButton(
$value,
$name,
phutil_escape_html_newlines($caption),
$disabled ? 'disabled' : null,
$disabled);
}
return $radio;
} }
} }

View file

@ -336,41 +336,36 @@ final class ManiphestTaskDetailController extends ManiphestController {
$curtain->addAction($relationship_submenu); $curtain->addAction($relationship_submenu);
} }
$viewer_phid = $viewer->getPHID();
$owner_phid = $task->getOwnerPHID(); $owner_phid = $task->getOwnerPHID();
$author_phid = $task->getAuthorPHID(); $author_phid = $task->getAuthorPHID();
$handles = $viewer->loadHandles(array($owner_phid, $author_phid)); $handles = $viewer->loadHandles(array($owner_phid, $author_phid));
$assigned_refs = id(new PHUICurtainObjectRefListView())
->setViewer($viewer)
->setEmptyMessage(pht('None'));
if ($owner_phid) { if ($owner_phid) {
$image_uri = $handles[$owner_phid]->getImageURI(); $assigned_ref = $assigned_refs->newObjectRefView()
$image_href = $handles[$owner_phid]->getURI(); ->setHandle($handles[$owner_phid])
$owner = $viewer->renderHandle($owner_phid)->render(); ->setHighlighted($owner_phid === $viewer_phid);
$content = phutil_tag('strong', array(), $owner);
$assigned_to = id(new PHUIHeadThingView())
->setImage($image_uri)
->setImageHref($image_href)
->setContent($content);
} else {
$assigned_to = phutil_tag('em', array(), pht('None'));
} }
$curtain->newPanel() $curtain->newPanel()
->setHeaderText(pht('Assigned To')) ->setHeaderText(pht('Assigned To'))
->appendChild($assigned_to); ->appendChild($assigned_refs);
$author_uri = $handles[$author_phid]->getImageURI(); $author_refs = id(new PHUICurtainObjectRefListView())
$author_href = $handles[$author_phid]->getURI(); ->setViewer($viewer);
$author = $viewer->renderHandle($author_phid)->render();
$content = phutil_tag('strong', array(), $author); $author_ref = $author_refs->newObjectRefView()
$date = phabricator_date($task->getDateCreated(), $viewer); ->setHandle($handles[$author_phid])
$content = pht('%s, %s', $content, $date); ->setEpoch($task->getDateCreated())
$authored_by = id(new PHUIHeadThingView()) ->setHighlighted($author_phid === $viewer_phid);
->setImage($author_uri)
->setImageHref($author_href)
->setContent($content);
$curtain->newPanel() $curtain->newPanel()
->setHeaderText(pht('Authored By')) ->setHeaderText(pht('Authored By'))
->appendChild($authored_by); ->appendChild($author_refs);
return $curtain; return $curtain;
} }

View file

@ -24,6 +24,10 @@ final class PhabricatorAphlictSetupCheck extends PhabricatorSetupCheck {
$this->newIssue('aphlict.connect') $this->newIssue('aphlict.connect')
->setShortName(pht('Notification Server Down')) ->setShortName(pht('Notification Server Down'))
->setName(pht('Unable to Connect to Notification Server')) ->setName(pht('Unable to Connect to Notification Server'))
->setSummary(
pht(
'Phabricator is configured to use a notification server, '.
'but is not able to connect to it.'))
->setMessage($message) ->setMessage($message)
->addRelatedPhabricatorConfig('notification.servers') ->addRelatedPhabricatorConfig('notification.servers')
->addCommand( ->addCommand(

View file

@ -271,6 +271,25 @@ final class PhabricatorProjectQuery
$all_graph = $this->getAllReachableAncestors($projects); $all_graph = $this->getAllReachableAncestors($projects);
// See T13484. If the graph is damaged (and contains a cycle or an edge
// pointing at a project which has been destroyed), some of the nodes we
// started with may be filtered out by reachability tests. If any of the
// projects we are linking up don't have available ancestors, filter them
// out.
foreach ($projects as $key => $project) {
$project_phid = $project->getPHID();
if (!isset($all_graph[$project_phid])) {
$this->didRejectResult($project);
unset($projects[$key]);
continue;
}
}
if (!$projects) {
return array();
}
// NOTE: Although we may not need much information about ancestors, we // NOTE: Although we may not need much information about ancestors, we
// always need to test if the viewer is a member, because we will return // always need to test if the viewer is a member, because we will return
// ancestor projects to the policy filter via ExtendedPolicy calls. If // ancestor projects to the policy filter via ExtendedPolicy calls. If

View file

@ -15,25 +15,131 @@ final class PhabricatorSubscriptionsCurtainExtension
public function buildCurtainPanel($object) { public function buildCurtainPanel($object) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();
$viewer_phid = $viewer->getPHID();
$object_phid = $object->getPHID(); $object_phid = $object->getPHID();
$max_handles = 100;
$max_visible = 8;
// TODO: We should limit the number of subscriber PHIDs we'll load, so
// we degrade gracefully when objects have thousands of subscribers.
$subscriber_phids = PhabricatorSubscribersQuery::loadSubscribersForPHID( $subscriber_phids = PhabricatorSubscribersQuery::loadSubscribersForPHID(
$object_phid); $object_phid);
$subscriber_count = count($subscriber_phids);
$subscriber_phids = $this->sortSubscriberPHIDs(
$subscriber_phids,
null);
// If we have fewer subscribers than the maximum number of handles we're
// willing to load, load all the handles and then sort the list based on
// complete handle data.
// If we have too many PHIDs, we'll skip this step and accept a less
// useful ordering.
$handles = null;
if ($subscriber_count <= $max_handles) {
$handles = $viewer->loadHandles($subscriber_phids); $handles = $viewer->loadHandles($subscriber_phids);
// TODO: This class can't accept a HandleList yet. $subscriber_phids = $this->sortSubscriberPHIDs(
$handles = iterator_to_array($handles); $subscriber_phids,
$handles);
}
$susbscribers_view = id(new SubscriptionListStringBuilder()) // If we have more PHIDs to show than visible slots, slice the list.
->setObjectPHID($object_phid) if ($subscriber_count > $max_visible) {
->setHandles($handles) $visible_phids = array_slice($subscriber_phids, 0, $max_visible - 1);
->buildPropertyString(); $show_all = true;
} else {
$visible_phids = $subscriber_phids;
$show_all = false;
}
// If we didn't load handles earlier because we had too many PHIDs,
// load them now.
if ($handles === null) {
$handles = $viewer->loadHandles($visible_phids);
}
$ref_list = id(new PHUICurtainObjectRefListView())
->setViewer($viewer)
->setEmptyMessage(pht('None'));
foreach ($visible_phids as $phid) {
$ref = $ref_list->newObjectRefView()
->setHandle($handles[$phid]);
if ($phid === $viewer_phid) {
$ref->setHighlighted(true);
}
}
if ($show_all) {
$view_all_uri = urisprintf(
'/subscriptions/list/%s/',
$object_phid);
$ref_list->newTailLink()
->setURI($view_all_uri)
->setText(pht('View All %d Subscriber(s)', $subscriber_count))
->setWorkflow(true);
}
return $this->newPanel() return $this->newPanel()
->setHeaderText(pht('Subscribers')) ->setHeaderText(pht('Subscribers'))
->setOrder(20000) ->setOrder(20000)
->appendChild($susbscribers_view); ->appendChild($ref_list);
}
private function sortSubscriberPHIDs(array $subscriber_phids, $handles) {
// Sort subscriber PHIDs with or without handle data. If we have handles,
// we can sort results more comprehensively.
$viewer = $this->getViewer();
$user_type = PhabricatorPeopleUserPHIDType::TYPECONST;
$viewer_phid = $viewer->getPHID();
$type_order_map = array(
PhabricatorPeopleUserPHIDType::TYPECONST => 0,
PhabricatorProjectProjectPHIDType::TYPECONST => 1,
PhabricatorOwnersPackagePHIDType::TYPECONST => 2,
);
$default_type_order = count($type_order_map);
$subscriber_map = array();
foreach ($subscriber_phids as $subscriber_phid) {
$is_viewer = ($viewer_phid === $subscriber_phid);
$subscriber_type = phid_get_type($subscriber_phid);
$type_order = idx($type_order_map, $subscriber_type, $default_type_order);
$sort_name = '';
$is_complete = false;
if ($handles) {
if (isset($handles[$subscriber_phid])) {
$handle = $handles[$subscriber_phid];
if ($handle->isComplete()) {
$is_complete = true;
$sort_name = $handle->getLinkName();
$sort_name = phutil_utf8_strtolower($sort_name);
}
}
}
$subscriber_map[$subscriber_phid] = id(new PhutilSortVector())
->addInt($is_viewer ? 0 : 1)
->addInt($is_complete ? 0 : 1)
->addInt($type_order)
->addString($sort_name);
}
$subscriber_map = msortv($subscriber_map, 'getSelf');
return array_keys($subscriber_map);
} }
} }

View file

@ -46,6 +46,13 @@ final class PhabricatorEdgeQuery extends PhabricatorQuery {
* @task config * @task config
*/ */
public function withSourcePHIDs(array $source_phids) { public function withSourcePHIDs(array $source_phids) {
if (!$source_phids) {
throw new Exception(
pht(
'Edge list passed to "withSourcePHIDs(...)" is empty, but it must '.
'be nonempty.'));
}
$this->sourcePHIDs = $source_phids; $this->sourcePHIDs = $source_phids;
return $this; return $this;
} }
@ -158,11 +165,10 @@ final class PhabricatorEdgeQuery extends PhabricatorQuery {
* @task exec * @task exec
*/ */
public function execute() { public function execute() {
if (!$this->sourcePHIDs) { if ($this->sourcePHIDs === null) {
throw new Exception( throw new Exception(
pht( pht(
'You must use %s to query edges.', 'You must use "withSourcePHIDs()" to query edges.'));
'withSourcePHIDs()'));
} }
$sources = phid_group_by_type($this->sourcePHIDs); $sources = phid_group_by_type($this->sourcePHIDs);

View file

@ -1718,6 +1718,11 @@ final class PhabricatorUSEnglishTranslation
'then try again.', 'then try again.',
), ),
'View All %d Subscriber(s)' => array(
'View Subscriber',
'View All %d Subscribers',
),
); );
} }

View file

@ -42,7 +42,7 @@ final class PhabricatorMarkupEngine extends Phobject {
private $objects = array(); private $objects = array();
private $viewer; private $viewer;
private $contextObject; private $contextObject;
private $version = 19; private $version = 20;
private $engineCaches = array(); private $engineCaches = array();
private $auxiliaryConfig = array(); private $auxiliaryConfig = array();

View file

@ -100,6 +100,9 @@ final class PhutilRemarkupNoteBlockRule extends PhutilRemarkupBlockRule {
} }
private function getRegEx() { private function getRegEx() {
static $regex;
if ($regex === null) {
$words = array( $words = array(
'NOTE', 'NOTE',
'IMPORTANT', 'IMPORTANT',
@ -111,11 +114,14 @@ final class PhutilRemarkupNoteBlockRule extends PhutilRemarkupBlockRule {
} }
$words = implode('|', $words); $words = implode('|', $words);
return $regex =
'/^(?:'. '/^(?:'.
'(?:\((?P<hideword>'.$words.')\))'. '(?:\((?P<hideword>'.$words.')\))'.
'|'. '|'.
'(?:(?P<showword>'.$words.'):))\s*'. '(?:(?P<showword>'.$words.'):))\s*'.
'/'; '/';
} }
return $regex;
}
} }

View file

@ -66,6 +66,8 @@ abstract class PhutilRemarkupQuotedBlockRule
foreach ($text as $key => $line) { foreach ($text as $key => $line) {
if (!strlen(trim($line))) { if (!strlen(trim($line))) {
unset($text[$key]); unset($text[$key]);
} else {
break;
} }
} }

View file

@ -121,8 +121,19 @@ final class PhutilRemarkupTableBlockRule extends PhutilRemarkupBlockRule {
return $table->newRawString(); return $table->newRawString();
} }
// Respect newlines in table cells as literal linebreaks.
$content = $cell->newRawContentString(); $content = $cell->newRawContentString();
$content = $this->applyRules($content); $content = trim($content, "\r\n");
$lines = phutil_split_lines($content, $retain_endings = false);
foreach ($lines as $key => $line) {
$lines[$key] = $this->applyRules($line);
}
$content = phutil_implode_html(
phutil_tag('br'),
$lines);
$cell_specs[] = array( $cell_specs[] = array(
'type' => $cell->getTagName(), 'type' => $cell->getTagName(),

View file

@ -153,33 +153,54 @@ final class PhutilRemarkupEngine extends PhutilMarkupEngine {
$block_rules = $this->blockRules; $block_rules = $this->blockRules;
$blocks = array(); $blocks = array();
$cursor = 0; $cursor = 0;
$prev_block = array();
$can_merge = array();
foreach ($block_rules as $key => $block_rule) {
if ($block_rule instanceof PhutilRemarkupDefaultBlockRule) {
$can_merge[$key] = true;
}
}
$last_block = null;
$last_block_key = -1;
// See T13487. For very large inputs, block separation can dominate
// runtime. This is written somewhat clumsily to attempt to handle
// very large inputs as gracefully as is practical.
while (isset($text[$cursor])) { while (isset($text[$cursor])) {
$starting_cursor = $cursor; $starting_cursor = $cursor;
foreach ($block_rules as $block_rule) { foreach ($block_rules as $block_key => $block_rule) {
$num_lines = $block_rule->getMatchingLineCount($text, $cursor); $num_lines = $block_rule->getMatchingLineCount($text, $cursor);
if ($num_lines) { if ($num_lines) {
if ($blocks) { $current_block = array(
$prev_block = last($blocks);
}
$curr_block = array(
'start' => $cursor, 'start' => $cursor,
'num_lines' => $num_lines, 'num_lines' => $num_lines,
'rule' => $block_rule, 'rule' => $block_rule,
'is_empty' => self::isEmptyBlock($text, $cursor, $num_lines), 'empty' => self::isEmptyBlock($text, $cursor, $num_lines),
'children' => array(), 'children' => array(),
'merge' => isset($can_merge[$block_key]),
); );
if ($prev_block $should_merge = self::shouldMergeParagraphBlocks(
&& self::shouldMergeBlocks($text, $prev_block, $curr_block)) { $text,
$blocks[last_key($blocks)]['num_lines'] += $curr_block['num_lines']; $last_block,
$blocks[last_key($blocks)]['is_empty'] = $current_block);
$blocks[last_key($blocks)]['is_empty'] && $curr_block['is_empty'];
if ($should_merge) {
$last_block['num_lines'] =
($last_block['num_lines'] + $current_block['num_lines']);
$last_block['empty'] =
($last_block['empty'] && $current_block['empty']);
$blocks[$last_block_key] = $last_block;
} else { } else {
$blocks[] = $curr_block; $blocks[] = $current_block;
$last_block = $current_block;
$last_block_key++;
} }
$cursor += $num_lines; $cursor += $num_lines;
@ -192,9 +213,20 @@ final class PhutilRemarkupEngine extends PhutilMarkupEngine {
} }
} }
// See T13487. It's common for blocks to be small, and this loop seems to
// measure as faster if we manually concatenate blocks than if we
// "array_slice()" and "implode()" blocks. This is a bit muddy.
foreach ($blocks as $key => $block) { foreach ($blocks as $key => $block) {
$lines = array_slice($text, $block['start'], $block['num_lines']); $min = $block['start'];
$blocks[$key]['text'] = implode('', $lines); $max = $min + $block['num_lines'];
$lines = '';
for ($ii = $min; $ii < $max; $ii++) {
$lines .= $text[$ii];
}
$blocks[$key]['text'] = $lines;
} }
// Stop splitting child blocks apart if we get too deep. This arrests // Stop splitting child blocks apart if we get too deep. This arrests
@ -246,30 +278,48 @@ final class PhutilRemarkupEngine extends PhutilMarkupEngine {
return $output; return $output;
} }
private static function shouldMergeBlocks($text, $prev_block, $curr_block) { private static function shouldMergeParagraphBlocks(
$block_rules = ipull(array($prev_block, $curr_block), 'rule'); $text,
$last_block,
$current_block) {
$default_rule = 'PhutilRemarkupDefaultBlockRule'; // If we're at the beginning of the input, we can't merge.
try { if ($last_block === null) {
assert_instances_of($block_rules, $default_rule); return false;
}
// If the last block was empty keep merging // If the previous block wasn't a default block, we can't merge.
if ($prev_block['is_empty']) { if (!$last_block['merge']) {
return false;
}
// If the current block isn't a default block, we can't merge.
if (!$current_block['merge']) {
return false;
}
// If the last block was empty, we definitely want to merge.
if ($last_block['empty']) {
return true; return true;
} }
// If this line is blank keep merging // If this block is empty, we definitely want to merge.
if ($curr_block['is_empty']) { if ($current_block['empty']) {
return true; return true;
} }
// If the current line and the last line have content, keep merging // Check if the last line of the previous block or the first line of this
if (strlen(trim($text[$curr_block['start'] - 1]))) { // block have any non-whitespace text. If they both do, we're going to
if (strlen(trim($text[$curr_block['start']]))) { // merge.
// If either of them are a blank line or a line with only whitespace, we
// do not merge: this means we've found a paragraph break.
$tail = $text[$current_block['start'] - 1];
$head = $text[$current_block['start']];
if (strlen(trim($tail)) && strlen(trim($head))) {
return true; return true;
} }
}
} catch (Exception $e) {}
return false; return false;
} }

View file

@ -0,0 +1,11 @@
> x
>
> y
~~~~~~~~~~
<blockquote><p>x</p>
<p>y</p></blockquote>
~~~~~~~~~~
> x
>
> y

View file

@ -2,6 +2,9 @@
abstract class PhabricatorObjectRemarkupRule extends PhutilRemarkupRule { abstract class PhabricatorObjectRemarkupRule extends PhutilRemarkupRule {
private $referencePattern;
private $embedPattern;
const KEY_RULE_OBJECT = 'rule.object'; const KEY_RULE_OBJECT = 'rule.object';
const KEY_MENTIONED_OBJECTS = 'rule.object.mentioned'; const KEY_MENTIONED_OBJECTS = 'rule.object.mentioned';
@ -192,14 +195,20 @@ abstract class PhabricatorObjectRemarkupRule extends PhutilRemarkupRule {
} }
private function getObjectEmbedPattern() { private function getObjectEmbedPattern() {
if ($this->embedPattern === null) {
$prefix = $this->getObjectNamePrefix(); $prefix = $this->getObjectNamePrefix();
$prefix = preg_quote($prefix); $prefix = preg_quote($prefix);
$id = $this->getObjectIDPattern(); $id = $this->getObjectIDPattern();
return '(\B{'.$prefix.'('.$id.')([,\s](?:[^}\\\\]|\\\\.)*)?}\B)u'; $this->embedPattern =
'(\B{'.$prefix.'('.$id.')([,\s](?:[^}\\\\]|\\\\.)*)?}\B)u';
}
return $this->embedPattern;
} }
private function getObjectReferencePattern() { private function getObjectReferencePattern() {
if ($this->referencePattern === null) {
$prefix = $this->getObjectNamePrefix(); $prefix = $this->getObjectNamePrefix();
$prefix = preg_quote($prefix); $prefix = preg_quote($prefix);
@ -223,7 +232,11 @@ abstract class PhabricatorObjectRemarkupRule extends PhutilRemarkupRule {
// The "\b" allows us to link "(abcdef)" or similar without linking things // The "\b" allows us to link "(abcdef)" or similar without linking things
// in the middle of words. // in the middle of words.
return '((?<![#@-])'.$boundary.$prefix.'('.$id.')(?:#([-\w\d]+))?(?!\w))u'; $this->referencePattern =
'((?<![#@-])'.$boundary.$prefix.'('.$id.')(?:#([-\w\d]+))?(?!\w))u';
}
return $this->referencePattern;
} }

View file

@ -0,0 +1,68 @@
<?php
final class PHUICurtainObjectRefListView
extends AphrontTagView {
private $refs = array();
private $emptyMessage;
private $tail = array();
protected function getTagAttributes() {
return array(
'class' => 'phui-curtain-object-ref-list-view',
);
}
public function setEmptyMessage($empty_message) {
$this->emptyMessage = $empty_message;
return $this;
}
protected function getTagContent() {
$refs = $this->refs;
if (!$refs && ($this->emptyMessage !== null)) {
$view = phutil_tag(
'div',
array(
'class' => 'phui-curtain-object-ref-list-view-empty',
),
$this->emptyMessage);
} else {
$view = $refs;
}
$tail = null;
if ($this->tail) {
$tail = phutil_tag(
'div',
array(
'class' => 'phui-curtain-object-ref-list-view-tail',
),
$this->tail);
}
return array(
$view,
$tail,
);
}
public function newObjectRefView() {
$ref_view = id(new PHUICurtainObjectRefView())
->setViewer($this->getViewer());
$this->refs[] = $ref_view;
return $ref_view;
}
public function newTailLink() {
$link = new PHUILinkView();
$this->tail[] = $link;
return $link;
}
}

View file

@ -0,0 +1,194 @@
<?php
final class PHUICurtainObjectRefView
extends AphrontTagView {
private $handle;
private $epoch;
private $highlighted;
public function setHandle(PhabricatorObjectHandle $handle) {
$this->handle = $handle;
return $this;
}
public function setEpoch($epoch) {
$this->epoch = $epoch;
return $this;
}
public function setHighlighted($highlighted) {
$this->highlighted = $highlighted;
return $this;
}
protected function getTagAttributes() {
$classes = array();
$classes[] = 'phui-curtain-object-ref-view';
if ($this->highlighted) {
$classes[] = 'phui-curtain-object-ref-view-highlighted';
}
$classes = implode(' ', $classes);
return array(
'class' => $classes,
);
}
protected function getTagContent() {
require_celerity_resource('phui-curtain-object-ref-view-css');
$viewer = $this->getViewer();
$handle = $this->handle;
$more_rows = array();
$epoch = $this->epoch;
if ($epoch !== null) {
$epoch_view = phabricator_datetime($epoch, $viewer);
$epoch_cells = array();
$epoch_cells[] = phutil_tag(
'td',
array(
'class' => 'phui-curtain-object-ref-view-epoch-cell',
),
$epoch_view);
$more_rows[] = phutil_tag('tr', array(), $epoch_cells);
}
$header_cells = array();
$image_view = $this->newImage();
if ($more_rows) {
$row_count = 1 + count($more_rows);
} else {
$row_count = null;
}
$header_cells[] = phutil_tag(
'td',
array(
'rowspan' => $row_count,
'class' => 'phui-curtain-object-ref-view-image-cell',
),
$image_view);
$title_view = $this->newTitle();
$header_cells[] = phutil_tag(
'td',
array(
'class' => 'phui-curtain-object-ref-view-title-cell',
),
$title_view);
$rows = array();
if (!$more_rows) {
$title_row_class = 'phui-curtain-object-ref-view-without-content';
} else {
$title_row_class = 'phui-curtain-object-ref-view-with-content';
}
$rows[] = phutil_tag(
'tr',
array(
'class' => $title_row_class,
),
$header_cells);
$body = phutil_tag(
'tbody',
array(),
array(
$rows,
$more_rows,
));
return phutil_tag('table', array(), $body);
}
private function newTitle() {
$title_view = null;
$handle = $this->handle;
if ($handle) {
$title_view = $handle->renderLink();
}
return $title_view;
}
private function newImage() {
$image_uri = $this->getImageURI();
$target_uri = $this->getTargetURI();
$icon_view = null;
if ($image_uri == null) {
$icon_view = $this->newIconView();
}
if ($image_uri !== null) {
$image_view = javelin_tag(
'a',
array(
'style' => sprintf('background-image: url(%s)', $image_uri),
'href' => $target_uri,
'aural' => false,
));
} else if ($icon_view !== null) {
$image_view = javelin_tag(
'a',
array(
'href' => $target_uri,
'class' => 'phui-curtain-object-ref-view-icon-image',
'aural' => false,
),
$icon_view);
} else {
$image_view = null;
}
return $image_view;
}
private function getTargetURI() {
$target_uri = null;
$handle = $this->handle;
if ($handle) {
$target_uri = $handle->getURI();
}
return $target_uri;
}
private function getImageURI() {
$image_uri = null;
$handle = $this->handle;
if ($handle) {
$image_uri = $handle->getImageURI();
}
return $image_uri;
}
private function newIconView() {
$handle = $this->handle;
if ($handle) {
$icon_view = id(new PHUIIconView())
->setIcon($handle->getIcon());
}
return $icon_view;
}
}

View file

@ -0,0 +1,20 @@
<?php
final class PHUILauncherView
extends AphrontTagView {
protected function getTagName() {
return 'div';
}
protected function getTagAttributes() {
$classes = array();
$classes[] = 'phui-launcher-view';
return array(
'class' => implode(' ', $classes),
);
}
}

View file

@ -0,0 +1,50 @@
<?php
final class PHUILinkView
extends AphrontTagView {
private $uri;
private $text;
private $workflow;
public function setURI($uri) {
$this->uri = $uri;
return $this;
}
public function getURI() {
return $this->uri;
}
public function setText($text) {
$this->text = $text;
return $this;
}
public function setWorkflow($workflow) {
$this->workflow = $workflow;
return $this;
}
protected function getTagName() {
return 'a';
}
protected function getTagAttributes() {
$sigil = array();
if ($this->workflow) {
$sigil[] = 'workflow';
}
return array(
'href' => $this->getURI(),
'sigil' => $sigil,
);
}
protected function getTagContent() {
return $this->text;
}
}

View file

@ -27,6 +27,7 @@ final class PHUIObjectBoxView extends AphrontTagView {
private $showHideOpen; private $showHideOpen;
private $propertyLists = array(); private $propertyLists = array();
private $tailButtons = array();
const COLOR_RED = 'red'; const COLOR_RED = 'red';
const COLOR_BLUE = 'blue'; const COLOR_BLUE = 'blue';
@ -153,6 +154,16 @@ final class PHUIObjectBoxView extends AphrontTagView {
return $this; return $this;
} }
public function newTailButton() {
$button = id(new PHUIButtonView())
->setTag('a')
->setColor(PHUIButtonView::GREY);
$this->tailButtons[] = $button;
return $button;
}
protected function getTagAttributes() { protected function getTagAttributes() {
$classes = array(); $classes = array();
$classes[] = 'phui-box'; $classes[] = 'phui-box';
@ -329,6 +340,15 @@ final class PHUIObjectBoxView extends AphrontTagView {
$content[] = $this->objectList; $content[] = $this->objectList;
} }
if ($this->tailButtons) {
$content[] = phutil_tag(
'div',
array(
'class' => 'phui-object-box-tail-buttons',
),
$this->tailButtons);
}
return $content; return $content;
} }
} }

View file

@ -0,0 +1,84 @@
/**
* @provides phui-curtain-object-ref-view-css
*/
.phui-curtain-object-ref-list-view-empty {
font-style: italic;
color: {$greytext};
}
.phui-curtain-object-ref-view {
padding: 4px 6px;
border-radius: 3px;
}
.phui-curtain-object-ref-view-image-cell {
min-width: 32px;
padding-bottom: 24px;
}
.phui-curtain-object-ref-view-image-cell > a {
height: 24px;
width: 24px;
background-size: 100%;
border-radius: 3px;
display: block;
position: absolute;
}
.phui-curtain-object-ref-view-image-cell .phui-icon-view {
font-size: 16px;
line-height: 16px;
vertical-align: middle;
text-align: center;
width: 24px;
height: 24px;
top: 3px;
display: block;
position: absolute;
color: #ffffff;
}
.phui-curtain-object-ref-view-icon-image {
background-color: {$backdrop};
}
.phui-curtain-object-ref-view-title-cell {
font-weight: bold;
text-overflow: ellipsis;
overflow: hidden;
/* This is forcing "text-overflow: ellipsis" to actually work. */
max-width: 210px;
}
.phui-curtain-object-ref-view-without-content >
.phui-curtain-object-ref-view-title-cell {
vertical-align: middle;
}
.phui-curtain-object-ref-view-with-content >
.phui-curtain-object-ref-view-image-cell > a {
margin-top: 4px;
}
.phui-curtain-object-ref-view-title-cell > a {
color: {$darkgreytext};
}
.phui-curtain-object-ref-view-epoch-cell {
color: {$greytext};
}
.phui-curtain-object-ref-list-view-tail {
text-align: center;
margin-top: 8px;
padding: 4px;
background: {$lightgreybackground};
border-top: 1px dashed {$thinblueborder};
box-shadow: inset 0 2px 3px rgba(0, 0, 0, 0.04);
}
.phui-curtain-object-ref-view-highlighted {
background: {$bluebackground};
}

View file

@ -62,6 +62,12 @@ div.phui-object-box.phui-object-box-flush {
font-size: {$normalfontsize}; font-size: {$normalfontsize};
} }
.phui-object-box-tail-buttons {
padding: 8px;
background: {$lightgreybackground};
border-top: 1px solid {$lightgreyborder};
}
/* - Object Box Colors ------------------------------------------------------ */ /* - Object Box Colors ------------------------------------------------------ */
.phui-box-border.phui-object-box-green { .phui-box-border.phui-object-box-green {

View file

@ -2,6 +2,11 @@
* @provides phui-two-column-view-css * @provides phui-two-column-view-css
*/ */
.phui-launcher-view {
max-width: 1140px;
margin: 0 auto;
}
.phui-two-column-fixed { .phui-two-column-fixed {
max-width: 1140px; max-width: 1140px;
margin: 0 auto; margin: 0 auto;