diff --git a/resources/builtin/project.png b/resources/builtin/project.png index 1e16790ea8..c6abf5ed42 100644 Binary files a/resources/builtin/project.png and b/resources/builtin/project.png differ diff --git a/resources/builtin/projects/v3/briefcase.png b/resources/builtin/projects/v3/briefcase.png new file mode 100644 index 0000000000..c6abf5ed42 Binary files /dev/null and b/resources/builtin/projects/v3/briefcase.png differ diff --git a/resources/builtin/projects/v3/bug.png b/resources/builtin/projects/v3/bug.png new file mode 100644 index 0000000000..bb2948a93a Binary files /dev/null and b/resources/builtin/projects/v3/bug.png differ diff --git a/resources/builtin/projects/v3/calendar.png b/resources/builtin/projects/v3/calendar.png new file mode 100644 index 0000000000..6ebdc2e08c Binary files /dev/null and b/resources/builtin/projects/v3/calendar.png differ diff --git a/resources/builtin/projects/v3/cloud.png b/resources/builtin/projects/v3/cloud.png new file mode 100644 index 0000000000..efb644001f Binary files /dev/null and b/resources/builtin/projects/v3/cloud.png differ diff --git a/resources/builtin/projects/v3/creditcard.png b/resources/builtin/projects/v3/creditcard.png new file mode 100644 index 0000000000..d231c9437d Binary files /dev/null and b/resources/builtin/projects/v3/creditcard.png differ diff --git a/resources/builtin/projects/v3/database.png b/resources/builtin/projects/v3/database.png new file mode 100644 index 0000000000..9e44c0ec58 Binary files /dev/null and b/resources/builtin/projects/v3/database.png differ diff --git a/resources/builtin/projects/v3/desktop.png b/resources/builtin/projects/v3/desktop.png new file mode 100644 index 0000000000..cf6f80eeaf Binary files /dev/null and b/resources/builtin/projects/v3/desktop.png differ diff --git a/resources/builtin/projects/v3/experimental.png b/resources/builtin/projects/v3/experimental.png new file mode 100644 index 0000000000..5bb05ac100 Binary files /dev/null and b/resources/builtin/projects/v3/experimental.png differ diff --git a/resources/builtin/projects/v3/flag.png b/resources/builtin/projects/v3/flag.png new file mode 100644 index 0000000000..c7d2563115 Binary files /dev/null and b/resources/builtin/projects/v3/flag.png differ diff --git a/resources/builtin/projects/v3/folder.png b/resources/builtin/projects/v3/folder.png new file mode 100644 index 0000000000..cac0f9fbfa Binary files /dev/null and b/resources/builtin/projects/v3/folder.png differ diff --git a/resources/builtin/projects/v3/lock.png b/resources/builtin/projects/v3/lock.png new file mode 100644 index 0000000000..839cb1e5bf Binary files /dev/null and b/resources/builtin/projects/v3/lock.png differ diff --git a/resources/builtin/projects/v3/mail.png b/resources/builtin/projects/v3/mail.png new file mode 100644 index 0000000000..17f91ea881 Binary files /dev/null and b/resources/builtin/projects/v3/mail.png differ diff --git a/resources/builtin/projects/v3/mobile.png b/resources/builtin/projects/v3/mobile.png new file mode 100644 index 0000000000..fbb1985015 Binary files /dev/null and b/resources/builtin/projects/v3/mobile.png differ diff --git a/resources/builtin/projects/v3/organization.png b/resources/builtin/projects/v3/organization.png new file mode 100644 index 0000000000..1957dd8e68 Binary files /dev/null and b/resources/builtin/projects/v3/organization.png differ diff --git a/resources/builtin/projects/v3/people.png b/resources/builtin/projects/v3/people.png new file mode 100644 index 0000000000..5bb42656df Binary files /dev/null and b/resources/builtin/projects/v3/people.png differ diff --git a/resources/builtin/projects/v3/servers.png b/resources/builtin/projects/v3/servers.png new file mode 100644 index 0000000000..df20dd6492 Binary files /dev/null and b/resources/builtin/projects/v3/servers.png differ diff --git a/resources/builtin/projects/v3/tag.png b/resources/builtin/projects/v3/tag.png new file mode 100644 index 0000000000..c91bac473a Binary files /dev/null and b/resources/builtin/projects/v3/tag.png differ diff --git a/resources/builtin/projects/v3/trash.png b/resources/builtin/projects/v3/trash.png new file mode 100644 index 0000000000..ce3f557d44 Binary files /dev/null and b/resources/builtin/projects/v3/trash.png differ diff --git a/resources/builtin/projects/v3/truck.png b/resources/builtin/projects/v3/truck.png new file mode 100644 index 0000000000..3c903ea58b Binary files /dev/null and b/resources/builtin/projects/v3/truck.png differ diff --git a/resources/builtin/projects/v3/umbrella.png b/resources/builtin/projects/v3/umbrella.png new file mode 100644 index 0000000000..98c7c12365 Binary files /dev/null and b/resources/builtin/projects/v3/umbrella.png differ diff --git a/resources/sshd/sshd_config.phabricator.example b/resources/sshd/sshd_config.phabricator.example index 2f2a581223..506d32bbbf 100644 --- a/resources/sshd/sshd_config.phabricator.example +++ b/resources/sshd/sshd_config.phabricator.example @@ -18,6 +18,7 @@ AllowTcpForwarding no PrintMotd no PrintLastLog no PasswordAuthentication no +ChallengeResponseAuthentication no AuthorizedKeysFile none PidFile /var/run/sshd-phabricator.pid diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 9deeb338f6..095759a351 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1488,8 +1488,8 @@ phutil_register_library_map(array( 'ManiphestHovercardEngineExtension' => 'applications/maniphest/engineextension/ManiphestHovercardEngineExtension.php', 'ManiphestInfoConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php', 'ManiphestNameIndex' => 'applications/maniphest/storage/ManiphestNameIndex.php', - 'ManiphestPointsConfigOptionType' => 'applications/maniphest/config/ManiphestPointsConfigOptionType.php', - 'ManiphestPriorityConfigOptionType' => 'applications/maniphest/config/ManiphestPriorityConfigOptionType.php', + 'ManiphestPointsConfigType' => 'applications/maniphest/config/ManiphestPointsConfigType.php', + 'ManiphestPrioritiesConfigType' => 'applications/maniphest/config/ManiphestPrioritiesConfigType.php', 'ManiphestPriorityEmailCommand' => 'applications/maniphest/command/ManiphestPriorityEmailCommand.php', 'ManiphestPrioritySearchConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestPrioritySearchConduitAPIMethod.php', 'ManiphestProjectNameFulltextEngineExtension' => 'applications/maniphest/engineextension/ManiphestProjectNameFulltextEngineExtension.php', @@ -1500,11 +1500,11 @@ phutil_register_library_map(array( 'ManiphestReportController' => 'applications/maniphest/controller/ManiphestReportController.php', 'ManiphestSchemaSpec' => 'applications/maniphest/storage/ManiphestSchemaSpec.php', 'ManiphestSearchConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestSearchConduitAPIMethod.php', - 'ManiphestStatusConfigOptionType' => 'applications/maniphest/config/ManiphestStatusConfigOptionType.php', 'ManiphestStatusEmailCommand' => 'applications/maniphest/command/ManiphestStatusEmailCommand.php', 'ManiphestStatusSearchConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestStatusSearchConduitAPIMethod.php', + 'ManiphestStatusesConfigType' => 'applications/maniphest/config/ManiphestStatusesConfigType.php', 'ManiphestSubpriorityController' => 'applications/maniphest/controller/ManiphestSubpriorityController.php', - 'ManiphestSubtypesConfigOptionsType' => 'applications/maniphest/config/ManiphestSubtypesConfigOptionsType.php', + 'ManiphestSubtypesConfigType' => 'applications/maniphest/config/ManiphestSubtypesConfigType.php', 'ManiphestTask' => 'applications/maniphest/storage/ManiphestTask.php', 'ManiphestTaskAssignHeraldAction' => 'applications/maniphest/herald/ManiphestTaskAssignHeraldAction.php', 'ManiphestTaskAssignOtherHeraldAction' => 'applications/maniphest/herald/ManiphestTaskAssignOtherHeraldAction.php', @@ -2153,6 +2153,7 @@ phutil_register_library_map(array( 'PhabricatorBoardLayoutEngine' => 'applications/project/engine/PhabricatorBoardLayoutEngine.php', 'PhabricatorBoardRenderingEngine' => 'applications/project/engine/PhabricatorBoardRenderingEngine.php', 'PhabricatorBoardResponseEngine' => 'applications/project/engine/PhabricatorBoardResponseEngine.php', + 'PhabricatorBoolConfigType' => 'applications/config/type/PhabricatorBoolConfigType.php', 'PhabricatorBoolEditField' => 'applications/transactions/editfield/PhabricatorBoolEditField.php', 'PhabricatorBritishEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorBritishEnglishTranslation.php', 'PhabricatorBuiltinDraftEngine' => 'applications/transactions/draft/PhabricatorBuiltinDraftEngine.php', @@ -2328,14 +2329,15 @@ phutil_register_library_map(array( 'PhabricatorChatLogEvent' => 'applications/chatlog/storage/PhabricatorChatLogEvent.php', 'PhabricatorChatLogQuery' => 'applications/chatlog/query/PhabricatorChatLogQuery.php', 'PhabricatorChunkedFileStorageEngine' => 'applications/files/engine/PhabricatorChunkedFileStorageEngine.php', + 'PhabricatorClassConfigType' => 'applications/config/type/PhabricatorClassConfigType.php', 'PhabricatorClusterConfigOptions' => 'applications/config/option/PhabricatorClusterConfigOptions.php', - 'PhabricatorClusterDatabasesConfigOptionType' => 'infrastructure/cluster/config/PhabricatorClusterDatabasesConfigOptionType.php', + 'PhabricatorClusterDatabasesConfigType' => 'infrastructure/cluster/config/PhabricatorClusterDatabasesConfigType.php', 'PhabricatorClusterException' => 'infrastructure/cluster/exception/PhabricatorClusterException.php', 'PhabricatorClusterExceptionHandler' => 'infrastructure/cluster/exception/PhabricatorClusterExceptionHandler.php', 'PhabricatorClusterImpossibleWriteException' => 'infrastructure/cluster/exception/PhabricatorClusterImpossibleWriteException.php', 'PhabricatorClusterImproperWriteException' => 'infrastructure/cluster/exception/PhabricatorClusterImproperWriteException.php', 'PhabricatorClusterNoHostForRoleException' => 'infrastructure/cluster/exception/PhabricatorClusterNoHostForRoleException.php', - 'PhabricatorClusterSearchConfigOptionType' => 'infrastructure/cluster/config/PhabricatorClusterSearchConfigOptionType.php', + 'PhabricatorClusterSearchConfigType' => 'infrastructure/cluster/config/PhabricatorClusterSearchConfigType.php', 'PhabricatorClusterServiceHealthRecord' => 'infrastructure/cluster/PhabricatorClusterServiceHealthRecord.php', 'PhabricatorClusterStrandedException' => 'infrastructure/cluster/exception/PhabricatorClusterStrandedException.php', 'PhabricatorColumnProxyInterface' => 'applications/project/interface/PhabricatorColumnProxyInterface.php', @@ -2448,6 +2450,7 @@ phutil_register_library_map(array( 'PhabricatorConfigTableSchema' => 'applications/config/schema/PhabricatorConfigTableSchema.php', 'PhabricatorConfigTransaction' => 'applications/config/storage/PhabricatorConfigTransaction.php', 'PhabricatorConfigTransactionQuery' => 'applications/config/query/PhabricatorConfigTransactionQuery.php', + 'PhabricatorConfigType' => 'applications/config/type/PhabricatorConfigType.php', 'PhabricatorConfigValidationException' => 'applications/config/exception/PhabricatorConfigValidationException.php', 'PhabricatorConfigVersionController' => 'applications/config/controller/PhabricatorConfigVersionController.php', 'PhabricatorConpherenceApplication' => 'applications/conpherence/application/PhabricatorConpherenceApplication.php', @@ -2754,6 +2757,7 @@ phutil_register_library_map(array( 'PhabricatorEmojiRemarkupRule' => 'applications/macro/markup/PhabricatorEmojiRemarkupRule.php', 'PhabricatorEmojiTranslation' => 'infrastructure/internationalization/translation/PhabricatorEmojiTranslation.php', 'PhabricatorEmptyQueryException' => 'infrastructure/query/PhabricatorEmptyQueryException.php', + 'PhabricatorEnumConfigType' => 'applications/config/type/PhabricatorEnumConfigType.php', 'PhabricatorEnv' => 'infrastructure/env/PhabricatorEnv.php', 'PhabricatorEnvTestCase' => 'infrastructure/env/__tests__/PhabricatorEnvTestCase.php', 'PhabricatorEpochEditField' => 'applications/transactions/editfield/PhabricatorEpochEditField.php', @@ -2997,6 +3001,7 @@ phutil_register_library_map(array( 'PhabricatorInlineCommentPreviewController' => 'infrastructure/diff/PhabricatorInlineCommentPreviewController.php', 'PhabricatorInlineSummaryView' => 'infrastructure/diff/view/PhabricatorInlineSummaryView.php', 'PhabricatorInstructionsEditField' => 'applications/transactions/editfield/PhabricatorInstructionsEditField.php', + 'PhabricatorIntConfigType' => 'applications/config/type/PhabricatorIntConfigType.php', 'PhabricatorInternalSetting' => 'applications/settings/setting/PhabricatorInternalSetting.php', 'PhabricatorInternationalizationManagementExtractWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementExtractWorkflow.php', 'PhabricatorInternationalizationManagementWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementWorkflow.php', @@ -3005,6 +3010,7 @@ phutil_register_library_map(array( 'PhabricatorIteratedMD5PasswordHasherTestCase' => 'infrastructure/util/password/__tests__/PhabricatorIteratedMD5PasswordHasherTestCase.php', 'PhabricatorIteratorFileUploadSource' => 'applications/files/uploadsource/PhabricatorIteratorFileUploadSource.php', 'PhabricatorJIRAAuthProvider' => 'applications/auth/provider/PhabricatorJIRAAuthProvider.php', + 'PhabricatorJSONConfigType' => 'applications/config/type/PhabricatorJSONConfigType.php', 'PhabricatorJavelinLinter' => 'infrastructure/lint/linter/PhabricatorJavelinLinter.php', 'PhabricatorJiraIssueHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorJiraIssueHasObjectEdgeType.php', 'PhabricatorJumpNavHandler' => 'applications/search/engine/PhabricatorJumpNavHandler.php', @@ -3193,7 +3199,7 @@ phutil_register_library_map(array( 'PhabricatorNotificationQuery' => 'applications/notification/query/PhabricatorNotificationQuery.php', 'PhabricatorNotificationSearchEngine' => 'applications/notification/query/PhabricatorNotificationSearchEngine.php', 'PhabricatorNotificationServerRef' => 'applications/notification/client/PhabricatorNotificationServerRef.php', - 'PhabricatorNotificationServersConfigOptionType' => 'applications/notification/config/PhabricatorNotificationServersConfigOptionType.php', + 'PhabricatorNotificationServersConfigType' => 'applications/notification/config/PhabricatorNotificationServersConfigType.php', 'PhabricatorNotificationStatusView' => 'applications/notification/view/PhabricatorNotificationStatusView.php', 'PhabricatorNotificationTestController' => 'applications/notification/controller/PhabricatorNotificationTestController.php', 'PhabricatorNotificationTestFeedStory' => 'applications/notification/feed/PhabricatorNotificationTestFeedStory.php', @@ -3608,7 +3614,7 @@ phutil_register_library_map(array( 'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php', 'PhabricatorProjectCardView' => 'applications/project/view/PhabricatorProjectCardView.php', 'PhabricatorProjectColorTransaction' => 'applications/project/xaction/PhabricatorProjectColorTransaction.php', - 'PhabricatorProjectColorsConfigOptionType' => 'applications/project/config/PhabricatorProjectColorsConfigOptionType.php', + 'PhabricatorProjectColorsConfigType' => 'applications/project/config/PhabricatorProjectColorsConfigType.php', 'PhabricatorProjectColumn' => 'applications/project/storage/PhabricatorProjectColumn.php', 'PhabricatorProjectColumnDetailController' => 'applications/project/controller/PhabricatorProjectColumnDetailController.php', 'PhabricatorProjectColumnEditController' => 'applications/project/controller/PhabricatorProjectColumnEditController.php', @@ -3646,7 +3652,7 @@ phutil_register_library_map(array( 'PhabricatorProjectHovercardEngineExtension' => 'applications/project/engineextension/PhabricatorProjectHovercardEngineExtension.php', 'PhabricatorProjectIconSet' => 'applications/project/icon/PhabricatorProjectIconSet.php', 'PhabricatorProjectIconTransaction' => 'applications/project/xaction/PhabricatorProjectIconTransaction.php', - 'PhabricatorProjectIconsConfigOptionType' => 'applications/project/config/PhabricatorProjectIconsConfigOptionType.php', + 'PhabricatorProjectIconsConfigType' => 'applications/project/config/PhabricatorProjectIconsConfigType.php', 'PhabricatorProjectImageTransaction' => 'applications/project/xaction/PhabricatorProjectImageTransaction.php', 'PhabricatorProjectInterface' => 'applications/project/interface/PhabricatorProjectInterface.php', 'PhabricatorProjectListController' => 'applications/project/controller/PhabricatorProjectListController.php', @@ -3743,6 +3749,7 @@ phutil_register_library_map(array( 'PhabricatorRecaptchaConfigOptions' => 'applications/config/option/PhabricatorRecaptchaConfigOptions.php', 'PhabricatorRedirectController' => 'applications/base/controller/PhabricatorRedirectController.php', 'PhabricatorRefreshCSRFController' => 'applications/auth/controller/PhabricatorRefreshCSRFController.php', + 'PhabricatorRegexListConfigType' => 'applications/config/type/PhabricatorRegexListConfigType.php', 'PhabricatorRegistrationProfile' => 'applications/people/storage/PhabricatorRegistrationProfile.php', 'PhabricatorReleephApplication' => 'applications/releeph/application/PhabricatorReleephApplication.php', 'PhabricatorReleephApplicationConfigOptions' => 'applications/releeph/config/PhabricatorReleephApplicationConfigOptions.php', @@ -3937,6 +3944,7 @@ phutil_register_library_map(array( 'PhabricatorSelectSetting' => 'applications/settings/setting/PhabricatorSelectSetting.php', 'PhabricatorSendGridConfigOptions' => 'applications/config/option/PhabricatorSendGridConfigOptions.php', 'PhabricatorSessionsSettingsPanel' => 'applications/settings/panel/PhabricatorSessionsSettingsPanel.php', + 'PhabricatorSetConfigType' => 'applications/config/type/PhabricatorSetConfigType.php', 'PhabricatorSetting' => 'applications/settings/setting/PhabricatorSetting.php', 'PhabricatorSettingsAccountPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsAccountPanelGroup.php', 'PhabricatorSettingsAddEmailAction' => 'applications/settings/action/PhabricatorSettingsAddEmailAction.php', @@ -4067,6 +4075,8 @@ phutil_register_library_map(array( 'PhabricatorStoragePatch' => 'infrastructure/storage/management/PhabricatorStoragePatch.php', 'PhabricatorStorageSchemaSpec' => 'infrastructure/storage/schema/PhabricatorStorageSchemaSpec.php', 'PhabricatorStorageSetupCheck' => 'applications/config/check/PhabricatorStorageSetupCheck.php', + 'PhabricatorStringConfigType' => 'applications/config/type/PhabricatorStringConfigType.php', + 'PhabricatorStringListConfigType' => 'applications/config/type/PhabricatorStringListConfigType.php', 'PhabricatorStringListEditField' => 'applications/transactions/editfield/PhabricatorStringListEditField.php', 'PhabricatorStringSetting' => 'applications/settings/setting/PhabricatorStringSetting.php', 'PhabricatorSubmitEditField' => 'applications/transactions/editfield/PhabricatorSubmitEditField.php', @@ -4127,7 +4137,9 @@ phutil_register_library_map(array( 'PhabricatorTestStorageEngine' => 'applications/files/engine/PhabricatorTestStorageEngine.php', 'PhabricatorTestWorker' => 'infrastructure/daemon/workers/__tests__/PhabricatorTestWorker.php', 'PhabricatorTextAreaEditField' => 'applications/transactions/editfield/PhabricatorTextAreaEditField.php', + 'PhabricatorTextConfigType' => 'applications/config/type/PhabricatorTextConfigType.php', 'PhabricatorTextEditField' => 'applications/transactions/editfield/PhabricatorTextEditField.php', + 'PhabricatorTextListConfigType' => 'applications/config/type/PhabricatorTextListConfigType.php', 'PhabricatorTime' => 'infrastructure/time/PhabricatorTime.php', 'PhabricatorTimeFormatSetting' => 'applications/settings/setting/PhabricatorTimeFormatSetting.php', 'PhabricatorTimeGuard' => 'infrastructure/time/PhabricatorTimeGuard.php', @@ -4252,6 +4264,7 @@ phutil_register_library_map(array( 'PhabricatorWebContentSource' => 'infrastructure/contentsource/PhabricatorWebContentSource.php', 'PhabricatorWebServerSetupCheck' => 'applications/config/check/PhabricatorWebServerSetupCheck.php', 'PhabricatorWeekStartDaySetting' => 'applications/settings/setting/PhabricatorWeekStartDaySetting.php', + 'PhabricatorWildConfigType' => 'applications/config/type/PhabricatorWildConfigType.php', 'PhabricatorWordPressAuthProvider' => 'applications/auth/provider/PhabricatorWordPressAuthProvider.php', 'PhabricatorWorker' => 'infrastructure/daemon/workers/PhabricatorWorker.php', 'PhabricatorWorkerActiveTask' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerActiveTask.php', @@ -6590,8 +6603,8 @@ phutil_register_library_map(array( 'ManiphestHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension', 'ManiphestInfoConduitAPIMethod' => 'ManiphestConduitAPIMethod', 'ManiphestNameIndex' => 'ManiphestDAO', - 'ManiphestPointsConfigOptionType' => 'PhabricatorConfigJSONOptionType', - 'ManiphestPriorityConfigOptionType' => 'PhabricatorConfigJSONOptionType', + 'ManiphestPointsConfigType' => 'PhabricatorJSONConfigType', + 'ManiphestPrioritiesConfigType' => 'PhabricatorJSONConfigType', 'ManiphestPriorityEmailCommand' => 'ManiphestEmailCommand', 'ManiphestPrioritySearchConduitAPIMethod' => 'ManiphestConduitAPIMethod', 'ManiphestProjectNameFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension', @@ -6602,11 +6615,11 @@ phutil_register_library_map(array( 'ManiphestReportController' => 'ManiphestController', 'ManiphestSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'ManiphestSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', - 'ManiphestStatusConfigOptionType' => 'PhabricatorConfigJSONOptionType', 'ManiphestStatusEmailCommand' => 'ManiphestEmailCommand', 'ManiphestStatusSearchConduitAPIMethod' => 'ManiphestConduitAPIMethod', + 'ManiphestStatusesConfigType' => 'PhabricatorJSONConfigType', 'ManiphestSubpriorityController' => 'ManiphestController', - 'ManiphestSubtypesConfigOptionsType' => 'PhabricatorConfigJSONOptionType', + 'ManiphestSubtypesConfigType' => 'PhabricatorJSONConfigType', 'ManiphestTask' => array( 'ManiphestDAO', 'PhabricatorSubscribableInterface', @@ -7348,6 +7361,7 @@ phutil_register_library_map(array( 'PhabricatorBoardLayoutEngine' => 'Phobject', 'PhabricatorBoardRenderingEngine' => 'Phobject', 'PhabricatorBoardResponseEngine' => 'Phobject', + 'PhabricatorBoolConfigType' => 'PhabricatorTextConfigType', 'PhabricatorBoolEditField' => 'PhabricatorEditField', 'PhabricatorBritishEnglishTranslation' => 'PhutilTranslation', 'PhabricatorBuiltinDraftEngine' => 'PhabricatorDraftEngine', @@ -7565,14 +7579,15 @@ phutil_register_library_map(array( ), 'PhabricatorChatLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorChunkedFileStorageEngine' => 'PhabricatorFileStorageEngine', + 'PhabricatorClassConfigType' => 'PhabricatorTextConfigType', 'PhabricatorClusterConfigOptions' => 'PhabricatorApplicationConfigOptions', - 'PhabricatorClusterDatabasesConfigOptionType' => 'PhabricatorConfigJSONOptionType', + 'PhabricatorClusterDatabasesConfigType' => 'PhabricatorJSONConfigType', 'PhabricatorClusterException' => 'Exception', 'PhabricatorClusterExceptionHandler' => 'PhabricatorRequestExceptionHandler', 'PhabricatorClusterImpossibleWriteException' => 'PhabricatorClusterException', 'PhabricatorClusterImproperWriteException' => 'PhabricatorClusterException', 'PhabricatorClusterNoHostForRoleException' => 'Exception', - 'PhabricatorClusterSearchConfigOptionType' => 'PhabricatorConfigJSONOptionType', + 'PhabricatorClusterSearchConfigType' => 'PhabricatorJSONConfigType', 'PhabricatorClusterServiceHealthRecord' => 'Phobject', 'PhabricatorClusterStrandedException' => 'PhabricatorClusterException', 'PhabricatorColumnsEditField' => 'PhabricatorPHIDListEditField', @@ -7697,6 +7712,7 @@ phutil_register_library_map(array( 'PhabricatorConfigTableSchema' => 'PhabricatorConfigStorageSchema', 'PhabricatorConfigTransaction' => 'PhabricatorApplicationTransaction', 'PhabricatorConfigTransactionQuery' => 'PhabricatorApplicationTransactionQuery', + 'PhabricatorConfigType' => 'Phobject', 'PhabricatorConfigValidationException' => 'Exception', 'PhabricatorConfigVersionController' => 'PhabricatorConfigController', 'PhabricatorConpherenceApplication' => 'PhabricatorApplication', @@ -8040,6 +8056,7 @@ phutil_register_library_map(array( 'PhabricatorEmojiRemarkupRule' => 'PhutilRemarkupRule', 'PhabricatorEmojiTranslation' => 'PhutilTranslation', 'PhabricatorEmptyQueryException' => 'Exception', + 'PhabricatorEnumConfigType' => 'PhabricatorTextConfigType', 'PhabricatorEnv' => 'Phobject', 'PhabricatorEnvTestCase' => 'PhabricatorTestCase', 'PhabricatorEpochEditField' => 'PhabricatorEditField', @@ -8324,6 +8341,7 @@ phutil_register_library_map(array( 'PhabricatorInlineCommentPreviewController' => 'PhabricatorController', 'PhabricatorInlineSummaryView' => 'AphrontView', 'PhabricatorInstructionsEditField' => 'PhabricatorEditField', + 'PhabricatorIntConfigType' => 'PhabricatorTextConfigType', 'PhabricatorInternalSetting' => 'PhabricatorSetting', 'PhabricatorInternationalizationManagementExtractWorkflow' => 'PhabricatorInternationalizationManagementWorkflow', 'PhabricatorInternationalizationManagementWorkflow' => 'PhabricatorManagementWorkflow', @@ -8332,6 +8350,7 @@ phutil_register_library_map(array( 'PhabricatorIteratedMD5PasswordHasherTestCase' => 'PhabricatorTestCase', 'PhabricatorIteratorFileUploadSource' => 'PhabricatorFileUploadSource', 'PhabricatorJIRAAuthProvider' => 'PhabricatorOAuth1AuthProvider', + 'PhabricatorJSONConfigType' => 'PhabricatorTextConfigType', 'PhabricatorJavelinLinter' => 'ArcanistLinter', 'PhabricatorJiraIssueHasObjectEdgeType' => 'PhabricatorEdgeType', 'PhabricatorJumpNavHandler' => 'Phobject', @@ -8533,7 +8552,7 @@ phutil_register_library_map(array( 'PhabricatorNotificationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorNotificationSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorNotificationServerRef' => 'Phobject', - 'PhabricatorNotificationServersConfigOptionType' => 'PhabricatorConfigJSONOptionType', + 'PhabricatorNotificationServersConfigType' => 'PhabricatorJSONConfigType', 'PhabricatorNotificationStatusView' => 'AphrontTagView', 'PhabricatorNotificationTestController' => 'PhabricatorNotificationController', 'PhabricatorNotificationTestFeedStory' => 'PhabricatorFeedStory', @@ -9046,7 +9065,7 @@ phutil_register_library_map(array( 'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectCardView' => 'AphrontTagView', 'PhabricatorProjectColorTransaction' => 'PhabricatorProjectTransactionType', - 'PhabricatorProjectColorsConfigOptionType' => 'PhabricatorConfigJSONOptionType', + 'PhabricatorProjectColorsConfigType' => 'PhabricatorJSONConfigType', 'PhabricatorProjectColumn' => array( 'PhabricatorProjectDAO', 'PhabricatorApplicationTransactionInterface', @@ -9097,7 +9116,7 @@ phutil_register_library_map(array( 'PhabricatorProjectHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension', 'PhabricatorProjectIconSet' => 'PhabricatorIconSet', 'PhabricatorProjectIconTransaction' => 'PhabricatorProjectTransactionType', - 'PhabricatorProjectIconsConfigOptionType' => 'PhabricatorConfigJSONOptionType', + 'PhabricatorProjectIconsConfigType' => 'PhabricatorJSONConfigType', 'PhabricatorProjectImageTransaction' => 'PhabricatorProjectTransactionType', 'PhabricatorProjectListController' => 'PhabricatorProjectController', 'PhabricatorProjectListView' => 'AphrontView', @@ -9199,6 +9218,7 @@ phutil_register_library_map(array( 'PhabricatorRecaptchaConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorRedirectController' => 'PhabricatorController', 'PhabricatorRefreshCSRFController' => 'PhabricatorAuthController', + 'PhabricatorRegexListConfigType' => 'PhabricatorTextListConfigType', 'PhabricatorRegistrationProfile' => 'Phobject', 'PhabricatorReleephApplication' => 'PhabricatorApplication', 'PhabricatorReleephApplicationConfigOptions' => 'PhabricatorApplicationConfigOptions', @@ -9453,6 +9473,7 @@ phutil_register_library_map(array( 'PhabricatorSelectSetting' => 'PhabricatorSetting', 'PhabricatorSendGridConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorSessionsSettingsPanel' => 'PhabricatorSettingsPanel', + 'PhabricatorSetConfigType' => 'PhabricatorTextConfigType', 'PhabricatorSetting' => 'Phobject', 'PhabricatorSettingsAccountPanelGroup' => 'PhabricatorSettingsPanelGroup', 'PhabricatorSettingsAddEmailAction' => 'PhabricatorSystemAction', @@ -9600,6 +9621,8 @@ phutil_register_library_map(array( 'PhabricatorStoragePatch' => 'Phobject', 'PhabricatorStorageSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'PhabricatorStorageSetupCheck' => 'PhabricatorSetupCheck', + 'PhabricatorStringConfigType' => 'PhabricatorTextConfigType', + 'PhabricatorStringListConfigType' => 'PhabricatorTextListConfigType', 'PhabricatorStringListEditField' => 'PhabricatorEditField', 'PhabricatorStringSetting' => 'PhabricatorSetting', 'PhabricatorSubmitEditField' => 'PhabricatorEditField', @@ -9659,7 +9682,9 @@ phutil_register_library_map(array( 'PhabricatorTestStorageEngine' => 'PhabricatorFileStorageEngine', 'PhabricatorTestWorker' => 'PhabricatorWorker', 'PhabricatorTextAreaEditField' => 'PhabricatorEditField', + 'PhabricatorTextConfigType' => 'PhabricatorConfigType', 'PhabricatorTextEditField' => 'PhabricatorEditField', + 'PhabricatorTextListConfigType' => 'PhabricatorTextConfigType', 'PhabricatorTime' => 'Phobject', 'PhabricatorTimeFormatSetting' => 'PhabricatorSelectSetting', 'PhabricatorTimeGuard' => 'Phobject', @@ -9817,6 +9842,7 @@ phutil_register_library_map(array( 'PhabricatorWebContentSource' => 'PhabricatorContentSource', 'PhabricatorWebServerSetupCheck' => 'PhabricatorSetupCheck', 'PhabricatorWeekStartDaySetting' => 'PhabricatorSelectSetting', + 'PhabricatorWildConfigType' => 'PhabricatorJSONConfigType', 'PhabricatorWordPressAuthProvider' => 'PhabricatorOAuth2AuthProvider', 'PhabricatorWorker' => 'Phobject', 'PhabricatorWorkerActiveTask' => 'PhabricatorWorkerTask', diff --git a/src/applications/config/controller/PhabricatorConfigEditController.php b/src/applications/config/controller/PhabricatorConfigEditController.php index 71b4499591..783fcb4dd3 100644 --- a/src/applications/config/controller/PhabricatorConfigEditController.php +++ b/src/applications/config/controller/PhabricatorConfigEditController.php @@ -274,6 +274,56 @@ final class PhabricatorConfigEditController PhabricatorConfigOption $option, AphrontRequest $request) { + $type = $option->newOptionType(); + if ($type) { + $is_set = $type->isValuePresentInRequest($option, $request); + if ($is_set) { + $value = $type->readValueFromRequest($option, $request); + + $errors = array(); + try { + $canonical_value = $type->newValueFromRequestValue( + $option, + $value); + $type->validateStoredValue($option, $canonical_value); + $xaction = $type->newTransaction($option, $canonical_value); + } catch (PhabricatorConfigValidationException $ex) { + $errors[] = $ex->getMessage(); + $xaction = null; + } catch (Exception $ex) { + // NOTE: Some older validators throw bare exceptions. Purely in good + // taste, it would be nice to convert these at some point. + $errors[] = $ex->getMessage(); + $xaction = null; + } + + return array( + $errors ? pht('Invalid') : null, + $errors, + $value, + $xaction, + ); + } else { + $delete_xaction = id(new PhabricatorConfigTransaction()) + ->setTransactionType(PhabricatorConfigTransaction::TYPE_EDIT) + ->setNewValue( + array( + 'deleted' => true, + 'value' => null, + )); + + return array( + null, + array(), + null, + $delete_xaction, + ); + } + } + + // TODO: If we missed on the new `PhabricatorConfigType` map, fall back + // to the old semi-modular, semi-hacky way of doing things. + $xaction = new PhabricatorConfigTransaction(); $xaction->setTransactionType(PhabricatorConfigTransaction::TYPE_EDIT); @@ -284,92 +334,10 @@ final class PhabricatorConfigEditController $info = $option->getCustomObject()->readRequest($option, $request); list($e_value, $errors, $set_value, $value) = $info; } else { - $value = $request->getStr('value'); - if (!strlen($value)) { - $value = null; - - $xaction->setNewValue( - array( - 'deleted' => true, - 'value' => null, - )); - - return array($e_value, $errors, $value, $xaction); - } - - $type = $option->getType(); - $set_value = null; - - switch ($type) { - case 'int': - if (preg_match('/^-?[0-9]+$/', trim($value))) { - $set_value = (int)$value; - } else { - $e_value = pht('Invalid'); - $errors[] = pht('Value must be an integer.'); - } - break; - case 'string': - case 'enum': - $set_value = (string)$value; - break; - case 'list': - case 'list': - $set_value = phutil_split_lines( - $request->getStr('value'), - $retain_endings = false); - - foreach ($set_value as $key => $v) { - if (!strlen($v)) { - unset($set_value[$key]); - } - } - $set_value = array_values($set_value); - - break; - case 'set': - $set_value = array_fill_keys($request->getStrList('value'), true); - break; - case 'bool': - switch ($value) { - case 'true': - $set_value = true; - break; - case 'false': - $set_value = false; - break; - default: - $e_value = pht('Invalid'); - $errors[] = pht('Value must be boolean, "true" or "false".'); - break; - } - break; - case 'class': - if (!class_exists($value)) { - $e_value = pht('Invalid'); - $errors[] = pht('Class does not exist.'); - } else { - $base = $option->getBaseClass(); - if (!is_subclass_of($value, $base)) { - $e_value = pht('Invalid'); - $errors[] = pht('Class is not of valid type.'); - } else { - $set_value = $value; - } - } - break; - default: - $json = json_decode($value, true); - if ($json === null && strtolower($value) != 'null') { - $e_value = pht('Invalid'); - $errors[] = pht( - 'The given value must be valid JSON. This means, among '. - 'other things, that you must wrap strings in double-quotes.'); - } else { - $set_value = $json; - } - break; - } + throw new Exception( + pht( + 'Unknown configuration option type "%s".', + $option->getType())); } if (!$errors) { @@ -390,30 +358,22 @@ final class PhabricatorConfigEditController PhabricatorConfigEntry $entry, $value) { + $type = $option->newOptionType(); + if ($type) { + return $type->newDisplayValue($option, $value); + } + if ($option->isCustomType()) { return $option->getCustomObject()->getDisplayValue( $option, $entry, $value); - } else { - $type = $option->getType(); - switch ($type) { - case 'int': - case 'string': - case 'enum': - case 'class': - return $value; - case 'bool': - return $value ? 'true' : 'false'; - case 'list': - case 'list': - return implode("\n", nonempty($value, array())); - case 'set': - return implode("\n", nonempty(array_keys($value), array())); - default: - return PhabricatorConfigJSON::prettyPrintJSON($value); - } } + + throw new Exception( + pht( + 'Unknown configuration option type "%s".', + $option->getType())); } private function renderControls( @@ -421,75 +381,24 @@ final class PhabricatorConfigEditController $display_value, $e_value) { + $type = $option->newOptionType(); + if ($type) { + return $type->newControls( + $option, + $display_value, + $e_value); + } + if ($option->isCustomType()) { $controls = $option->getCustomObject()->renderControls( $option, $display_value, $e_value); } else { - $type = $option->getType(); - switch ($type) { - case 'int': - case 'string': - $control = id(new AphrontFormTextControl()); - break; - case 'bool': - $control = id(new AphrontFormSelectControl()) - ->setOptions( - array( - '' => pht('(Use Default)'), - 'true' => idx($option->getBoolOptions(), 0), - 'false' => idx($option->getBoolOptions(), 1), - )); - break; - case 'enum': - $options = array_mergev( - array( - array('' => pht('(Use Default)')), - $option->getEnumOptions(), - )); - $control = id(new AphrontFormSelectControl()) - ->setOptions($options); - break; - case 'class': - $symbols = id(new PhutilSymbolLoader()) - ->setType('class') - ->setAncestorClass($option->getBaseClass()) - ->setConcreteOnly(true) - ->selectSymbolsWithoutLoading(); - $names = ipull($symbols, 'name', 'name'); - asort($names); - $names = array( - '' => pht('(Use Default)'), - ) + $names; - - $control = id(new AphrontFormSelectControl()) - ->setOptions($names); - break; - case 'list': - case 'list': - $control = id(new AphrontFormTextAreaControl()) - ->setCaption(pht('Separate values with newlines.')); - break; - case 'set': - $control = id(new AphrontFormTextAreaControl()) - ->setCaption(pht('Separate values with newlines or commas.')); - break; - default: - $control = id(new AphrontFormTextAreaControl()) - ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL) - ->setCustomClass('PhabricatorMonospaced') - ->setCaption(pht('Enter value in JSON.')); - break; - } - - $control - ->setLabel(pht('Database Value')) - ->setError($e_value) - ->setValue($display_value) - ->setName('value'); - - $controls = array($control); + throw new Exception( + pht( + 'Unknown configuration option type "%s".', + $option->getType())); } return $controls; diff --git a/src/applications/config/management/PhabricatorConfigManagementSetWorkflow.php b/src/applications/config/management/PhabricatorConfigManagementSetWorkflow.php index 4511865785..9f50d2eeed 100644 --- a/src/applications/config/management/PhabricatorConfigManagementSetWorkflow.php +++ b/src/applications/config/management/PhabricatorConfigManagementSetWorkflow.php @@ -59,84 +59,37 @@ final class PhabricatorConfigManagementSetWorkflow $option = $options[$key]; - $type = $option->getType(); - switch ($type) { - case 'string': - case 'class': - case 'enum': - $value = (string)$value; - break; - case 'int': - if (!ctype_digit($value)) { - throw new PhutilArgumentUsageException( - pht( - "Config key '%s' is of type '%s'. Specify an integer.", - $key, - $type)); - } - $value = (int)$value; - break; - case 'bool': - if ($value == 'true') { - $value = true; - } else if ($value == 'false') { - $value = false; - } else { - throw new PhutilArgumentUsageException( - pht( - "Config key '%s' is of type '%s'. Specify '%s' or '%s'.", - $key, - $type, - 'true', - 'false')); - } - break; - default: - $value = json_decode($value, true); - if (!is_array($value)) { - switch ($type) { - case 'set': - $command = csprintf( - './bin/config set %R %s', - $key, - '{"value1": true, "value2": true}'); - - $message = sprintf( - "%s\n\n %s\n", - pht( - 'Config key "%s" is of type "%s". Specify it in JSON. '. - 'For example:', - $key, - $type), - $command); - break; - default: - if (preg_match('/^listnewOptionType(); + if ($type) { + try { + $value = $type->newValueFromCommandLineValue( + $option, + $value); + $type->validateStoredValue($option, $value); + } catch (PhabricatorConfigValidationException $ex) { + throw new PhutilArgumentUsageException($ex->getMessage()); + } + } else { + // NOTE: For now, this handles both "wild" values and custom types. + $type = $option->getType(); + switch ($type) { + default: + $value = json_decode($value, true); + if (!is_array($value)) { + switch ($type) { + default: $message = pht( 'Config key "%s" is of type "%s". Specify it in JSON.', $key, $type); - } - break; + break; + } + throw new PhutilArgumentUsageException($message); } - throw new PhutilArgumentUsageException($message); - } - break; + break; + } } + $use_database = $args->getArg('database'); if ($option->getLocked() && $use_database) { throw new PhutilArgumentUsageException( diff --git a/src/applications/config/option/PhabricatorApplicationConfigOptions.php b/src/applications/config/option/PhabricatorApplicationConfigOptions.php index 34d74e88ab..49cd9eb17e 100644 --- a/src/applications/config/option/PhabricatorApplicationConfigOptions.php +++ b/src/applications/config/option/PhabricatorApplicationConfigOptions.php @@ -20,133 +20,34 @@ abstract class PhabricatorApplicationConfigOptions extends Phobject { return; } + $type = $option->newOptionType(); + if ($type) { + try { + $type->validateStoredValue($option, $value); + $this->didValidateOption($option, $value); + } catch (PhabricatorConfigValidationException $ex) { + throw $ex; + } catch (Exception $ex) { + // If custom validators threw exceptions other than validation + // exceptions, convert them to validation exceptions so we repair the + // configuration and raise an error. + throw new PhabricatorConfigValidationException($ex->getMessage()); + } + + return; + } + if ($option->isCustomType()) { try { return $option->getCustomObject()->validateOption($option, $value); } catch (Exception $ex) { - // If custom validators threw exceptions, convert them to configuation - // validation exceptions so we repair the configuration and raise - // an error. throw new PhabricatorConfigValidationException($ex->getMessage()); } - } - - switch ($option->getType()) { - case 'bool': - if ($value !== true && - $value !== false) { - throw new PhabricatorConfigValidationException( - pht( - "Option '%s' is of type bool, but value is not true or false.", - $option->getKey())); - } - break; - case 'int': - if (!is_int($value)) { - throw new PhabricatorConfigValidationException( - pht( - "Option '%s' is of type int, but value is not an integer.", - $option->getKey())); - } - break; - case 'string': - if (!is_string($value)) { - throw new PhabricatorConfigValidationException( - pht( - "Option '%s' is of type string, but value is not a string.", - $option->getKey())); - } - break; - case 'class': - $symbols = id(new PhutilSymbolLoader()) - ->setType('class') - ->setAncestorClass($option->getBaseClass()) - ->setConcreteOnly(true) - ->selectSymbolsWithoutLoading(); - $names = ipull($symbols, 'name', 'name'); - if (empty($names[$value])) { - throw new PhabricatorConfigValidationException( - pht( - "Option '%s' value must name a class extending '%s'.", - $option->getKey(), - $option->getBaseClass())); - } - break; - case 'set': - $valid = true; - if (!is_array($value)) { - throw new PhabricatorConfigValidationException( - pht( - "Option '%s' must be a set, but value is not an array.", - $option->getKey())); - } - foreach ($value as $v) { - if ($v !== true) { - throw new PhabricatorConfigValidationException( - pht( - "Option '%s' must be a set, but array contains values other ". - "than 'true'.", - $option->getKey())); - } - } - break; - case 'list': - $valid = true; - if (!is_array($value)) { - throw new PhabricatorConfigValidationException( - pht( - "Option '%s' must be a list of regular expressions, but value ". - "is not an array.", - $option->getKey())); - } - if ($value && array_keys($value) != range(0, count($value) - 1)) { - throw new PhabricatorConfigValidationException( - pht( - "Option '%s' must be a list of regular expressions, but the ". - "value is a map with unnatural keys.", - $option->getKey())); - } - foreach ($value as $v) { - $ok = @preg_match($v, ''); - if ($ok === false) { - throw new PhabricatorConfigValidationException( - pht( - "Option '%s' must be a list of regular expressions, but the ". - "value '%s' is not a valid regular expression.", - $option->getKey(), - $v)); - } - } - break; - case 'list': - $valid = true; - if (!is_array($value)) { - throw new PhabricatorConfigValidationException( - pht( - "Option '%s' must be a list of strings, but value is not ". - "an array.", - $option->getKey())); - } - if ($value && array_keys($value) != range(0, count($value) - 1)) { - throw new PhabricatorConfigValidationException( - pht( - "Option '%s' must be a list of strings, but the value is a ". - "map with unnatural keys.", - $option->getKey())); - } - foreach ($value as $v) { - if (!is_string($v)) { - throw new PhabricatorConfigValidationException( - pht( - "Option '%s' must be a list of strings, but it contains one ". - "or more non-strings.", - $option->getKey())); - } - } - break; - case 'wild': - default: - break; + } else { + throw new Exception( + pht( + 'Unknown configuration option type "%s".', + $option->getType())); } $this->didValidateOption($option, $value); diff --git a/src/applications/config/option/PhabricatorClusterConfigOptions.php b/src/applications/config/option/PhabricatorClusterConfigOptions.php index c3636c31e0..ca4153a8b2 100644 --- a/src/applications/config/option/PhabricatorClusterConfigOptions.php +++ b/src/applications/config/option/PhabricatorClusterConfigOptions.php @@ -20,7 +20,7 @@ final class PhabricatorClusterConfigOptions } public function getOptions() { - $databases_type = 'custom:PhabricatorClusterDatabasesConfigOptionType'; + $databases_type = 'cluster.databases'; $databases_help = $this->deformat(pht(<<deformat(pht(<<type; } + public function newOptionType() { + $type_key = $this->getType(); + $type_map = PhabricatorConfigType::getAllTypes(); + return idx($type_map, $type_key); + } + public function isCustomType() { return !strncmp($this->getType(), 'custom:', 7); } diff --git a/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php b/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php index 556b1b169a..cc9732dc70 100644 --- a/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php +++ b/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php @@ -297,9 +297,9 @@ EODOC $this->newOption('metamta.user-address-format', 'enum', 'full') ->setEnumOptions( array( - 'short' => 'short', - 'real' => 'real', - 'full' => 'full', + 'short' => pht('Short'), + 'real' => pht('Real'), + 'full' => pht('Full'), )) ->setSummary(pht('Control how Phabricator renders user names in mail.')) ->setDescription($address_description) diff --git a/src/applications/config/option/PhabricatorNotificationConfigOptions.php b/src/applications/config/option/PhabricatorNotificationConfigOptions.php index 34fae7c184..9f06bc3c2d 100644 --- a/src/applications/config/option/PhabricatorNotificationConfigOptions.php +++ b/src/applications/config/option/PhabricatorNotificationConfigOptions.php @@ -20,7 +20,7 @@ final class PhabricatorNotificationConfigOptions } public function getOptions() { - $servers_type = 'custom:PhabricatorNotificationServersConfigOptionType'; + $servers_type = 'cluster.notifications'; $servers_help = $this->deformat(pht(<< 'blindigo', - 'red' => 'red', - 'blue' => 'blue', - 'green' => 'green', - 'indigo' => 'indigo', - 'dark' => 'dark', + 'blindigo' => pht('Blindigo'), + 'red' => pht('Red'), + 'blue' => pht('Blue'), + 'green' => pht('Green'), + 'indigo' => pht('Indigo'), + 'dark' => pht('Dark'), ); $example = <<newException( + pht( + 'Value for option "%s" of type "%s" must be either '. + '"true" or "false".', + $option->getKey(), + $this->getTypeKey())); + } + + return ($value === 'true'); + } + + public function newDisplayValue( + PhabricatorConfigOption $option, + $value) { + + if ($value) { + return 'true'; + } else { + return 'false'; + } + } + + public function validateStoredValue( + PhabricatorConfigOption $option, + $value) { + + if (!is_bool($value)) { + throw $this->newException( + pht( + 'Option "%s" is of type "%s", but the configured value is not '. + 'a boolean.', + $option->getKey(), + $this->getTypeKey())); + } + } + + protected function newControl(PhabricatorConfigOption $option) { + $bool_map = $option->getBoolOptions(); + + $map = array( + '' => pht('(Use Default)'), + ) + array( + 'true' => idx($bool_map, 0), + 'false' => idx($bool_map, 1), + ); + + return id(new AphrontFormSelectControl()) + ->setOptions($map); + } +} diff --git a/src/applications/config/type/PhabricatorClassConfigType.php b/src/applications/config/type/PhabricatorClassConfigType.php new file mode 100644 index 0000000000..0ec5e0eb5e --- /dev/null +++ b/src/applications/config/type/PhabricatorClassConfigType.php @@ -0,0 +1,76 @@ +newException( + pht( + 'Option "%s" is of type "%s", but the configured value is not '. + 'a string.', + $option->getKey(), + $this->getTypeKey())); + } + + $base = $option->getBaseClass(); + $map = $this->getClassOptions($option); + + try { + $ok = class_exists($value); + } catch (Exception $ex) { + $ok = false; + } + + if (!$ok) { + throw $this->newException( + pht( + 'Option "%s" is of type "%s", but the configured value is not the '. + 'name of a known class. Valid selections are: %s.', + $option->getKey(), + $this->getTypeKey(), + implode(', ', array_keys($map)))); + } + + if (!isset($map[$value])) { + throw $this->newException( + pht( + 'Option "%s" is of type "%s", but the current value ("%s") is not '. + 'a known, concrete subclass of base class "%s". Valid selections '. + 'are: %s.', + $option->getKey(), + $this->getTypeKey(), + $value, + $base, + implode(', ', array_keys($map)))); + } + } + + protected function newControl(PhabricatorConfigOption $option) { + $map = array( + '' => pht('(Use Default)'), + ) + $this->getClassOptions($option); + + return id(new AphrontFormSelectControl()) + ->setOptions($map); + } + + private function getClassOptions(PhabricatorConfigOption $option) { + $symbols = id(new PhutilSymbolLoader()) + ->setType('class') + ->setAncestorClass($option->getBaseClass()) + ->setConcreteOnly(true) + ->selectSymbolsWithoutLoading(); + + $map = ipull($symbols, 'name', 'name'); + asort($map); + + return $map; + } + +} diff --git a/src/applications/config/type/PhabricatorConfigType.php b/src/applications/config/type/PhabricatorConfigType.php new file mode 100644 index 0000000000..2968d00d18 --- /dev/null +++ b/src/applications/config/type/PhabricatorConfigType.php @@ -0,0 +1,111 @@ +getPhobjectClassConstant('TYPEKEY'); + } + + final public static function getAllTypes() { + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->setUniqueMethod('getTypeKey') + ->execute(); + } + + public function isValuePresentInRequest( + PhabricatorConfigOption $option, + AphrontRequest $request) { + $http_type = $this->newHTTPParameterType(); + return $http_type->getExists($request, 'value'); + } + + public function readValueFromRequest( + PhabricatorConfigOption $option, + AphrontRequest $request) { + $http_type = $this->newHTTPParameterType(); + return $http_type->getValue($request, 'value'); + } + + abstract protected function newHTTPParameterType(); + + public function newTransaction( + PhabricatorConfigOption $option, + $value) { + + $xaction_value = $this->newTransactionValue($option, $value); + + return id(new PhabricatorConfigTransaction()) + ->setTransactionType(PhabricatorConfigTransaction::TYPE_EDIT) + ->setNewValue( + array( + 'deleted' => false, + 'value' => $xaction_value, + )); + } + + protected function newTransactionValue( + PhabricatorConfigOption $option, + $value) { + return $value; + } + + public function newDisplayValue( + PhabricatorConfigOption $option, + $value) { + return $value; + } + + public function newControls( + PhabricatorConfigOption $option, + $value, + $error) { + + $control = $this->newControl($option) + ->setError($error) + ->setLabel(pht('Database Value')) + ->setName('value'); + + $value = $this->newControlValue($option, $value); + $control->setValue($value); + + return array( + $control, + ); + } + + abstract protected function newControl(PhabricatorConfigOption $option); + + protected function newControlValue( + PhabricatorConfigOption $option, + $value) { + return $value; + } + + protected function newException($message) { + return new PhabricatorConfigValidationException($message); + } + + public function newValueFromRequestValue( + PhabricatorConfigOption $option, + $value) { + return $this->newCanonicalValue($option, $value); + } + + public function newValueFromCommandLineValue( + PhabricatorConfigOption $option, + $value) { + return $this->newCanonicalValue($option, $value); + } + + protected function newCanonicalValue( + PhabricatorConfigOption $option, + $value) { + return $value; + } + + abstract public function validateStoredValue( + PhabricatorConfigOption $option, + $value); + +} diff --git a/src/applications/config/type/PhabricatorEnumConfigType.php b/src/applications/config/type/PhabricatorEnumConfigType.php new file mode 100644 index 0000000000..0d1d958b2e --- /dev/null +++ b/src/applications/config/type/PhabricatorEnumConfigType.php @@ -0,0 +1,43 @@ +newException( + pht( + 'Option "%s" is of type "%s", but the configured value is not '. + 'a string.', + $option->getKey(), + $this->getTypeKey())); + } + + $map = $option->getEnumOptions(); + if (!isset($map[$value])) { + throw $this->newException( + pht( + 'Option "%s" is of type "%s", but the current value ("%s") is not '. + 'among the set of valid values: %s.', + $option->getKey(), + $this->getTypeKey(), + $value, + implode(', ', array_keys($map)))); + } + } + + protected function newControl(PhabricatorConfigOption $option) { + $map = array( + '' => pht('(Use Default)'), + ) + $option->getEnumOptions(); + + return id(new AphrontFormSelectControl()) + ->setOptions($map); + } + +} diff --git a/src/applications/config/type/PhabricatorIntConfigType.php b/src/applications/config/type/PhabricatorIntConfigType.php new file mode 100644 index 0000000000..807104f1a5 --- /dev/null +++ b/src/applications/config/type/PhabricatorIntConfigType.php @@ -0,0 +1,36 @@ +newException( + pht( + 'Value for option "%s" must be an integer.', + $option->getKey())); + } + + return (int)$value; + } + + public function validateStoredValue( + PhabricatorConfigOption $option, + $value) { + + if (!is_int($value)) { + throw $this->newException( + pht( + 'Option "%s" is of type "%s", but the configured value is not '. + 'an integer.', + $option->getKey(), + $this->getTypeKey())); + } + } + +} diff --git a/src/applications/config/type/PhabricatorJSONConfigType.php b/src/applications/config/type/PhabricatorJSONConfigType.php new file mode 100644 index 0000000000..b1226c6e53 --- /dev/null +++ b/src/applications/config/type/PhabricatorJSONConfigType.php @@ -0,0 +1,38 @@ +newException( + pht( + 'Value for option "%s" (of type "%s") must be specified in JSON, '. + 'but input could not be decoded: %s', + $option->getKey(), + $this->getTypeKey(), + $ex->getMessage())); + } + + return $value; + } + + protected function newControl(PhabricatorConfigOption $option) { + return id(new AphrontFormTextAreaControl()) + ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL) + ->setCustomClass('PhabricatorMonospaced') + ->setCaption(pht('Enter value in JSON.')); + } + + public function newDisplayValue( + PhabricatorConfigOption $option, + $value) { + return PhabricatorConfigJSON::prettyPrintJSON($value); + } + +} diff --git a/src/applications/config/type/PhabricatorRegexListConfigType.php b/src/applications/config/type/PhabricatorRegexListConfigType.php new file mode 100644 index 0000000000..b37b440c7e --- /dev/null +++ b/src/applications/config/type/PhabricatorRegexListConfigType.php @@ -0,0 +1,24 @@ +'; + + protected function validateStoredItem( + PhabricatorConfigOption $option, + $value) { + + $ok = @preg_match($value, ''); + if ($ok === false) { + throw $this->newException( + pht( + 'Option "%s" is of type "%s" and must be set to a list of valid '. + 'regular expressions, but "%s" is not a valid regular expression.', + $option->getKey(), + $this->getTypeKey(), + $value)); + } + } + +} diff --git a/src/applications/config/type/PhabricatorSetConfigType.php b/src/applications/config/type/PhabricatorSetConfigType.php new file mode 100644 index 0000000000..805ae50468 --- /dev/null +++ b/src/applications/config/type/PhabricatorSetConfigType.php @@ -0,0 +1,92 @@ +setCaption(pht('Separate values with newlines or commas.')); + } + + protected function newCanonicalValue( + PhabricatorConfigOption $option, + $value) { + + $value = preg_split('/[\n,]+/', $value); + foreach ($value as $k => $v) { + if (!strlen($v)) { + unset($value[$k]); + } + $value[$k] = trim($v); + } + + return array_fill_keys($value, true); + } + + public function newValueFromCommandLineValue( + PhabricatorConfigOption $option, + $value) { + + try { + $value = phutil_json_decode($value); + } catch (Exception $ex) { + throw $this->newException( + pht( + 'Option "%s" is of type "%s", but the value you provided is not a '. + 'valid JSON list: when providing a set from the command line, '. + 'specify it as a list of values in JSON. You may need to quote the '. + 'value for your shell (for example: \'["a", "b", ...]\').', + $option->getKey(), + $this->getTypeKey())); + } + + if ($value) { + if (array_keys($value) !== range(0, count($value) - 1)) { + throw $this->newException( + pht( + 'Option "%s" is of type "%s", and should be specified on the '. + 'command line as a JSON list of values. You may need to quote '. + 'the value for your shell (for example: \'["a", "b", ...]\').', + $option->getKey(), + $this->getTypeKey())); + } + } + + return array_fill_keys($value, true); + } + + public function newDisplayValue( + PhabricatorConfigOption $option, + $value) { + return implode("\n", array_keys($value)); + } + + public function validateStoredValue( + PhabricatorConfigOption $option, + $value) { + + if (!is_array($value)) { + throw $this->newException( + pht( + 'Option "%s" is of type "%s", but the configured value is not '. + 'a list.', + $option->getKey(), + $this->getTypeKey())); + } + + foreach ($value as $k => $v) { + if ($v !== true) { + throw $this->newException( + pht( + 'Option "%s" is of type "%s", but the value at index "%s" of the '. + 'list is not "true".', + $option->getKey(), + $this->getTypeKey(), + $k)); + } + } + } + +} diff --git a/src/applications/config/type/PhabricatorStringConfigType.php b/src/applications/config/type/PhabricatorStringConfigType.php new file mode 100644 index 0000000000..da05b75780 --- /dev/null +++ b/src/applications/config/type/PhabricatorStringConfigType.php @@ -0,0 +1,22 @@ +newException( + pht( + 'Option "%s" is of type "%s", but the configured value is not '. + 'a string.', + $option->getKey(), + $this->getTypeKey())); + } + } + +} diff --git a/src/applications/config/type/PhabricatorStringListConfigType.php b/src/applications/config/type/PhabricatorStringListConfigType.php new file mode 100644 index 0000000000..70f4c04316 --- /dev/null +++ b/src/applications/config/type/PhabricatorStringListConfigType.php @@ -0,0 +1,8 @@ +'; + +} diff --git a/src/applications/config/type/PhabricatorTextConfigType.php b/src/applications/config/type/PhabricatorTextConfigType.php new file mode 100644 index 0000000000..8e67d65b7a --- /dev/null +++ b/src/applications/config/type/PhabricatorTextConfigType.php @@ -0,0 +1,27 @@ +setCaption(pht('Separate values with newlines.')); + } + + protected function newCanonicalValue( + PhabricatorConfigOption $option, + $value) { + + $value = phutil_split_lines($value, $retain_endings = false); + foreach ($value as $k => $v) { + if (!strlen($v)) { + unset($value[$k]); + } + } + + return array_values($value); + } + + public function newValueFromCommandLineValue( + PhabricatorConfigOption $option, + $value) { + + try { + $value = phutil_json_decode($value); + } catch (Exception $ex) { + throw $this->newException( + pht( + 'Option "%s" is of type "%s", but the value you provided is not a '. + 'valid JSON list. When setting a list option from the command '. + 'line, specify the value in JSON. You may need to quote the '. + 'value for your shell (for example: \'["a", "b", ...]\').', + $option->getKey(), + $this->getTypeKey())); + } + + return $value; + } + + public function newDisplayValue( + PhabricatorConfigOption $option, + $value) { + return implode("\n", $value); + } + + public function validateStoredValue( + PhabricatorConfigOption $option, + $value) { + + if (!is_array($value)) { + throw $this->newException( + pht( + 'Option "%s" is of type "%s", but the configured value is not '. + 'a list.', + $option->getKey(), + $this->getTypeKey())); + } + + $expect_key = 0; + foreach ($value as $k => $v) { + if (!is_string($v)) { + throw $this->newException( + pht( + 'Option "%s" is of type "%s", but the item at index "%s" of the '. + 'list is not a string.', + $option->getKey(), + $this->getTypeKey(), + $k)); + } + + // Make sure this is a list with keys "0, 1, 2, ...", not a map with + // arbitrary keys. + if ($k != $expect_key) { + throw $this->newException( + pht( + 'Option "%s" is of type "%s", but the value is not a list: it '. + 'is a map with unnatural or sparse keys.', + $option->getKey(), + $this->getTypeKey())); + } + $expect_key++; + + $this->validateStoredItem($option, $v); + } + } + + protected function validateStoredItem( + PhabricatorConfigOption $option, + $value) { + return; + } + +} diff --git a/src/applications/config/type/PhabricatorWildConfigType.php b/src/applications/config/type/PhabricatorWildConfigType.php new file mode 100644 index 0000000000..3f278ca4e9 --- /dev/null +++ b/src/applications/config/type/PhabricatorWildConfigType.php @@ -0,0 +1,39 @@ +newException( + pht( + 'Value for option "%s" (of type "%s") must be specified in JSON, '. + 'but input could not be decoded. (Did you forget to quote a string?)', + $option->getKey(), + $this->getTypeKey())); + } + + return $value; + } + + public function validateStoredValue( + PhabricatorConfigOption $option, + $value) { + return; + } + +} diff --git a/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php b/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php index a3db26bef6..7ff886664f 100644 --- a/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php +++ b/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php @@ -260,7 +260,10 @@ EOHELP ->setDescription( pht('Format for inlined or attached patches.')) ->setEnumOptions( - array('unified' => 'unified', 'git' => 'git')), + array( + 'unified' => pht('Unified'), + 'git' => pht('Git'), + )), ); } diff --git a/src/applications/differential/field/DifferentialTestPlanCommitMessageField.php b/src/applications/differential/field/DifferentialTestPlanCommitMessageField.php index 178e51d985..a477a9036e 100644 --- a/src/applications/differential/field/DifferentialTestPlanCommitMessageField.php +++ b/src/applications/differential/field/DifferentialTestPlanCommitMessageField.php @@ -50,11 +50,4 @@ final class DifferentialTestPlanCommitMessageField ); } - public function validateTransactions($object, array $xactions) { - return $this->validateCommitMessageCorpusTransactions( - $object, - $xactions, - pht('Test Plan')); - } - } diff --git a/src/applications/differential/xaction/DifferentialRevisionTestPlanTransaction.php b/src/applications/differential/xaction/DifferentialRevisionTestPlanTransaction.php index bf2beab3d8..c7c77fbcff 100644 --- a/src/applications/differential/xaction/DifferentialRevisionTestPlanTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionTestPlanTransaction.php @@ -55,7 +55,10 @@ final class DifferentialRevisionTestPlanTransaction } public function validateTransactions($object, array $xactions) { - $errors = array(); + $errors = $this->validateCommitMessageCorpusTransactions( + $object, + $xactions, + pht('Test Plan')); $is_required = PhabricatorEnv::getEnvConfig( 'differential.require-test-plan-field'); diff --git a/src/applications/diffusion/controller/DiffusionRepositoryURIViewController.php b/src/applications/diffusion/controller/DiffusionRepositoryURIViewController.php index 61335d20d0..308df8f0d2 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryURIViewController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryURIViewController.php @@ -23,6 +23,22 @@ final class DiffusionRepositoryURIViewController return new Aphront404Response(); } + // For display, reload the URI by loading it through the repository. This + // may adjust builtin URIs for repository configuration, so we may end up + // with a different view of builtin URIs than we'd see if we loaded them + // directly from the database. See T12884. + $repository_with_uris = id(new PhabricatorRepositoryQuery()) + ->setViewer($viewer) + ->needURIs(true) + ->execute(); + + $repository_uris = $repository->getURIs(); + $repository_uris = mpull($repository_uris, null, 'getID'); + $uri = idx($repository_uris, $uri->getID()); + if (!$uri) { + return new Aphront404Response(); + } + $title = array( pht('URI'), $repository->getDisplayName(), diff --git a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php index 7932451d2e..5b7171a200 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php @@ -141,7 +141,7 @@ final class HarbormasterBuildViewController if ($ended) { $when[] = pht( 'Completed at %s', - phabricator_datetime($started, $viewer)); + phabricator_datetime($ended, $viewer)); $duration = ($ended - $started); if ($duration) { diff --git a/src/applications/maniphest/config/ManiphestPointsConfigOptionType.php b/src/applications/maniphest/config/ManiphestPointsConfigOptionType.php deleted file mode 100644 index f79050b4a1..0000000000 --- a/src/applications/maniphest/config/ManiphestPointsConfigOptionType.php +++ /dev/null @@ -1,10 +0,0 @@ - array( 'name' => pht('Unbreak Now!'), + 'keywords' => array('unbreak'), 'short' => pht('Unbreak!'), 'color' => 'pink', - 'keywords' => array('unbreak'), ), 90 => array( 'name' => pht('Needs Triage'), + 'keywords' => array('triage'), 'short' => pht('Triage'), 'color' => 'violet', - 'keywords' => array('triage'), ), 80 => array( 'name' => pht('High'), + 'keywords' => array('high'), 'short' => pht('High'), 'color' => 'red', - 'keywords' => array('high'), ), 50 => array( 'name' => pht('Normal'), + 'keywords' => array('normal'), 'short' => pht('Normal'), 'color' => 'orange', - 'keywords' => array('normal'), ), 25 => array( 'name' => pht('Low'), + 'keywords' => array('low'), 'short' => pht('Low'), 'color' => 'yellow', - 'keywords' => array('low'), ), 0 => array( 'name' => pht('Wishlist'), + 'keywords' => array('wish', 'wishlist'), 'short' => pht('Wish'), 'color' => 'sky', - 'keywords' => array('wish', 'wishlist'), ), ); - $status_type = 'custom:ManiphestStatusConfigOptionType'; + $status_type = 'maniphest.statuses'; $status_defaults = array( 'open' => array( 'name' => pht('Open'), @@ -265,7 +265,7 @@ EOTEXT ); $fields_json = id(new PhutilJSON())->encodeFormatted($fields_example); - $points_type = 'custom:ManiphestPointsConfigOptionType'; + $points_type = 'maniphest.points'; $points_example_1 = array( 'enabled' => true, @@ -299,7 +299,7 @@ See the example below for a starting point. EOTEXT )); - $subtype_type = 'custom:ManiphestSubtypesConfigOptionsType'; + $subtype_type = 'maniphest.subtypes'; $subtype_default_key = PhabricatorEditEngineSubtype::SUBTYPE_DEFAULT; $subtype_example = array( array( @@ -349,6 +349,32 @@ EOTEXT , $subtype_default_key)); + $priorities_description = $this->deformat(pht(<<.// List of unique keywords which identify + this priority, like "high" or "low". Each priority must have at least one + keyword and two priorities may not share the same keyword. + - `short` //Optional string.// Alternate shorter name, used in UIs where + there is less space available. + - `color` //Optional string.// Color for this priority, like "red" or + "blue". + - `disabled` //Optional bool.// Set to true to prevent users from choosing + this priority when creating or editing tasks. Existing tasks will not be + affected, and can be batch edited to a different priority or left to + eventually die out. + +You can choose the default priority for newly created tasks with +"maniphest.default-priority". +EOTEXT + )); + return array( $this->newOption('maniphest.custom-field-definitions', 'wild', array()) @@ -367,30 +393,7 @@ EOTEXT $priority_type, $priority_defaults) ->setSummary(pht('Configure Maniphest priority names.')) - ->setDescription( - pht( - 'Allows you to edit or override the default priorities available '. - 'in Maniphest, like "High", "Normal" and "Low". The configuration '. - 'should contain a map of priority constants to priority '. - 'specifications (see defaults below for examples).'. - "\n\n". - 'The keys you can define for a priority are:'. - "\n\n". - ' - `name` Name of the priority.'."\n". - ' - `short` Alternate shorter name, used in UIs where there is '. - ' not much space available.'."\n". - ' - `color` A color for this priority, like "red" or "blue".'. - ' - `keywords` An optional list of keywords which can '. - ' be used to select this priority when using `!priority` '. - ' commands in email.'."\n". - ' - `disabled` Optional boolean to prevent users from choosing '. - ' this priority when creating or editing tasks. Existing '. - ' tasks will be unaffected, and can be batch edited to a '. - ' different priority or left to eventually die out.'. - "\n\n". - 'You can choose which priority is the default for newly created '. - 'tasks with `%s`.', - 'maniphest.default-priority')), + ->setDescription($priorities_description), $this->newOption('maniphest.statuses', $status_type, $status_defaults) ->setSummary(pht('Configure Maniphest task statuses.')) ->setDescription($status_description) diff --git a/src/applications/maniphest/constants/ManiphestTaskPriority.php b/src/applications/maniphest/constants/ManiphestTaskPriority.php index 86c7adbcb7..d559299af9 100644 --- a/src/applications/maniphest/constants/ManiphestTaskPriority.php +++ b/src/applications/maniphest/constants/ManiphestTaskPriority.php @@ -203,6 +203,7 @@ final class ManiphestTaskPriority extends ManiphestConstants { $config)); } + $all_keywords = array(); foreach ($config as $key => $value) { if (!ctype_digit((string)$key)) { throw new Exception( @@ -223,9 +224,9 @@ final class ManiphestTaskPriority extends ManiphestConstants { $value, array( 'name' => 'string', + 'keywords' => 'list', 'short' => 'optional string', 'color' => 'optional string', - 'keywords' => 'list', 'disabled' => 'optional bool', )); @@ -242,6 +243,18 @@ final class ManiphestTaskPriority extends ManiphestConstants { 'low', 'critical')); } + + if (isset($all_keywords[$keyword])) { + throw new Exception( + pht( + 'Two different task priorities ("%s" and "%s") have the same '. + 'keyword ("%s"). Keywords must uniquely identify priorities.', + $value['name'], + $all_keywords[$keyword], + $keyword)); + } + + $all_keywords[$keyword] = $value['name']; } } } diff --git a/src/applications/notification/config/PhabricatorNotificationServersConfigOptionType.php b/src/applications/notification/config/PhabricatorNotificationServersConfigType.php similarity index 85% rename from src/applications/notification/config/PhabricatorNotificationServersConfigOptionType.php rename to src/applications/notification/config/PhabricatorNotificationServersConfigType.php index 07cdf7eece..5f0c1f7e2f 100644 --- a/src/applications/notification/config/PhabricatorNotificationServersConfigOptionType.php +++ b/src/applications/notification/config/PhabricatorNotificationServersConfigType.php @@ -1,19 +1,17 @@ $spec) { if (!is_array($spec)) { - throw new Exception( + throw $this->newException( pht( 'Notification server configuration is not valid: each entry in '. 'the list must be a dictionary describing a service, but '. @@ -38,7 +36,7 @@ final class PhabricatorNotificationServersConfigOptionType 'disabled' => 'optional bool', )); } catch (Exception $ex) { - throw new Exception( + throw $this->newException( pht( 'Notification server configuration has an invalid service '. 'specification (at index "%s"): %s.', @@ -64,7 +62,7 @@ final class PhabricatorNotificationServersConfigOptionType } break; default: - throw new Exception( + throw $this->newException( pht( 'Notification server configuration describes an invalid '. 'host ("%s", at index "%s") with an unrecognized type ("%s"). '. @@ -81,7 +79,7 @@ final class PhabricatorNotificationServersConfigOptionType case 'https': break; default: - throw new Exception( + throw $this->newException( pht( 'Notification server configuration describes an invalid '. 'host ("%s", at index "%s") with an invalid protocol ("%s"). '. @@ -95,7 +93,7 @@ final class PhabricatorNotificationServersConfigOptionType $path = idx($spec, 'path'); if ($type == 'admin' && strlen($path)) { - throw new Exception( + throw $this->newException( pht( 'Notification server configuration describes an invalid host '. '("%s", at index "%s"). This is an "admin" service but it has a '. @@ -108,7 +106,7 @@ final class PhabricatorNotificationServersConfigOptionType // mistakes. $key = "{$host}:{$port}"; if (isset($map[$key])) { - throw new Exception( + throw $this->newException( pht( 'Notification server configuration is invalid: it describes the '. 'same host and port ("%s") multiple times. Each host and port '. @@ -120,7 +118,7 @@ final class PhabricatorNotificationServersConfigOptionType if ($value) { if (!$has_admin) { - throw new Exception( + throw $this->newException( pht( 'Notification server configuration is invalid: it does not '. 'specify any enabled servers with type "admin". Notifications '. @@ -128,7 +126,7 @@ final class PhabricatorNotificationServersConfigOptionType } if (!$has_client) { - throw new Exception( + throw $this->newException( pht( 'Notification server configuration is invalid: it does not '. 'specify any enabled servers with type "client". Notifications '. diff --git a/src/applications/people/config/PhabricatorUserConfigOptions.php b/src/applications/people/config/PhabricatorUserConfigOptions.php index 79be912699..3ef736c8ca 100644 --- a/src/applications/people/config/PhabricatorUserConfigOptions.php +++ b/src/applications/people/config/PhabricatorUserConfigOptions.php @@ -43,7 +43,7 @@ final class PhabricatorUserConfigOptions $this->newOption('user.fields', $custom_field_type, $default) ->setCustomData(id(new PhabricatorUser())->getCustomFieldBaseClass()) ->setDescription(pht('Select and reorder user profile fields.')), - $this->newOption('user.custom-field-definitions', 'map', array()) + $this->newOption('user.custom-field-definitions', 'wild', array()) ->setDescription(pht('Add new simple fields to user profiles.')), $this->newOption('user.require-real-name', 'bool', true) ->setDescription(pht('Always require real name for user profiles.')) diff --git a/src/applications/project/config/PhabricatorProjectColorsConfigOptionType.php b/src/applications/project/config/PhabricatorProjectColorsConfigOptionType.php deleted file mode 100644 index 4cd8c09bbc..0000000000 --- a/src/applications/project/config/PhabricatorProjectColorsConfigOptionType.php +++ /dev/null @@ -1,10 +0,0 @@ -deformat(pht(<<deformat(pht(<<getPHID()] = array( + 'uri' => $file->getBestURI(), + 'tip' => pht('Builtin Image'), + ); + } + $images[PhabricatorPHIDConstants::PHID_VOID] = array( 'uri' => $default_image->getBestURI(), - 'tip' => pht('No Picture'), + 'tip' => pht('Default Picture'), ); require_celerity_resource('people-profile-css'); @@ -200,7 +230,7 @@ final class PhabricatorProjectEditPictureController $compose_button = javelin_tag( 'button', array( - 'class' => 'grey', + 'class' => 'button-grey', 'id' => $launch_id, 'sigil' => 'icon-composer', ), @@ -227,7 +257,7 @@ final class PhabricatorProjectEditPictureController $form->appendChild( id(new AphrontFormMarkupControl()) - ->setLabel(pht('Quick Create')) + ->setLabel(pht('Custom')) ->setValue($compose_form)); $upload_form = id(new AphrontFormView()) diff --git a/src/applications/search/management/PhabricatorSearchManagementWorkflow.php b/src/applications/search/management/PhabricatorSearchManagementWorkflow.php index 86d8c104cb..43dad986ea 100644 --- a/src/applications/search/management/PhabricatorSearchManagementWorkflow.php +++ b/src/applications/search/management/PhabricatorSearchManagementWorkflow.php @@ -13,7 +13,7 @@ abstract class PhabricatorSearchManagementWorkflow $config_value = PhabricatorEnv::getEnvConfig($config_key); try { - PhabricatorClusterSearchConfigOptionType::validateValue($config_value); + PhabricatorClusterSearchConfigType::validateValue($config_value); } catch (Exception $ex) { throw new PhutilArgumentUsageException( pht( diff --git a/src/infrastructure/cluster/config/PhabricatorClusterDatabasesConfigOptionType.php b/src/infrastructure/cluster/config/PhabricatorClusterDatabasesConfigType.php similarity index 82% rename from src/infrastructure/cluster/config/PhabricatorClusterDatabasesConfigOptionType.php rename to src/infrastructure/cluster/config/PhabricatorClusterDatabasesConfigType.php index 48e65dc938..c44a6e8ffd 100644 --- a/src/infrastructure/cluster/config/PhabricatorClusterDatabasesConfigOptionType.php +++ b/src/infrastructure/cluster/config/PhabricatorClusterDatabasesConfigType.php @@ -1,19 +1,17 @@ $spec) { if (!is_array($spec)) { - throw new Exception( + throw $this->newException( pht( 'Database cluster configuration is not valid: each entry in the '. 'list must be a dictionary describing a database host, but '. @@ -40,7 +38,7 @@ final class PhabricatorClusterDatabasesConfigOptionType 'persistent' => 'optional bool', )); } catch (Exception $ex) { - throw new Exception( + throw $this->newException( pht( 'Database cluster configuration has an invalid host '. 'specification (at index "%s"): %s.', @@ -57,7 +55,7 @@ final class PhabricatorClusterDatabasesConfigOptionType case 'replica': break; default: - throw new Exception( + throw $this->newException( pht( 'Database cluster configuration describes an invalid '. 'host ("%s", at index "%s") with an unrecognized role ("%s"). '. @@ -78,7 +76,7 @@ final class PhabricatorClusterDatabasesConfigOptionType // mistakes. $key = "{$host}:{$port}"; if (isset($map[$key])) { - throw new Exception( + throw $this->newException( pht( 'Database cluster configuration is invalid: it describes the '. 'same host ("%s") multiple times. Each host should appear only '. diff --git a/src/infrastructure/cluster/config/PhabricatorClusterSearchConfigOptionType.php b/src/infrastructure/cluster/config/PhabricatorClusterSearchConfigType.php similarity index 86% rename from src/infrastructure/cluster/config/PhabricatorClusterSearchConfigOptionType.php rename to src/infrastructure/cluster/config/PhabricatorClusterSearchConfigType.php index 90ead23e6d..ae54ff1a11 100644 --- a/src/infrastructure/cluster/config/PhabricatorClusterSearchConfigOptionType.php +++ b/src/infrastructure/cluster/config/PhabricatorClusterSearchConfigType.php @@ -1,20 +1,17 @@ $spec) { diff --git a/src/view/page/menu/PhabricatorMainMenuSearchView.php b/src/view/page/menu/PhabricatorMainMenuSearchView.php index d21d23d0fd..0b2ca8ab10 100644 --- a/src/view/page/menu/PhabricatorMainMenuSearchView.php +++ b/src/view/page/menu/PhabricatorMainMenuSearchView.php @@ -39,6 +39,9 @@ final class PhabricatorMainMenuSearchView extends AphrontView { 'name' => 'query', 'id' => $search_id, 'autocomplete' => 'off', + 'autocorrect' => 'off', + 'autocapitalize' => 'off', + 'spellcheck' => 'false', )); $target = javelin_tag(