diff --git a/.arcconfig b/.arcconfig index f0fa152929..88f04f72c5 100644 --- a/.arcconfig +++ b/.arcconfig @@ -1,4 +1,5 @@ { "phabricator.uri": "https://secure.phabricator.com/", - "load": ["src/"] + "load": ["src/"], + "history.immutable": false } diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 33f5f5af12..7d846e4cec 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,8 +7,8 @@ */ return array( 'names' => array( - 'core.pkg.css' => '8b87d014', - 'core.pkg.js' => '13c7e56a', + 'core.pkg.css' => '90c46327', + 'core.pkg.js' => 'b562c3db', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '3fb7f532', 'differential.pkg.js' => '634399e9', @@ -24,7 +24,7 @@ return array( 'rsrc/css/aphront/multi-column.css' => 'fd18389d', 'rsrc/css/aphront/notification.css' => '3f6c89c9', 'rsrc/css/aphront/panel-view.css' => '8427b78d', - 'rsrc/css/aphront/phabricator-nav-view.css' => 'ac79a758', + 'rsrc/css/aphront/phabricator-nav-view.css' => '09f3d0db', 'rsrc/css/aphront/table-view.css' => '832656fd', 'rsrc/css/aphront/tokenizer.css' => '056da01b', 'rsrc/css/aphront/tooltip.css' => '1a07aea8', @@ -94,17 +94,18 @@ return array( 'rsrc/css/application/policy/policy.css' => '957ea14c', 'rsrc/css/application/ponder/ponder-view.css' => 'fbd45f96', 'rsrc/css/application/project/project-card-view.css' => '9418c97d', - 'rsrc/css/application/project/project-view.css' => 'cbaa10a1', + 'rsrc/css/application/project/project-view.css' => '9ce99f21', 'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733', 'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5', 'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '8d8b92cd', 'rsrc/css/application/releeph/releeph-request-typeahead.css' => '667a48ae', + 'rsrc/css/application/search/application-search-view.css' => 'b3e0e5ef', 'rsrc/css/application/search/search-results.css' => '7dea472c', 'rsrc/css/application/slowvote/slowvote.css' => 'a94b7230', 'rsrc/css/application/tokens/tokens.css' => '3d0f239e', 'rsrc/css/application/uiexample/example.css' => '528b19de', 'rsrc/css/core/core.css' => 'd0801452', - 'rsrc/css/core/remarkup.css' => '523d34bb', + 'rsrc/css/core/remarkup.css' => '5ed06ed8', 'rsrc/css/core/syntax.css' => '769d3498', 'rsrc/css/core/z-index.css' => '5b6fcf3f', 'rsrc/css/diviner/diviner-shared.css' => 'aa3656aa', @@ -113,22 +114,22 @@ return array( 'rsrc/css/font/font-lato.css' => 'c7ccd872', 'rsrc/css/font/phui-font-icon-base.css' => '6449bce8', 'rsrc/css/layout/phabricator-filetree-view.css' => 'fccf9f82', - 'rsrc/css/layout/phabricator-side-menu-view.css' => 'dd849797', 'rsrc/css/layout/phabricator-source-code-view.css' => 'cbeef983', 'rsrc/css/phui/calendar/phui-calendar-day.css' => '572b1893', 'rsrc/css/phui/calendar/phui-calendar-list.css' => 'fcc9fb41', 'rsrc/css/phui/calendar/phui-calendar-month.css' => '8e10e92c', - 'rsrc/css/phui/calendar/phui-calendar.css' => 'daadaf39', + 'rsrc/css/phui/calendar/phui-calendar.css' => '477acfaa', 'rsrc/css/phui/phui-action-list.css' => 'c5eba19d', 'rsrc/css/phui/phui-action-panel.css' => '91c7b835', 'rsrc/css/phui/phui-badge.css' => '3baef8db', + 'rsrc/css/phui/phui-basic-nav-view.css' => '7093573b', 'rsrc/css/phui/phui-big-info-view.css' => 'bd903741', 'rsrc/css/phui/phui-box.css' => '5c8387cf', 'rsrc/css/phui/phui-button.css' => '4a5fbe3d', 'rsrc/css/phui/phui-chart.css' => '6bf6f78e', - 'rsrc/css/phui/phui-crumbs-view.css' => 'b4fa5755', + 'rsrc/css/phui/phui-crumbs-view.css' => '9dac418c', 'rsrc/css/phui/phui-curtain-view.css' => '7148ae25', - 'rsrc/css/phui/phui-document-pro.css' => 'a3730b94', + 'rsrc/css/phui/phui-document-pro.css' => 'dc3d46ed', 'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf', 'rsrc/css/phui/phui-document.css' => '715aedfb', 'rsrc/css/phui/phui-feed-story.css' => 'aa49845d', @@ -136,7 +137,7 @@ return array( 'rsrc/css/phui/phui-form-view.css' => 'fab0a10f', 'rsrc/css/phui/phui-form.css' => 'aac1d51d', 'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f', - 'rsrc/css/phui/phui-header-view.css' => '4c7dd8f5', + 'rsrc/css/phui/phui-header-view.css' => '06385974', 'rsrc/css/phui/phui-hovercard.css' => 'de1a2119', 'rsrc/css/phui/phui-icon-set-selector.css' => '1ab67aad', 'rsrc/css/phui/phui-icon.css' => 'd0534b71', @@ -148,7 +149,7 @@ return array( 'rsrc/css/phui/phui-object-item-list-view.css' => '8d99e42b', 'rsrc/css/phui/phui-pager.css' => 'bea33d23', 'rsrc/css/phui/phui-pinboard-view.css' => '2495140e', - 'rsrc/css/phui/phui-profile-menu.css' => 'c8557f33', + 'rsrc/css/phui/phui-profile-menu.css' => '8a3fc181', 'rsrc/css/phui/phui-property-list-view.css' => '6d8e58ac', 'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591', 'rsrc/css/phui/phui-segment-bar-view.css' => '46342871', @@ -498,7 +499,7 @@ return array( 'rsrc/js/core/behavior-more.js' => 'a80d0378', 'rsrc/js/core/behavior-object-selector.js' => 'e0ec7f2f', 'rsrc/js/core/behavior-oncopy.js' => '2926fff2', - 'rsrc/js/core/behavior-phabricator-nav.js' => '56a1ca03', + 'rsrc/js/core/behavior-phabricator-nav.js' => '08675c6d', 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '116cf19b', 'rsrc/js/core/behavior-read-only-warning.js' => 'ba158207', 'rsrc/js/core/behavior-refresh-csrf.js' => 'ab2f381b', @@ -541,6 +542,7 @@ return array( 'aphront-tokenizer-control-css' => '056da01b', 'aphront-tooltip-css' => '1a07aea8', 'aphront-typeahead-control-css' => 'd4f16145', + 'application-search-view-css' => 'b3e0e5ef', 'auth-css' => '0877ed6e', 'bulk-job-css' => 'df9c1d4a', 'changeset-view-manager' => 'a2828756', @@ -658,7 +660,7 @@ return array( 'javelin-behavior-phabricator-keyboard-pager' => 'a8da01f0', 'javelin-behavior-phabricator-keyboard-shortcuts' => '01fca1f0', 'javelin-behavior-phabricator-line-linker' => '1499a8cb', - 'javelin-behavior-phabricator-nav' => '56a1ca03', + 'javelin-behavior-phabricator-nav' => '08675c6d', 'javelin-behavior-phabricator-notification-example' => '8ce821c5', 'javelin-behavior-phabricator-object-selector' => 'e0ec7f2f', 'javelin-behavior-phabricator-oncopy' => '2926fff2', @@ -784,17 +786,16 @@ return array( 'phabricator-keyboard-shortcut' => '1ae869f2', 'phabricator-keyboard-shortcut-manager' => '4a021c10', 'phabricator-main-menu-view' => 'b623169f', - 'phabricator-nav-view-css' => 'ac79a758', + 'phabricator-nav-view-css' => '09f3d0db', 'phabricator-notification' => 'ccf1cbf8', 'phabricator-notification-css' => '3f6c89c9', 'phabricator-notification-menu-css' => 'f31c0bde', 'phabricator-object-selector-css' => '85ee8ce6', 'phabricator-phtize' => 'd254d646', 'phabricator-prefab' => 'cfd23f37', - 'phabricator-remarkup-css' => '523d34bb', + 'phabricator-remarkup-css' => '5ed06ed8', 'phabricator-search-results-css' => '7dea472c', 'phabricator-shaped-request' => '7cbe244b', - 'phabricator-side-menu-view-css' => 'dd849797', 'phabricator-slowvote-css' => 'a94b7230', 'phabricator-source-code-view-css' => 'cbeef983', 'phabricator-standard-page-view' => 'e709f6d0', @@ -824,26 +825,27 @@ return array( 'phriction-document-css' => '4282e4ad', 'phui-action-panel-css' => '91c7b835', 'phui-badge-view-css' => '3baef8db', + 'phui-basic-nav-view-css' => '7093573b', 'phui-big-info-view-css' => 'bd903741', 'phui-box-css' => '5c8387cf', 'phui-button-css' => '4a5fbe3d', - 'phui-calendar-css' => 'daadaf39', + 'phui-calendar-css' => '477acfaa', 'phui-calendar-day-css' => '572b1893', 'phui-calendar-list-css' => 'fcc9fb41', 'phui-calendar-month-css' => '8e10e92c', 'phui-chart-css' => '6bf6f78e', - 'phui-crumbs-view-css' => 'b4fa5755', + 'phui-crumbs-view-css' => '9dac418c', 'phui-curtain-view-css' => '7148ae25', 'phui-document-summary-view-css' => '9ca48bdf', 'phui-document-view-css' => '715aedfb', - 'phui-document-view-pro-css' => 'a3730b94', + 'phui-document-view-pro-css' => 'dc3d46ed', 'phui-feed-story-css' => 'aa49845d', 'phui-font-icon-base-css' => '6449bce8', 'phui-fontkit-css' => '9cda225e', 'phui-form-css' => 'aac1d51d', 'phui-form-view-css' => 'fab0a10f', 'phui-head-thing-view-css' => 'fd311e5f', - 'phui-header-view-css' => '4c7dd8f5', + 'phui-header-view-css' => '06385974', 'phui-hovercard' => '1bd28176', 'phui-hovercard-view-css' => 'de1a2119', 'phui-icon-set-selector-css' => '1ab67aad', @@ -857,7 +859,7 @@ return array( 'phui-object-item-list-view-css' => '8d99e42b', 'phui-pager-css' => 'bea33d23', 'phui-pinboard-view-css' => '2495140e', - 'phui-profile-menu-css' => 'c8557f33', + 'phui-profile-menu-css' => '8a3fc181', 'phui-property-list-view-css' => '6d8e58ac', 'phui-remarkup-preview-css' => '1a8f2591', 'phui-segment-bar-view-css' => '46342871', @@ -882,7 +884,7 @@ return array( 'policy-transaction-detail-css' => '82100a43', 'ponder-view-css' => 'fbd45f96', 'project-card-view-css' => '9418c97d', - 'project-view-css' => 'cbaa10a1', + 'project-view-css' => '9ce99f21', 'releeph-core' => '9b3c5733', 'releeph-preview-branch' => 'b7a6f4a5', 'releeph-request-differential-create-dialog' => '8d8b92cd', @@ -980,6 +982,16 @@ return array( 'phabricator-prefab', 'phuix-icon-view', ), + '08675c6d' => array( + 'javelin-behavior', + 'javelin-behavior-device', + 'javelin-stratcom', + 'javelin-dom', + 'javelin-magical-init', + 'javelin-vector', + 'javelin-request', + 'javelin-util', + ), '087e919c' => array( 'javelin-install', 'javelin-dom', @@ -1335,16 +1347,6 @@ return array( 'javelin-stratcom', 'javelin-vector', ), - '56a1ca03' => array( - 'javelin-behavior', - 'javelin-behavior-device', - 'javelin-stratcom', - 'javelin-dom', - 'javelin-magical-init', - 'javelin-vector', - 'javelin-request', - 'javelin-util', - ), '58dea2fa' => array( 'javelin-install', 'javelin-util', @@ -2252,7 +2254,7 @@ return array( 'phui-header-view-css', 'phabricator-filetree-view-css', 'phabricator-nav-view-css', - 'phabricator-side-menu-view-css', + 'phui-basic-nav-view-css', 'phui-crumbs-view-css', 'phui-object-item-list-view-css', 'global-drag-and-drop-css', diff --git a/resources/celerity/packages.php b/resources/celerity/packages.php index 2de94b5a8c..103d395f79 100644 --- a/resources/celerity/packages.php +++ b/resources/celerity/packages.php @@ -113,7 +113,7 @@ return array( 'phui-header-view-css', 'phabricator-filetree-view-css', 'phabricator-nav-view-css', - 'phabricator-side-menu-view-css', + 'phui-basic-nav-view-css', 'phui-crumbs-view-css', 'phui-object-item-list-view-css', 'global-drag-and-drop-css', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 1ac5528904..c5059723a5 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1106,7 +1106,9 @@ phutil_register_library_map(array( 'HarbormasterBuildEngine' => 'applications/harbormaster/engine/HarbormasterBuildEngine.php', 'HarbormasterBuildFailureException' => 'applications/harbormaster/exception/HarbormasterBuildFailureException.php', 'HarbormasterBuildGraph' => 'applications/harbormaster/engine/HarbormasterBuildGraph.php', + 'HarbormasterBuildInitiatorDatasource' => 'applications/harbormaster/typeahead/HarbormasterBuildInitiatorDatasource.php', 'HarbormasterBuildLintMessage' => 'applications/harbormaster/storage/build/HarbormasterBuildLintMessage.php', + 'HarbormasterBuildListController' => 'applications/harbormaster/controller/HarbormasterBuildListController.php', 'HarbormasterBuildLog' => 'applications/harbormaster/storage/build/HarbormasterBuildLog.php', 'HarbormasterBuildLogChunk' => 'applications/harbormaster/storage/build/HarbormasterBuildLogChunk.php', 'HarbormasterBuildLogChunkIterator' => 'applications/harbormaster/storage/build/HarbormasterBuildLogChunkIterator.php', @@ -1129,6 +1131,10 @@ phutil_register_library_map(array( 'HarbormasterBuildPlanTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildPlanTransactionQuery.php', 'HarbormasterBuildQuery' => 'applications/harbormaster/query/HarbormasterBuildQuery.php', 'HarbormasterBuildRequest' => 'applications/harbormaster/engine/HarbormasterBuildRequest.php', + 'HarbormasterBuildSearchConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterBuildSearchConduitAPIMethod.php', + 'HarbormasterBuildSearchEngine' => 'applications/harbormaster/query/HarbormasterBuildSearchEngine.php', + 'HarbormasterBuildStatus' => 'applications/harbormaster/constants/HarbormasterBuildStatus.php', + 'HarbormasterBuildStatusDatasource' => 'applications/harbormaster/typeahead/HarbormasterBuildStatusDatasource.php', 'HarbormasterBuildStep' => 'applications/harbormaster/storage/configuration/HarbormasterBuildStep.php', 'HarbormasterBuildStepCoreCustomField' => 'applications/harbormaster/customfield/HarbormasterBuildStepCoreCustomField.php', 'HarbormasterBuildStepCustomField' => 'applications/harbormaster/customfield/HarbormasterBuildStepCustomField.php', @@ -1199,6 +1205,7 @@ phutil_register_library_map(array( 'HarbormasterQueryAutotargetsConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterQueryAutotargetsConduitAPIMethod.php', 'HarbormasterQueryBuildablesConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterQueryBuildablesConduitAPIMethod.php', 'HarbormasterQueryBuildsConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterQueryBuildsConduitAPIMethod.php', + 'HarbormasterQueryBuildsSearchEngineAttachment' => 'applications/harbormaster/engineextension/HarbormasterQueryBuildsSearchEngineAttachment.php', 'HarbormasterRemarkupRule' => 'applications/harbormaster/remarkup/HarbormasterRemarkupRule.php', 'HarbormasterRunBuildPlansHeraldAction' => 'applications/harbormaster/herald/HarbormasterRunBuildPlansHeraldAction.php', 'HarbormasterSchemaSpec' => 'applications/harbormaster/storage/HarbormasterSchemaSpec.php', @@ -1277,6 +1284,7 @@ phutil_register_library_map(array( 'HeraldRepetitionPolicyConfig' => 'applications/herald/config/HeraldRepetitionPolicyConfig.php', 'HeraldRule' => 'applications/herald/storage/HeraldRule.php', 'HeraldRuleController' => 'applications/herald/controller/HeraldRuleController.php', + 'HeraldRuleDatasource' => 'applications/herald/typeahead/HeraldRuleDatasource.php', 'HeraldRuleEditor' => 'applications/herald/editor/HeraldRuleEditor.php', 'HeraldRuleListController' => 'applications/herald/controller/HeraldRuleListController.php', 'HeraldRulePHIDType' => 'applications/herald/phid/HeraldRulePHIDType.php', @@ -2036,6 +2044,9 @@ phutil_register_library_map(array( 'PhabricatorCalendarEventEndDateTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventEndDateTransaction.php', 'PhabricatorCalendarEventFrequencyTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventFrequencyTransaction.php', 'PhabricatorCalendarEventFulltextEngine' => 'applications/calendar/search/PhabricatorCalendarEventFulltextEngine.php', + 'PhabricatorCalendarEventHeraldAdapter' => 'applications/calendar/herald/PhabricatorCalendarEventHeraldAdapter.php', + 'PhabricatorCalendarEventHeraldField' => 'applications/calendar/herald/PhabricatorCalendarEventHeraldField.php', + 'PhabricatorCalendarEventHeraldFieldGroup' => 'applications/calendar/herald/PhabricatorCalendarEventHeraldFieldGroup.php', 'PhabricatorCalendarEventHostPolicyRule' => 'applications/calendar/policyrule/PhabricatorCalendarEventHostPolicyRule.php', 'PhabricatorCalendarEventHostTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventHostTransaction.php', 'PhabricatorCalendarEventIconTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventIconTransaction.php', @@ -2046,6 +2057,7 @@ phutil_register_library_map(array( 'PhabricatorCalendarEventJoinController' => 'applications/calendar/controller/PhabricatorCalendarEventJoinController.php', 'PhabricatorCalendarEventListController' => 'applications/calendar/controller/PhabricatorCalendarEventListController.php', 'PhabricatorCalendarEventMailReceiver' => 'applications/calendar/mail/PhabricatorCalendarEventMailReceiver.php', + 'PhabricatorCalendarEventNameHeraldField' => 'applications/calendar/herald/PhabricatorCalendarEventNameHeraldField.php', 'PhabricatorCalendarEventNameTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventNameTransaction.php', 'PhabricatorCalendarEventPHIDType' => 'applications/calendar/phid/PhabricatorCalendarEventPHIDType.php', 'PhabricatorCalendarEventQuery' => 'applications/calendar/query/PhabricatorCalendarEventQuery.php', @@ -2254,6 +2266,7 @@ phutil_register_library_map(array( 'PhabricatorCustomFieldNumericIndexStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldNumericIndexStorage.php', 'PhabricatorCustomFieldSearchEngineExtension' => 'infrastructure/customfield/engineextension/PhabricatorCustomFieldSearchEngineExtension.php', 'PhabricatorCustomFieldStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php', + 'PhabricatorCustomFieldStorageQuery' => 'infrastructure/customfield/query/PhabricatorCustomFieldStorageQuery.php', 'PhabricatorCustomFieldStringIndexStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldStringIndexStorage.php', 'PhabricatorCustomHeaderConfigType' => 'applications/config/custom/PhabricatorCustomHeaderConfigType.php', 'PhabricatorDaemon' => 'infrastructure/daemon/PhabricatorDaemon.php', @@ -5630,6 +5643,7 @@ phutil_register_library_map(array( 'HarbormasterDAO', 'PhabricatorApplicationTransactionInterface', 'PhabricatorPolicyInterface', + 'PhabricatorConduitResultInterface', ), 'HarbormasterBuildAbortedException' => 'Exception', 'HarbormasterBuildActionController' => 'HarbormasterController', @@ -5646,7 +5660,9 @@ phutil_register_library_map(array( 'HarbormasterBuildEngine' => 'Phobject', 'HarbormasterBuildFailureException' => 'Exception', 'HarbormasterBuildGraph' => 'AbstractDirectedGraph', + 'HarbormasterBuildInitiatorDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'HarbormasterBuildLintMessage' => 'HarbormasterDAO', + 'HarbormasterBuildListController' => 'HarbormasterController', 'HarbormasterBuildLog' => array( 'HarbormasterDAO', 'PhabricatorPolicyInterface', @@ -5682,6 +5698,10 @@ phutil_register_library_map(array( 'HarbormasterBuildPlanTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'HarbormasterBuildQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'HarbormasterBuildRequest' => 'Phobject', + 'HarbormasterBuildSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', + 'HarbormasterBuildSearchEngine' => 'PhabricatorApplicationSearchEngine', + 'HarbormasterBuildStatus' => 'Phobject', + 'HarbormasterBuildStatusDatasource' => 'PhabricatorTypeaheadDatasource', 'HarbormasterBuildStep' => array( 'HarbormasterDAO', 'PhabricatorApplicationTransactionInterface', @@ -5765,6 +5785,7 @@ phutil_register_library_map(array( 'HarbormasterQueryAutotargetsConduitAPIMethod' => 'HarbormasterConduitAPIMethod', 'HarbormasterQueryBuildablesConduitAPIMethod' => 'HarbormasterConduitAPIMethod', 'HarbormasterQueryBuildsConduitAPIMethod' => 'HarbormasterConduitAPIMethod', + 'HarbormasterQueryBuildsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', 'HarbormasterRemarkupRule' => 'PhabricatorObjectRemarkupRule', 'HarbormasterRunBuildPlansHeraldAction' => 'HeraldAction', 'HarbormasterSchemaSpec' => 'PhabricatorConfigSchemaSpec', @@ -5856,6 +5877,7 @@ phutil_register_library_map(array( 'PhabricatorSubscribableInterface', ), 'HeraldRuleController' => 'HeraldController', + 'HeraldRuleDatasource' => 'PhabricatorTypeaheadDatasource', 'HeraldRuleEditor' => 'PhabricatorApplicationTransactionEditor', 'HeraldRuleListController' => 'HeraldController', 'HeraldRulePHIDType' => 'PhabricatorPHIDType', @@ -6739,6 +6761,9 @@ phutil_register_library_map(array( 'PhabricatorCalendarEventEndDateTransaction' => 'PhabricatorCalendarEventDateTransaction', 'PhabricatorCalendarEventFrequencyTransaction' => 'PhabricatorCalendarEventTransactionType', 'PhabricatorCalendarEventFulltextEngine' => 'PhabricatorFulltextEngine', + 'PhabricatorCalendarEventHeraldAdapter' => 'HeraldAdapter', + 'PhabricatorCalendarEventHeraldField' => 'HeraldField', + 'PhabricatorCalendarEventHeraldFieldGroup' => 'HeraldFieldGroup', 'PhabricatorCalendarEventHostPolicyRule' => 'PhabricatorPolicyRule', 'PhabricatorCalendarEventHostTransaction' => 'PhabricatorCalendarEventTransactionType', 'PhabricatorCalendarEventIconTransaction' => 'PhabricatorCalendarEventTransactionType', @@ -6752,6 +6777,7 @@ phutil_register_library_map(array( 'PhabricatorCalendarEventJoinController' => 'PhabricatorCalendarController', 'PhabricatorCalendarEventListController' => 'PhabricatorCalendarController', 'PhabricatorCalendarEventMailReceiver' => 'PhabricatorObjectMailReceiver', + 'PhabricatorCalendarEventNameHeraldField' => 'PhabricatorCalendarEventHeraldField', 'PhabricatorCalendarEventNameTransaction' => 'PhabricatorCalendarEventTransactionType', 'PhabricatorCalendarEventPHIDType' => 'PhabricatorPHIDType', 'PhabricatorCalendarEventQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', @@ -6986,6 +7012,7 @@ phutil_register_library_map(array( 'PhabricatorCustomFieldNumericIndexStorage' => 'PhabricatorCustomFieldIndexStorage', 'PhabricatorCustomFieldSearchEngineExtension' => 'PhabricatorSearchEngineExtension', 'PhabricatorCustomFieldStorage' => 'PhabricatorLiskDAO', + 'PhabricatorCustomFieldStorageQuery' => 'Phobject', 'PhabricatorCustomFieldStringIndexStorage' => 'PhabricatorCustomFieldIndexStorage', 'PhabricatorCustomHeaderConfigType' => 'PhabricatorConfigOptionType', 'PhabricatorDaemon' => 'PhutilDaemon', diff --git a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php index 2b457418d0..5498efadd3 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php +++ b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php @@ -289,5 +289,19 @@ final class PhabricatorCalendarEventEditor return $body; } + protected function shouldApplyHeraldRules( + PhabricatorLiskDAO $object, + array $xactions) { + return true; + } + + protected function buildHeraldAdapter( + PhabricatorLiskDAO $object, + array $xactions) { + + return id(new PhabricatorCalendarEventHeraldAdapter()) + ->setObject($object); + } + } diff --git a/src/applications/calendar/herald/PhabricatorCalendarEventHeraldAdapter.php b/src/applications/calendar/herald/PhabricatorCalendarEventHeraldAdapter.php new file mode 100644 index 0000000000..49d5f38959 --- /dev/null +++ b/src/applications/calendar/herald/PhabricatorCalendarEventHeraldAdapter.php @@ -0,0 +1,56 @@ +object = $object; + return $this; + } + + public function getObject() { + return $this->object; + } + + public function getAdapterContentName() { + return pht('Calendar Events'); + } + + public function supportsRuleType($rule_type) { + switch ($rule_type) { + case HeraldRuleTypeConfig::RULE_TYPE_GLOBAL: + case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL: + return true; + case HeraldRuleTypeConfig::RULE_TYPE_OBJECT: + default: + return false; + } + } + + public function getHeraldName() { + return $this->getObject()->getMonogram(); + } + +} diff --git a/src/applications/calendar/herald/PhabricatorCalendarEventHeraldField.php b/src/applications/calendar/herald/PhabricatorCalendarEventHeraldField.php new file mode 100644 index 0000000000..7d9d32a5de --- /dev/null +++ b/src/applications/calendar/herald/PhabricatorCalendarEventHeraldField.php @@ -0,0 +1,13 @@ +getName(); + } + + protected function getHeraldFieldStandardType() { + return self::STANDARD_TEXT; + } + +} diff --git a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php index f093130bb4..9420261a59 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php @@ -369,13 +369,13 @@ final class PhabricatorCalendarEventSearchEngine ->setName($from->format('F Y')); $header = id(new PHUIHeaderView()) + ->setProfileHeader(true) ->setHeader($from->format('F Y')); return id(new PhabricatorApplicationSearchResultView()) ->setCrumbs($crumbs) ->setHeader($header) - ->setContent($month_view) - ->setCollapsed(true); + ->setContent($month_view); } private function buildCalendarDayView( @@ -443,13 +443,13 @@ final class PhabricatorCalendarEventSearchEngine ); $header = id(new PHUIHeaderView()) + ->setProfileHeader(true) ->setHeader($from->format('D, F jS')); return id(new PhabricatorApplicationSearchResultView()) ->setCrumbs($crumbs) ->setHeader($header) - ->setContent($day_view) - ->setCollapsed(true); + ->setContent($day_view); } private function getDisplayYearAndMonthAndDay( diff --git a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php index 7eb3b248c1..7b2fed7686 100644 --- a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php +++ b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php @@ -194,6 +194,7 @@ final class CelerityDefaultPostprocessor // Background color for "most" themes. 'page.background' => '#f8f8fb', + 'page.sidenav' => '#f0f0f2', 'menu.profile.text' => 'rgba(255,255,255,.8)', 'menu.profile.text.selected' => 'rgba(255,255,255,1)', diff --git a/src/applications/conduit/query/ConduitResultSearchEngineExtension.php b/src/applications/conduit/query/ConduitResultSearchEngineExtension.php index 9bf4b1a346..f3ca974fde 100644 --- a/src/applications/conduit/query/ConduitResultSearchEngineExtension.php +++ b/src/applications/conduit/query/ConduitResultSearchEngineExtension.php @@ -25,7 +25,7 @@ final class ConduitResultSearchEngineExtension return $object->getFieldSpecificationsForConduit(); } - public function getFieldValuesForConduit($object) { + public function getFieldValuesForConduit($object, $data) { return $object->getFieldValuesForConduit(); } diff --git a/src/applications/differential/conduit/DifferentialConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialConduitAPIMethod.php index e5b71c87bb..fdcf4395dd 100644 --- a/src/applications/differential/conduit/DifferentialConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialConduitAPIMethod.php @@ -150,21 +150,69 @@ abstract class DifferentialConduitAPIMethod extends ConduitAPIMethod { array $revisions) { assert_instances_of($revisions, 'DifferentialRevision'); - $results = array(); + if (!$revisions) { + return array(); + } + + $field_lists = array(); foreach ($revisions as $revision) { - // TODO: This is inefficient and issues a query for each object. + $revision_phid = $revision->getPHID(); + $field_list = PhabricatorCustomField::getObjectFields( $revision, PhabricatorCustomField::ROLE_CONDUIT); $field_list ->setViewer($viewer) - ->readFieldsFromStorage($revision); + ->readFieldsFromObject($revision); + $field_lists[$revision_phid] = $field_list; + } + + $all_fields = array(); + foreach ($field_lists as $field_list) { + foreach ($field_list->getFields() as $field) { + $all_fields[] = $field; + } + } + + id(new PhabricatorCustomFieldStorageQuery()) + ->addFields($all_fields) + ->execute(); + + $results = array(); + foreach ($field_lists as $revision_phid => $field_list) { + $results[$revision_phid] = array(); foreach ($field_list->getFields() as $field) { $field_key = $field->getFieldKeyForConduit(); $value = $field->getConduitDictionaryValue(); - $results[$revision->getPHID()][$field_key] = $value; + $results[$revision_phid][$field_key] = $value; + } + } + + // For compatibility, fill in these "custom fields" by querying for them + // efficiently. See T11404 for discussion. + + $legacy_edge_map = array( + 'phabricator:projects' => + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, + 'phabricator:depends-on' => + DifferentialRevisionDependsOnRevisionEdgeType::EDGECONST, + ); + + $query = id(new PhabricatorEdgeQuery()) + ->withSourcePHIDs(array_keys($results)) + ->withEdgeTypes($legacy_edge_map); + + $query->execute(); + + foreach ($results as $revision_phid => $dict) { + foreach ($legacy_edge_map as $edge_key => $edge_type) { + $phid_list = $query->getDestinationPHIDs( + array($revision_phid), + array($edge_type)); + + $results[$revision_phid][$edge_key] = $phid_list; } } diff --git a/src/applications/differential/customfield/DifferentialParentRevisionsField.php b/src/applications/differential/customfield/DifferentialParentRevisionsField.php index 30ecea4bf2..5dbc99dd6f 100644 --- a/src/applications/differential/customfield/DifferentialParentRevisionsField.php +++ b/src/applications/differential/customfield/DifferentialParentRevisionsField.php @@ -7,10 +7,6 @@ final class DifferentialParentRevisionsField return 'differential:depends-on'; } - public function getFieldKeyForConduit() { - return 'phabricator:depends-on'; - } - public function getFieldName() { return pht('Parent Revisions'); } @@ -33,7 +29,10 @@ final class DifferentialParentRevisionsField } public function shouldAppearInConduitDictionary() { - return true; + // To improve performance, we exclude this field from Conduit results. + // See T11404 for discussion. In modern "differential.revision.search", + // this information is available efficiently as an attachment. + return false; } public function getConduitDictionaryValue() { diff --git a/src/applications/differential/customfield/DifferentialProjectsField.php b/src/applications/differential/customfield/DifferentialProjectsField.php index 53bd7293fd..425e30e132 100644 --- a/src/applications/differential/customfield/DifferentialProjectsField.php +++ b/src/applications/differential/customfield/DifferentialProjectsField.php @@ -91,7 +91,10 @@ final class DifferentialProjectsField } public function shouldAppearInConduitDictionary() { - return true; + // To improve performance, we exclude this field from Conduit results. + // See T11404 for discussion. In modern "differential.revision.search", + // this information is available efficiently as an attachment. + return false; } public function getApplicationTransactionMetadata() { diff --git a/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php b/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php index d7b8c5dd33..2aed146cbf 100644 --- a/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php +++ b/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php @@ -20,6 +20,21 @@ final class HeraldDifferentialRevisionAdapter return new DifferentialRevision(); } + public function isTestAdapterForObject($object) { + return ($object instanceof DifferentialRevision); + } + + public function getAdapterTestDescription() { + return pht( + 'Test rules which run when a revision is created or updated.'); + } + + public function newTestAdapter($object) { + return self::newLegacyAdapter( + $object, + $object->loadActiveDiff()); + } + protected function initializeNewAdapter() { $this->revision = $this->newObject(); } diff --git a/src/applications/diffusion/herald/HeraldCommitAdapter.php b/src/applications/diffusion/herald/HeraldCommitAdapter.php index 67234edeab..538753f669 100644 --- a/src/applications/diffusion/herald/HeraldCommitAdapter.php +++ b/src/applications/diffusion/herald/HeraldCommitAdapter.php @@ -27,10 +27,24 @@ final class HeraldCommitAdapter return new PhabricatorRepositoryCommit(); } + public function isTestAdapterForObject($object) { + return ($object instanceof PhabricatorRepositoryCommit); + } + + public function getAdapterTestDescription() { + return pht( + 'Test rules which run after a commit is discovered and imported.'); + } + protected function initializeNewAdapter() { $this->commit = $this->newObject(); } + public function setObject($object) { + $this->commit = $object; + return $this; + } + public function getObject() { return $this->commit; } diff --git a/src/applications/diffusion/herald/HeraldPreCommitAdapter.php b/src/applications/diffusion/herald/HeraldPreCommitAdapter.php index 6b5b94b578..3f1bc6bfd0 100644 --- a/src/applications/diffusion/herald/HeraldPreCommitAdapter.php +++ b/src/applications/diffusion/herald/HeraldPreCommitAdapter.php @@ -25,6 +25,20 @@ abstract class HeraldPreCommitAdapter extends HeraldAdapter { return 'PhabricatorDiffusionApplication'; } + public function isTestAdapterForObject($object) { + return ($object instanceof PhabricatorRepositoryCommit); + } + + public function canCreateTestAdapterForObject($object) { + return false; + } + + public function getAdapterTestDescription() { + return pht( + 'Commit hook events depend on repository state which is only available '. + 'at push time, and can not be run in test mode.'); + } + protected function initializeNewAdapter() { $this->log = new PhabricatorRepositoryPushLog(); } diff --git a/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php b/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php index 00fb570e03..ed7104965a 100644 --- a/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php +++ b/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php @@ -70,6 +70,7 @@ final class PhabricatorHarbormasterApplication extends PhabricatorApplication { => 'HarbormasterBuildableActionController', ), 'build/' => array( + $this->getQueryRoutePattern() => 'HarbormasterBuildListController', '(?P\d+)/' => 'HarbormasterBuildViewController', '(?Ppause|resume|restart|abort)/'. '(?P\d+)/(?:(?P[^/]+)/)?' diff --git a/src/applications/harbormaster/conduit/HarbormasterBuildSearchConduitAPIMethod.php b/src/applications/harbormaster/conduit/HarbormasterBuildSearchConduitAPIMethod.php new file mode 100644 index 0000000000..daa3d6287e --- /dev/null +++ b/src/applications/harbormaster/conduit/HarbormasterBuildSearchConduitAPIMethod.php @@ -0,0 +1,18 @@ + 'optional list', @@ -27,64 +35,42 @@ final class HarbormasterQueryBuildsConduitAPIMethod protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); + $call = new ConduitCall( + 'harbormaster.build.search', + array_filter(array( + 'constraints' => array_filter(array( + 'ids' => $request->getValue('ids'), + 'phids' => $request->getValue('phids'), + 'statuses' => $request->getValue('buildStatuses'), + 'buildables' => $request->getValue('buildablePHIDs'), + 'plans' => $request->getValue('buildPlanPHIDs'), + )), + 'attachments' => array( + 'querybuilds' => true, + ), + 'limit' => $request->getValue('limit'), + 'before' => $request->getValue('before'), + 'after' => $request->getValue('after'), + ))); - $query = id(new HarbormasterBuildQuery()) - ->setViewer($viewer); - - $ids = $request->getValue('ids'); - if ($ids !== null) { - $query->withIDs($ids); - } - - $phids = $request->getValue('phids'); - if ($phids !== null) { - $query->withPHIDs($phids); - } - - $statuses = $request->getValue('buildStatuses'); - if ($statuses !== null) { - $query->withBuildStatuses($statuses); - } - - $buildable_phids = $request->getValue('buildablePHIDs'); - if ($buildable_phids !== null) { - $query->withBuildablePHIDs($buildable_phids); - } - - $build_plan_phids = $request->getValue('buildPlanPHIDs'); - if ($build_plan_phids !== null) { - $query->withBuildPlanPHIDs($build_plan_phids); - } - - $pager = $this->newPager($request); - - $builds = $query->executeWithCursorPager($pager); + $subsumption = $call->setUser($viewer) + ->execute(); $data = array(); - foreach ($builds as $build) { - - $id = $build->getID(); - $uri = '/harbormaster/build/'.$id.'/'; - $status = $build->getBuildStatus(); - - $data[] = array( - 'id' => $id, - 'phid' => $build->getPHID(), - 'uri' => PhabricatorEnv::getProductionURI($uri), - 'name' => $build->getBuildPlan()->getName(), - 'buildablePHID' => $build->getBuildablePHID(), - 'buildPlanPHID' => $build->getBuildPlanPHID(), - 'buildStatus' => $status, - 'buildStatusName' => HarbormasterBuild::getBuildStatusName($status), - ); + foreach ($subsumption['data'] as $build_data) { + $querybuilds = idxv( + $build_data, + array('attachments', 'querybuilds'), + array()); + $fields = idx($build_data, 'fields', array()); + unset($build_data['fields']); + unset($build_data['attachments']); + $data[] = array_mergev(array($build_data, $querybuilds, $fields)); } - $results = array( - 'data' => $data, - ); + $subsumption['data'] = $data; - $results = $this->addPagerResults($results, $pager); - return $results; + return $subsumption; } } diff --git a/src/applications/harbormaster/constants/HarbormasterBuildStatus.php b/src/applications/harbormaster/constants/HarbormasterBuildStatus.php new file mode 100644 index 0000000000..a2cdba5b63 --- /dev/null +++ b/src/applications/harbormaster/constants/HarbormasterBuildStatus.php @@ -0,0 +1,145 @@ + pht('Inactive'), + self::STATUS_PENDING => pht('Pending'), + self::STATUS_BUILDING => pht('Building'), + self::STATUS_PASSED => pht('Passed'), + self::STATUS_FAILED => pht('Failed'), + self::STATUS_ABORTED => pht('Aborted'), + self::STATUS_ERROR => pht('Unexpected Error'), + self::STATUS_PAUSED => pht('Paused'), + self::STATUS_DEADLOCKED => pht('Deadlocked'), + ); + } + + public static function getBuildStatusIcon($status) { + switch ($status) { + case self::STATUS_INACTIVE: + case self::STATUS_PENDING: + return PHUIStatusItemView::ICON_OPEN; + case self::STATUS_BUILDING: + return PHUIStatusItemView::ICON_RIGHT; + case self::STATUS_PASSED: + return PHUIStatusItemView::ICON_ACCEPT; + case self::STATUS_FAILED: + return PHUIStatusItemView::ICON_REJECT; + case self::STATUS_ABORTED: + return PHUIStatusItemView::ICON_MINUS; + case self::STATUS_ERROR: + return PHUIStatusItemView::ICON_MINUS; + case self::STATUS_PAUSED: + return PHUIStatusItemView::ICON_MINUS; + case self::STATUS_DEADLOCKED: + return PHUIStatusItemView::ICON_WARNING; + default: + return PHUIStatusItemView::ICON_QUESTION; + } + } + + public static function getBuildStatusColor($status) { + switch ($status) { + case self::STATUS_INACTIVE: + return 'dark'; + case self::STATUS_PENDING: + case self::STATUS_BUILDING: + return 'blue'; + case self::STATUS_PASSED: + return 'green'; + case self::STATUS_FAILED: + case self::STATUS_ABORTED: + case self::STATUS_ERROR: + case self::STATUS_DEADLOCKED: + return 'red'; + case self::STATUS_PAUSED: + return 'dark'; + default: + return 'bluegrey'; + } + } + + public static function getWaitingStatusConstants() { + return array( + self::STATUS_INACTIVE, + self::STATUS_PENDING, + ); + } + + public static function getActiveStatusConstants() { + return array( + self::STATUS_BUILDING, + self::STATUS_PAUSED, + ); + } + + public static function getCompletedStatusConstants() { + return array( + self::STATUS_PASSED, + self::STATUS_FAILED, + self::STATUS_ABORTED, + self::STATUS_ERROR, + self::STATUS_DEADLOCKED, + ); + } + +} diff --git a/src/applications/harbormaster/controller/HarbormasterBuildListController.php b/src/applications/harbormaster/controller/HarbormasterBuildListController.php new file mode 100644 index 0000000000..0da9f57d6f --- /dev/null +++ b/src/applications/harbormaster/controller/HarbormasterBuildListController.php @@ -0,0 +1,15 @@ +setController($this) + ->buildResponse(); + } + +} diff --git a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php index f78db68149..7932451d2e 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php @@ -581,9 +581,9 @@ final class HarbormasterBuildViewController } else { $status = $build->getBuildStatus(); $status_name = - HarbormasterBuild::getBuildStatusName($status); - $icon = HarbormasterBuild::getBuildStatusIcon($status); - $color = HarbormasterBuild::getBuildStatusColor($status); + HarbormasterBuildStatus::getBuildStatusName($status); + $icon = HarbormasterBuildStatus::getBuildStatusIcon($status); + $color = HarbormasterBuildStatus::getBuildStatusColor($status); } $item->setTarget($status_name); diff --git a/src/applications/harbormaster/controller/HarbormasterBuildableListController.php b/src/applications/harbormaster/controller/HarbormasterBuildableListController.php index 99595335be..1c08b1950f 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildableListController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildableListController.php @@ -9,6 +9,15 @@ final class HarbormasterBuildableListController extends HarbormasterController { public function handleRequest(AphrontRequest $request) { $items = array(); + $items[] = id(new PHUIListItemView()) + ->setType(PHUIListItemView::TYPE_LABEL) + ->setName(pht('Builds')); + + $items[] = id(new PHUIListItemView()) + ->setType(PHUIListItemView::TYPE_LINK) + ->setName(pht('Browse Builds')) + ->setHref($this->getApplicationURI('build/')); + $items[] = id(new PHUIListItemView()) ->setType(PHUIListItemView::TYPE_LABEL) ->setName(pht('Build Plans')); diff --git a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php index 390ff2ec3d..6a47dae429 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php @@ -196,11 +196,10 @@ final class HarbormasterBuildableViewController ->setHref($view_uri); $status = $build->getBuildStatus(); - $item->setStatusIcon( - 'fa-dot-circle-o '.HarbormasterBuild::getBuildStatusColor($status), - HarbormasterBuild::getBuildStatusName($status)); - - $item->addAttribute(HarbormasterBuild::getBuildStatusName($status)); + $status_color = HarbormasterBuildStatus::getBuildStatusColor($status); + $status_name = HarbormasterBuildStatus::getBuildStatusName($status); + $item->setStatusIcon('fa-dot-circle-o '.$status_color, $status_name); + $item->addAttribute($status_name); if ($build->isRestarting()) { $item->addIcon('fa-repeat', pht('Restarting')); diff --git a/src/applications/harbormaster/engine/HarbormasterBuildEngine.php b/src/applications/harbormaster/engine/HarbormasterBuildEngine.php index 273f899f91..4cece6a69d 100644 --- a/src/applications/harbormaster/engine/HarbormasterBuildEngine.php +++ b/src/applications/harbormaster/engine/HarbormasterBuildEngine.php @@ -63,7 +63,7 @@ final class HarbormasterBuildEngine extends Phobject { // If any exception is raised, the build is marked as a failure and the // exception is re-thrown (this ensures we don't leave builds in an // inconsistent state). - $build->setBuildStatus(HarbormasterBuild::STATUS_ERROR); + $build->setBuildStatus(HarbormasterBuildStatus::STATUS_ERROR); $build->save(); $lock->unlock(); @@ -106,30 +106,30 @@ final class HarbormasterBuildEngine extends Phobject { private function updateBuild(HarbormasterBuild $build) { if ($build->isAborting()) { $this->releaseAllArtifacts($build); - $build->setBuildStatus(HarbormasterBuild::STATUS_ABORTED); + $build->setBuildStatus(HarbormasterBuildStatus::STATUS_ABORTED); $build->save(); } - if (($build->getBuildStatus() == HarbormasterBuild::STATUS_PENDING) || + if (($build->getBuildStatus() == HarbormasterBuildStatus::STATUS_PENDING) || ($build->isRestarting())) { $this->restartBuild($build); - $build->setBuildStatus(HarbormasterBuild::STATUS_BUILDING); + $build->setBuildStatus(HarbormasterBuildStatus::STATUS_BUILDING); $build->save(); } if ($build->isResuming()) { - $build->setBuildStatus(HarbormasterBuild::STATUS_BUILDING); + $build->setBuildStatus(HarbormasterBuildStatus::STATUS_BUILDING); $build->save(); } if ($build->isPausing() && !$build->isComplete()) { - $build->setBuildStatus(HarbormasterBuild::STATUS_PAUSED); + $build->setBuildStatus(HarbormasterBuildStatus::STATUS_PAUSED); $build->save(); } $build->deleteUnprocessedCommands(); - if ($build->getBuildStatus() == HarbormasterBuild::STATUS_BUILDING) { + if ($build->getBuildStatus() == HarbormasterBuildStatus::STATUS_BUILDING) { $this->updateBuildSteps($build); } } @@ -243,7 +243,7 @@ final class HarbormasterBuildEngine extends Phobject { // If any step failed, fail the whole build, then bail. if (count($failed)) { - $build->setBuildStatus(HarbormasterBuild::STATUS_FAILED); + $build->setBuildStatus(HarbormasterBuildStatus::STATUS_FAILED); $build->save(); return; } @@ -251,7 +251,7 @@ final class HarbormasterBuildEngine extends Phobject { // If every step is complete, we're done with this build. Mark it passed // and bail. if (count($complete) == count($steps)) { - $build->setBuildStatus(HarbormasterBuild::STATUS_PASSED); + $build->setBuildStatus(HarbormasterBuildStatus::STATUS_PASSED); $build->save(); return; } @@ -287,7 +287,7 @@ final class HarbormasterBuildEngine extends Phobject { if (!$runnable && !$waiting && !$underway) { // This means the build is deadlocked, and the user has configured // circular dependencies. - $build->setBuildStatus(HarbormasterBuild::STATUS_DEADLOCKED); + $build->setBuildStatus(HarbormasterBuildStatus::STATUS_DEADLOCKED); $build->save(); return; } @@ -443,14 +443,14 @@ final class HarbormasterBuildEngine extends Phobject { $all_pass = true; $any_fail = false; foreach ($buildable->getBuilds() as $build) { - if ($build->getBuildStatus() != HarbormasterBuild::STATUS_PASSED) { + if ($build->getBuildStatus() != HarbormasterBuildStatus::STATUS_PASSED) { $all_pass = false; } - if ($build->getBuildStatus() == HarbormasterBuild::STATUS_FAILED || - $build->getBuildStatus() == HarbormasterBuild::STATUS_ERROR || - $build->getBuildStatus() == HarbormasterBuild::STATUS_DEADLOCKED) { - $any_fail = true; - } + $any_fail = in_array($build->getBuildStatus(), array( + HarbormasterBuildStatus::STATUS_FAILED, + HarbormasterBuildStatus::STATUS_ERROR, + HarbormasterBuildStatus::STATUS_DEADLOCKED, + )); } if ($any_fail) { diff --git a/src/applications/harbormaster/engineextension/HarbormasterQueryBuildsSearchEngineAttachment.php b/src/applications/harbormaster/engineextension/HarbormasterQueryBuildsSearchEngineAttachment.php new file mode 100644 index 0000000000..ce46ee9cba --- /dev/null +++ b/src/applications/harbormaster/engineextension/HarbormasterQueryBuildsSearchEngineAttachment.php @@ -0,0 +1,28 @@ +getBuildStatus()); + return array( + 'uri' => PhabricatorEnv::getProductionURI($object->getURI()), + 'name' => $object->getName(), + 'buildStatusName' => $status_name, + ); + } + +} diff --git a/src/applications/harbormaster/event/HarbormasterUIEventListener.php b/src/applications/harbormaster/event/HarbormasterUIEventListener.php index 82ffcad560..219f556b3a 100644 --- a/src/applications/harbormaster/event/HarbormasterUIEventListener.php +++ b/src/applications/harbormaster/event/HarbormasterUIEventListener.php @@ -132,9 +132,9 @@ final class HarbormasterUIEventListener } $status = $build->getBuildStatus(); - $status_name = HarbormasterBuild::getBuildStatusName($status); - $icon = HarbormasterBuild::getBuildStatusIcon($status); - $color = HarbormasterBuild::getBuildStatusColor($status); + $status_name = HarbormasterBuildStatus::getBuildStatusName($status); + $icon = HarbormasterBuildStatus::getBuildStatusIcon($status); + $color = HarbormasterBuildStatus::getBuildStatusColor($status); $item->setIcon($icon, $color, $status_name); diff --git a/src/applications/harbormaster/query/HarbormasterBuildQuery.php b/src/applications/harbormaster/query/HarbormasterBuildQuery.php index 1ee1ecb3a8..cf6db6115b 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildQuery.php @@ -8,6 +8,7 @@ final class HarbormasterBuildQuery private $buildStatuses; private $buildablePHIDs; private $buildPlanPHIDs; + private $initiatorPHIDs; private $needBuildTargets; public function withIDs(array $ids) { @@ -35,6 +36,11 @@ final class HarbormasterBuildQuery return $this; } + public function withInitiatorPHIDs(array $initiator_phids) { + $this->initiatorPHIDs = $initiator_phids; + return $this; + } + public function needBuildTargets($need_targets) { $this->needBuildTargets = $need_targets; return $this; @@ -167,6 +173,13 @@ final class HarbormasterBuildQuery $this->buildPlanPHIDs); } + if ($this->initiatorPHIDs !== null) { + $where[] = qsprintf( + $conn, + 'initiatorPHID IN (%Ls)', + $this->initiatorPHIDs); + } + return $where; } diff --git a/src/applications/harbormaster/query/HarbormasterBuildSearchEngine.php b/src/applications/harbormaster/query/HarbormasterBuildSearchEngine.php new file mode 100644 index 0000000000..948fea389c --- /dev/null +++ b/src/applications/harbormaster/query/HarbormasterBuildSearchEngine.php @@ -0,0 +1,172 @@ +setLabel(pht('Build Plans')) + ->setKey('plans') + ->setAliases(array('plan')) + ->setDescription( + pht('Search for builds running a given build plan.')) + ->setDatasource(new HarbormasterBuildPlanDatasource()), + id(new PhabricatorPHIDsSearchField()) + ->setLabel(pht('Buildables')) + ->setKey('buildables') + ->setAliases(array('buildable')) + ->setDescription( + pht('Search for builds running against particular buildables.')), + id(new PhabricatorSearchDatasourceField()) + ->setLabel(pht('Statuses')) + ->setKey('statuses') + ->setAliases(array('status')) + ->setDescription( + pht('Search for builds with given statuses.')) + ->setDatasource(new HarbormasterBuildStatusDatasource()), + id(new PhabricatorSearchDatasourceField()) + ->setLabel(pht('Initiators')) + ->setKey('initiators') + ->setAliases(array('initiator')) + ->setDescription( + pht( + 'Search for builds started by someone or something in particular.')) + ->setDatasource(new HarbormasterBuildInitiatorDatasource()), + ); + } + + protected function getHiddenFields() { + return array( + 'buildables', + ); + } + + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); + + if ($map['plans']) { + $query->withBuildPlanPHIDs($map['plans']); + } + + if ($map['statuses']) { + $query->withBuildStatuses($map['statuses']); + } + + if ($map['initiators']) { + $query->withInitiatorPHIDs($map['initiators']); + } + + return $query; + } + + protected function getURI($path) { + return '/harbormaster/build/'.$path; + } + + protected function getBuiltinQueryNames() { + return array( + 'initiated' => pht('My Builds'), + 'all' => pht('All Builds'), + 'waiting' => pht('Waiting'), + 'active' => pht('Active'), + 'completed' => pht('Completed'), + ); + } + + public function buildSavedQueryFromBuiltin($query_key) { + $query = $this->newSavedQuery(); + $query->setQueryKey($query_key); + + switch ($query_key) { + case 'initiated': + $viewer = $this->requireViewer(); + return $query->setParameter('initiators', array($viewer->getPHID())); + case 'all': + return $query; + case 'waiting': + return $query + ->setParameter( + 'statuses', + HarbormasterBuildStatus::getWaitingStatusConstants()); + case 'active': + return $query + ->setParameter( + 'statuses', + HarbormasterBuildStatus::getActiveStatusConstants()); + case 'completed': + return $query + ->setParameter( + 'statuses', + HarbormasterBuildStatus::getCompletedStatusConstants()); + } + + return parent::buildSavedQueryFromBuiltin($query_key); + } + + protected function renderResultList( + array $builds, + PhabricatorSavedQuery $query, + array $handles) { + assert_instances_of($builds, 'HarbormasterBuild'); + + $viewer = $this->requireViewer(); + + $buildables = mpull($builds, 'getBuildable'); + $object_phids = mpull($buildables, 'getBuildablePHID'); + $initiator_phids = mpull($builds, 'getInitiatorPHID'); + $phids = array_mergev(array($initiator_phids, $object_phids)); + $phids = array_unique(array_filter($phids)); + + $handles = $viewer->loadHandles($phids); + + $list = new PHUIObjectItemListView(); + foreach ($builds as $build) { + $id = $build->getID(); + $initiator = $handles[$build->getInitiatorPHID()]; + $buildable_object = $handles[$build->getBuildable()->getBuildablePHID()]; + + $item = id(new PHUIObjectItemView()) + ->setViewer($viewer) + ->setObject($build) + ->setObjectName(pht('Build %d', $build->getID())) + ->setHeader($build->getName()) + ->setHref($build->getURI()) + ->setEpoch($build->getDateCreated()) + ->addAttribute($buildable_object->getName()); + + if ($initiator) { + $item->addHandleIcon($initiator, $initiator->getName()); + } + + $status = $build->getBuildStatus(); + + $status_icon = HarbormasterBuildStatus::getBuildStatusIcon($status); + $status_color = HarbormasterBuildStatus::getBuildStatusColor($status); + $status_label = HarbormasterBuildStatus::getBuildStatusName($status); + + $item->setStatusIcon("{$status_icon} {$status_color}", $status_label); + + $list->addItem($item); + } + + $result = new PhabricatorApplicationSearchResultView(); + $result->setObjectList($list); + $result->setNoDataString(pht('No builds found.')); + + return $result; + } + +} diff --git a/src/applications/harbormaster/storage/HarbormasterBuildable.php b/src/applications/harbormaster/storage/HarbormasterBuildable.php index 7a7b32618c..5e55d38824 100644 --- a/src/applications/harbormaster/storage/HarbormasterBuildable.php +++ b/src/applications/harbormaster/storage/HarbormasterBuildable.php @@ -170,7 +170,7 @@ final class HarbormasterBuildable extends HarbormasterDAO ->setBuildablePHID($this->getPHID()) ->setBuildPlanPHID($plan->getPHID()) ->setBuildParameters($parameters) - ->setBuildStatus(HarbormasterBuild::STATUS_PENDING); + ->setBuildStatus(HarbormasterBuildStatus::STATUS_PENDING); if ($initiator_phid) { $build->setInitiatorPHID($initiator_phid); } diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuild.php b/src/applications/harbormaster/storage/build/HarbormasterBuild.php index 4d9278c7bf..9255b6ec4a 100644 --- a/src/applications/harbormaster/storage/build/HarbormasterBuild.php +++ b/src/applications/harbormaster/storage/build/HarbormasterBuild.php @@ -3,7 +3,8 @@ final class HarbormasterBuild extends HarbormasterDAO implements PhabricatorApplicationTransactionInterface, - PhabricatorPolicyInterface { + PhabricatorPolicyInterface, + PhabricatorConduitResultInterface { protected $buildablePHID; protected $buildPlanPHID; @@ -18,131 +19,9 @@ final class HarbormasterBuild extends HarbormasterDAO private $buildTargets = self::ATTACHABLE; private $unprocessedCommands = self::ATTACHABLE; - /** - * Not currently being built. - */ - const STATUS_INACTIVE = 'inactive'; - - /** - * Pending pick up by the Harbormaster daemon. - */ - const STATUS_PENDING = 'pending'; - - /** - * Current building the buildable. - */ - const STATUS_BUILDING = 'building'; - - /** - * The build has passed. - */ - const STATUS_PASSED = 'passed'; - - /** - * The build has failed. - */ - const STATUS_FAILED = 'failed'; - - /** - * The build has aborted. - */ - const STATUS_ABORTED = 'aborted'; - - /** - * The build encountered an unexpected error. - */ - const STATUS_ERROR = 'error'; - - /** - * The build has been paused. - */ - const STATUS_PAUSED = 'paused'; - - /** - * The build has been deadlocked. - */ - const STATUS_DEADLOCKED = 'deadlocked'; - - - /** - * Get a human readable name for a build status constant. - * - * @param const Build status constant. - * @return string Human-readable name. - */ - public static function getBuildStatusName($status) { - switch ($status) { - case self::STATUS_INACTIVE: - return pht('Inactive'); - case self::STATUS_PENDING: - return pht('Pending'); - case self::STATUS_BUILDING: - return pht('Building'); - case self::STATUS_PASSED: - return pht('Passed'); - case self::STATUS_FAILED: - return pht('Failed'); - case self::STATUS_ABORTED: - return pht('Aborted'); - case self::STATUS_ERROR: - return pht('Unexpected Error'); - case self::STATUS_PAUSED: - return pht('Paused'); - case self::STATUS_DEADLOCKED: - return pht('Deadlocked'); - default: - return pht('Unknown'); - } - } - - public static function getBuildStatusIcon($status) { - switch ($status) { - case self::STATUS_INACTIVE: - case self::STATUS_PENDING: - return PHUIStatusItemView::ICON_OPEN; - case self::STATUS_BUILDING: - return PHUIStatusItemView::ICON_RIGHT; - case self::STATUS_PASSED: - return PHUIStatusItemView::ICON_ACCEPT; - case self::STATUS_FAILED: - return PHUIStatusItemView::ICON_REJECT; - case self::STATUS_ABORTED: - return PHUIStatusItemView::ICON_MINUS; - case self::STATUS_ERROR: - return PHUIStatusItemView::ICON_MINUS; - case self::STATUS_PAUSED: - return PHUIStatusItemView::ICON_MINUS; - case self::STATUS_DEADLOCKED: - return PHUIStatusItemView::ICON_WARNING; - default: - return PHUIStatusItemView::ICON_QUESTION; - } - } - - public static function getBuildStatusColor($status) { - switch ($status) { - case self::STATUS_INACTIVE: - return 'dark'; - case self::STATUS_PENDING: - case self::STATUS_BUILDING: - return 'blue'; - case self::STATUS_PASSED: - return 'green'; - case self::STATUS_FAILED: - case self::STATUS_ABORTED: - case self::STATUS_ERROR: - case self::STATUS_DEADLOCKED: - return 'red'; - case self::STATUS_PAUSED: - return 'dark'; - default: - return 'bluegrey'; - } - } - public static function initializeNewBuild(PhabricatorUser $actor) { return id(new HarbormasterBuild()) - ->setBuildStatus(self::STATUS_INACTIVE) + ->setBuildStatus(HarbormasterBuildStatus::STATUS_INACTIVE) ->setBuildGeneration(0); } @@ -181,6 +60,9 @@ final class HarbormasterBuild extends HarbormasterDAO 'columns' => array('buildablePHID', 'planAutoKey'), 'unique' => true, ), + 'key_initiator' => array( + 'columns' => array('initiatorPHID'), + ), ), ) + parent::getConfiguration(); } @@ -226,8 +108,9 @@ final class HarbormasterBuild extends HarbormasterDAO } public function isBuilding() { - return $this->getBuildStatus() === self::STATUS_PENDING || - $this->getBuildStatus() === self::STATUS_BUILDING; + return + $this->getBuildStatus() === HarbormasterBuildStatus::STATUS_PENDING || + $this->getBuildStatus() === HarbormasterBuildStatus::STATUS_BUILDING; } public function isAutobuild() { @@ -290,20 +173,13 @@ final class HarbormasterBuild extends HarbormasterDAO } public function isComplete() { - switch ($this->getBuildStatus()) { - case self::STATUS_PASSED: - case self::STATUS_FAILED: - case self::STATUS_ABORTED: - case self::STATUS_ERROR: - case self::STATUS_PAUSED: - return true; - } - - return false; + return in_array( + $this->getBuildStatus(), + HarbormasterBuildStatus::getCompletedStatusConstants()); } public function isPaused() { - return ($this->getBuildStatus() == self::STATUS_PAUSED); + return ($this->getBuildStatus() == HarbormasterBuildStatus::STATUS_PAUSED); } public function getURI() { @@ -522,4 +398,49 @@ final class HarbormasterBuild extends HarbormasterDAO return pht('A build inherits policies from its buildable.'); } + +/* -( PhabricatorConduitResultInterface )---------------------------------- */ + + + public function getFieldSpecificationsForConduit() { + return array( + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('buildablePHID') + ->setType('phid') + ->setDescription(pht('PHID of the object this build is building.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('buildPlanPHID') + ->setType('phid') + ->setDescription(pht('PHID of the build plan being run.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('buildStatus') + ->setType('map') + ->setDescription(pht('The current status of this build.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('initiatorPHID') + ->setType('phid') + ->setDescription(pht('The person (or thing) that started this build.')), + ); + } + + public function getFieldValuesForConduit() { + $status = $this->getBuildStatus(); + return array( + 'buildablePHID' => $this->getBuildablePHID(), + 'buildPlanPHID' => $this->getBuildPlanPHID(), + 'buildStatus' => array( + 'value' => $status, + 'name' => HarbormasterBuildStatus::getBuildStatusName($status), + ), + 'initiatorPHID' => nonempty($this->getInitiatorPHID(), null), + ); + } + + public function getConduitSearchAttachments() { + return array( + id(new HarbormasterQueryBuildsSearchEngineAttachment()) + ->setAttachmentKey('querybuilds'), + ); + } + } diff --git a/src/applications/harbormaster/typeahead/HarbormasterBuildInitiatorDatasource.php b/src/applications/harbormaster/typeahead/HarbormasterBuildInitiatorDatasource.php new file mode 100644 index 0000000000..c66073e25e --- /dev/null +++ b/src/applications/harbormaster/typeahead/HarbormasterBuildInitiatorDatasource.php @@ -0,0 +1,22 @@ +buildResults(); + return $this->filterResultsAgainstTokens($results); + } + + public function renderTokens(array $values) { + return $this->renderTokensFromResults($this->buildResults(), $values); + } + + private function buildResults() { + $results = array(); + + $status_map = HarbormasterBuildStatus::getBuildStatusMap(); + foreach ($status_map as $value => $name) { + $result = id(new PhabricatorTypeaheadResult()) + ->setIcon(HarbormasterBuildStatus::getBuildStatusIcon($value)) + ->setColor(HarbormasterBuildStatus::getBuildStatusColor($value)) + ->setPHID($value) + ->setName($name) + ->addAttribute(pht('Status')); + + $results[$value] = $result; + } + + return $results; + } + +} diff --git a/src/applications/herald/adapter/HeraldAdapter.php b/src/applications/herald/adapter/HeraldAdapter.php index e08118a369..ac227819f9 100644 --- a/src/applications/herald/adapter/HeraldAdapter.php +++ b/src/applications/herald/adapter/HeraldAdapter.php @@ -189,7 +189,6 @@ abstract class HeraldAdapter extends Phobject { abstract public function getAdapterApplicationClass(); abstract public function getObject(); - /** * Return a new characteristic object for this adapter. * @@ -217,6 +216,23 @@ abstract class HeraldAdapter extends Phobject { return false; } + public function isTestAdapterForObject($object) { + return false; + } + + public function canCreateTestAdapterForObject($object) { + return $this->isTestAdapterForObject($object); + } + + public function newTestAdapter($object) { + return id(clone $this) + ->setObject($object); + } + + public function getAdapterTestDescription() { + return null; + } + public function explainValidTriggerObjects() { return pht('This adapter can not trigger on objects.'); } @@ -752,7 +768,10 @@ abstract class HeraldAdapter extends Phobject { ); } - abstract protected function initializeNewAdapter(); + protected function initializeNewAdapter() { + $this->setObject($this->newObject()); + return $this; + } /** * Does this adapter's event fire only once? diff --git a/src/applications/herald/controller/HeraldTestConsoleController.php b/src/applications/herald/controller/HeraldTestConsoleController.php index 1dd6034bb5..fae92864f1 100644 --- a/src/applications/herald/controller/HeraldTestConsoleController.php +++ b/src/applications/herald/controller/HeraldTestConsoleController.php @@ -2,14 +2,76 @@ final class HeraldTestConsoleController extends HeraldController { + private $testObject; + private $testAdapter; + + public function setTestObject($test_object) { + $this->testObject = $test_object; + return $this; + } + + public function getTestObject() { + return $this->testObject; + } + + public function setTestAdapter(HeraldAdapter $test_adapter) { + $this->testAdapter = $test_adapter; + return $this; + } + + public function getTestAdapter() { + return $this->testAdapter; + } + public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); - $object_name = trim($request->getStr('object_name')); + + $response = $this->loadTestObject($request); + if ($response) { + return $response; + } + + $response = $this->loadAdapter($request); + if ($response) { + return $response; + } + + $object = $this->getTestObject(); + $adapter = $this->getTestAdapter(); + + $adapter->setIsNewObject(false); + + $rules = id(new HeraldRuleQuery()) + ->setViewer($viewer) + ->withContentTypes(array($adapter->getAdapterContentType())) + ->withDisabled(false) + ->needConditionsAndActions(true) + ->needAppliedToPHIDs(array($object->getPHID())) + ->needValidateAuthors(true) + ->execute(); + + $engine = id(new HeraldEngine()) + ->setDryRun(true); + + $effects = $engine->applyRules($rules, $adapter); + $engine->applyEffects($effects, $adapter, $rules); + + $xscript = $engine->getTranscript(); + + return id(new AphrontRedirectResponse()) + ->setURI('/herald/transcript/'.$xscript->getID().'/'); + } + + private function loadTestObject(AphrontRequest $request) { + $viewer = $this->getViewer(); $e_name = true; + $v_name = null; $errors = array(); + if ($request->isFormPost()) { - if (!$object_name) { + $v_name = trim($request->getStr('object_name')); + if (!$v_name) { $e_name = pht('Required'); $errors[] = pht('An object name is required.'); } @@ -17,66 +79,18 @@ final class HeraldTestConsoleController extends HeraldController { if (!$errors) { $object = id(new PhabricatorObjectQuery()) ->setViewer($viewer) - ->withNames(array($object_name)) + ->withNames(array($v_name)) ->executeOne(); if (!$object) { $e_name = pht('Invalid'); $errors[] = pht('No object exists with that name.'); } + } - if (!$errors) { - - // TODO: Let the adapters claim objects instead. - - if ($object instanceof DifferentialRevision) { - $adapter = HeraldDifferentialRevisionAdapter::newLegacyAdapter( - $object, - $object->loadActiveDiff()); - } else if ($object instanceof PhabricatorRepositoryCommit) { - $adapter = id(new HeraldCommitAdapter()) - ->setCommit($object); - } else if ($object instanceof ManiphestTask) { - $adapter = id(new HeraldManiphestTaskAdapter()) - ->setTask($object); - } else if ($object instanceof PholioMock) { - $adapter = id(new HeraldPholioMockAdapter()) - ->setMock($object); - } else if ($object instanceof PhrictionDocument) { - $adapter = id(new PhrictionDocumentHeraldAdapter()) - ->setDocument($object); - } else if ($object instanceof PonderQuestion) { - $adapter = id(new HeraldPonderQuestionAdapter()) - ->setQuestion($object); - } else if ($object instanceof PhabricatorMetaMTAMail) { - $adapter = id(new PhabricatorMailOutboundMailHeraldAdapter()) - ->setObject($object); - } else { - throw new Exception(pht('Can not build adapter for object!')); - } - - $adapter->setIsNewObject(false); - - $rules = id(new HeraldRuleQuery()) - ->setViewer($viewer) - ->withContentTypes(array($adapter->getAdapterContentType())) - ->withDisabled(false) - ->needConditionsAndActions(true) - ->needAppliedToPHIDs(array($object->getPHID())) - ->needValidateAuthors(true) - ->execute(); - - $engine = id(new HeraldEngine()) - ->setDryRun(true); - - $effects = $engine->applyRules($rules, $adapter); - $engine->applyEffects($effects, $adapter, $rules); - - $xscript = $engine->getTranscript(); - - return id(new AphrontRedirectResponse()) - ->setURI('/herald/transcript/'.$xscript->getID().'/'); - } + if (!$errors) { + $this->setTestObject($object); + return null; } } @@ -92,11 +106,89 @@ final class HeraldTestConsoleController extends HeraldController { ->setLabel(pht('Object Name')) ->setName('object_name') ->setError($e_name) - ->setValue($object_name)) + ->setValue($v_name)) ->appendChild( id(new AphrontFormSubmitControl()) - ->setValue(pht('Test Rules'))); + ->setValue(pht('Continue'))); + return $this->buildTestConsoleResponse($form, $errors); + } + + private function loadAdapter(AphrontRequest $request) { + $viewer = $this->getViewer(); + $object = $this->getTestObject(); + + $adapter_key = $request->getStr('adapter'); + + $adapters = HeraldAdapter::getAllAdapters(); + + $can_select = array(); + $display_adapters = array(); + foreach ($adapters as $key => $adapter) { + if (!$adapter->isTestAdapterForObject($object)) { + continue; + } + + if (!$adapter->isAvailableToUser($viewer)) { + continue; + } + + $display_adapters[$key] = $adapter; + + if ($adapter->canCreateTestAdapterForObject($object)) { + $can_select[$key] = $adapter; + } + } + + if ($request->isFormPost() && $adapter_key) { + if (isset($can_select[$adapter_key])) { + $adapter = $can_select[$adapter_key]->newTestAdapter($object); + $this->setTestAdapter($adapter); + return null; + } + } + + $form = id(new AphrontFormView()) + ->addHiddenInput('object_name', $request->getStr('object_name')) + ->setViewer($viewer); + + $cancel_uri = $this->getApplicationURI(); + + if (!$display_adapters) { + $form + ->appendRemarkupInstructions( + pht('//There are no available Herald events for this object.//')) + ->appendControl( + id(new AphrontFormSubmitControl()) + ->addCancelButton($cancel_uri)); + } else { + $adapter_control = id(new AphrontFormRadioButtonControl()) + ->setLabel(pht('Event')) + ->setName('adapter') + ->setValue(head_key($can_select)); + + foreach ($display_adapters as $adapter_key => $adapter) { + $is_disabled = empty($can_select[$adapter_key]); + + $adapter_control->addButton( + $adapter_key, + $adapter->getAdapterContentName(), + $adapter->getAdapterTestDescription(), + null, + $is_disabled); + } + + $form + ->appendControl($adapter_control) + ->appendControl( + id(new AphrontFormSubmitControl()) + ->setValue(pht('Run Test'))); + } + + return $this->buildTestConsoleResponse($form, array()); + } + + private function buildTestConsoleResponse($form, array $errors) { $box = id(new PHUIObjectBoxView()) ->setFormErrors($errors) ->setForm($form); @@ -118,11 +210,7 @@ final class HeraldTestConsoleController extends HeraldController { return $this->newPage() ->setTitle($title) ->setCrumbs($crumbs) - ->appendChild( - array( - $view, - )); - + ->appendChild($view); } } diff --git a/src/applications/herald/query/HeraldRuleQuery.php b/src/applications/herald/query/HeraldRuleQuery.php index f4bd3fe734..e2c5f6928b 100644 --- a/src/applications/herald/query/HeraldRuleQuery.php +++ b/src/applications/herald/query/HeraldRuleQuery.php @@ -8,6 +8,7 @@ final class HeraldRuleQuery extends PhabricatorCursorPagedPolicyAwareQuery { private $ruleTypes; private $contentTypes; private $disabled; + private $datasourceQuery; private $triggerObjectPHIDs; private $needConditionsAndActions; @@ -49,6 +50,11 @@ final class HeraldRuleQuery extends PhabricatorCursorPagedPolicyAwareQuery { return $this; } + public function withDatasourceQuery($query) { + $this->datasourceQuery = $query; + return $this; + } + public function withTriggerObjectPHIDs(array $phids) { $this->triggerObjectPHIDs = $phids; return $this; @@ -219,6 +225,13 @@ final class HeraldRuleQuery extends PhabricatorCursorPagedPolicyAwareQuery { (int)$this->disabled); } + if ($this->datasourceQuery) { + $where[] = qsprintf( + $conn_r, + 'rule.name LIKE %>', + $this->datasourceQuery); + } + if ($this->triggerObjectPHIDs) { $where[] = qsprintf( $conn_r, diff --git a/src/applications/herald/storage/HeraldRule.php b/src/applications/herald/storage/HeraldRule.php index 9902e952ef..19f9b95507 100644 --- a/src/applications/herald/storage/HeraldRule.php +++ b/src/applications/herald/storage/HeraldRule.php @@ -34,7 +34,7 @@ final class HeraldRule extends HeraldDAO return array( self::CONFIG_AUX_PHID => true, self::CONFIG_COLUMN_SCHEMA => array( - 'name' => 'text255', + 'name' => 'sort255', 'contentType' => 'text255', 'mustMatchAll' => 'bool', 'configVersion' => 'uint32', @@ -47,6 +47,9 @@ final class HeraldRule extends HeraldDAO 'repetitionPolicy' => 'uint32?', ), self::CONFIG_KEY_SCHEMA => array( + 'key_name' => array( + 'columns' => array('name(128)'), + ), 'key_author' => array( 'columns' => array('authorPHID'), ), diff --git a/src/applications/herald/typeahead/HeraldRuleDatasource.php b/src/applications/herald/typeahead/HeraldRuleDatasource.php new file mode 100644 index 0000000000..c2db05c9ea --- /dev/null +++ b/src/applications/herald/typeahead/HeraldRuleDatasource.php @@ -0,0 +1,49 @@ +getViewer(); + $raw_query = $this->getRawQuery(); + + $rules = id(new HeraldRuleQuery()) + ->setViewer($viewer) + ->withDatasourceQuery($raw_query) + ->execute(); + + $handles = id(new PhabricatorHandleQuery()) + ->setViewer($viewer) + ->withPHIDs(mpull($rules, 'getPHID')) + ->execute(); + + $results = array(); + foreach ($rules as $rule) { + $handle = $handles[$rule->getPHID()]; + + $result = id(new PhabricatorTypeaheadResult()) + ->setName($handle->getFullName()) + ->setPHID($handle->getPHID()); + + if ($rule->getIsDisabled()) { + $result->setClosed(pht('Archived')); + } + + $results[] = $result; + } + + return $results; + } +} diff --git a/src/applications/home/controller/PhabricatorHomeMainController.php b/src/applications/home/controller/PhabricatorHomeMainController.php index c5387a5fb7..7bc8b06573 100644 --- a/src/applications/home/controller/PhabricatorHomeMainController.php +++ b/src/applications/home/controller/PhabricatorHomeMainController.php @@ -53,6 +53,7 @@ final class PhabricatorHomeMainController extends PhabricatorHomeController { return $this->newPage() ->setTitle('Phabricator') + ->addClass('phabricator-home') ->appendChild($content); } diff --git a/src/applications/maniphest/controller/ManiphestExportController.php b/src/applications/maniphest/controller/ManiphestExportController.php index 88e6849afb..1201c50478 100644 --- a/src/applications/maniphest/controller/ManiphestExportController.php +++ b/src/applications/maniphest/controller/ManiphestExportController.php @@ -31,7 +31,9 @@ final class ManiphestExportController extends ManiphestController { '

%s

'. '
'. '

'. - 'http://www.phpexcel.net/'. + ''. + 'https://github.com/PHPOffice/PHPExcel'. + ''. '

'. '
'. '

%s

', diff --git a/src/applications/maniphest/herald/HeraldManiphestTaskAdapter.php b/src/applications/maniphest/herald/HeraldManiphestTaskAdapter.php index d504a71214..8503839e3e 100644 --- a/src/applications/maniphest/herald/HeraldManiphestTaskAdapter.php +++ b/src/applications/maniphest/herald/HeraldManiphestTaskAdapter.php @@ -16,6 +16,15 @@ final class HeraldManiphestTaskAdapter extends HeraldAdapter { return pht('React to tasks being created or updated.'); } + public function isTestAdapterForObject($object) { + return ($object instanceof ManiphestTask); + } + + public function getAdapterTestDescription() { + return pht( + 'Test rules which run when a task is created or updated.'); + } + protected function initializeNewAdapter() { $this->task = $this->newObject(); } @@ -46,10 +55,16 @@ final class HeraldManiphestTaskAdapter extends HeraldAdapter { $this->task = $task; return $this; } + public function getTask() { return $this->task; } + public function setObject($object) { + $this->task = $object; + return $this; + } + public function getObject() { return $this->task; } diff --git a/src/applications/maniphest/view/ManiphestTaskListView.php b/src/applications/maniphest/view/ManiphestTaskListView.php index 5c01a2d9fb..6f714d0c5c 100644 --- a/src/applications/maniphest/view/ManiphestTaskListView.php +++ b/src/applications/maniphest/view/ManiphestTaskListView.php @@ -96,7 +96,7 @@ final class ManiphestTaskListView extends ManiphestView { $project_handles = array_select_keys( $handles, - $task->getProjectPHIDs()); + array_reverse($task->getProjectPHIDs())); $item->addAttribute( id(new PHUIHandleTagListView()) diff --git a/src/applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php b/src/applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php index 43c0a63300..6ca797833e 100644 --- a/src/applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php +++ b/src/applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php @@ -21,6 +21,17 @@ final class PhabricatorMailOutboundMailHeraldAdapter return new PhabricatorMetaMTAMail(); } + public function isTestAdapterForObject($object) { + return ($object instanceof PhabricatorMetaMTAMail); + } + + public function getAdapterTestDescription() { + return pht( + 'Test rules which run when outbound mail is being prepared for '. + 'delivery.'); + } + + public function getObject() { return $this->mail; } diff --git a/src/applications/pholio/herald/HeraldPholioMockAdapter.php b/src/applications/pholio/herald/HeraldPholioMockAdapter.php index 57fa74c3a1..5520a60cdc 100644 --- a/src/applications/pholio/herald/HeraldPholioMockAdapter.php +++ b/src/applications/pholio/herald/HeraldPholioMockAdapter.php @@ -20,6 +20,20 @@ final class HeraldPholioMockAdapter extends HeraldAdapter { return new PholioMock(); } + public function isTestAdapterForObject($object) { + return ($object instanceof PholioMock); + } + + public function getAdapterTestDescription() { + return pht( + 'Test rules which run when a mock is created or updated.'); + } + + public function setObject($object) { + $this->mock = $object; + return $this; + } + public function getObject() { return $this->mock; } @@ -28,6 +42,7 @@ final class HeraldPholioMockAdapter extends HeraldAdapter { $this->mock = $mock; return $this; } + public function getMock() { return $this->mock; } diff --git a/src/applications/phriction/herald/PhrictionDocumentHeraldAdapter.php b/src/applications/phriction/herald/PhrictionDocumentHeraldAdapter.php index 575d4b3dd8..74de97d5fc 100644 --- a/src/applications/phriction/herald/PhrictionDocumentHeraldAdapter.php +++ b/src/applications/phriction/herald/PhrictionDocumentHeraldAdapter.php @@ -20,6 +20,20 @@ final class PhrictionDocumentHeraldAdapter extends HeraldAdapter { return new PhrictionDocument(); } + public function isTestAdapterForObject($object) { + return ($object instanceof PhrictionDocument); + } + + public function getAdapterTestDescription() { + return pht( + 'Test rules which run when a wiki document is created or updated.'); + } + + public function setObject($object) { + $this->document = $object; + return $this; + } + public function getObject() { return $this->document; } diff --git a/src/applications/policy/engineextension/PhabricatorPolicySearchEngineExtension.php b/src/applications/policy/engineextension/PhabricatorPolicySearchEngineExtension.php index a2bb2271f4..2b603bc95f 100644 --- a/src/applications/policy/engineextension/PhabricatorPolicySearchEngineExtension.php +++ b/src/applications/policy/engineextension/PhabricatorPolicySearchEngineExtension.php @@ -30,7 +30,7 @@ final class PhabricatorPolicySearchEngineExtension ); } - public function getFieldValuesForConduit($object) { + public function getFieldValuesForConduit($object, $data) { $capabilities = $object->getCapabilities(); $map = array(); diff --git a/src/applications/ponder/herald/HeraldPonderQuestionAdapter.php b/src/applications/ponder/herald/HeraldPonderQuestionAdapter.php index 3a21457c3e..f3f47681bb 100644 --- a/src/applications/ponder/herald/HeraldPonderQuestionAdapter.php +++ b/src/applications/ponder/herald/HeraldPonderQuestionAdapter.php @@ -20,6 +20,21 @@ final class HeraldPonderQuestionAdapter extends HeraldAdapter { $this->question = $this->newObject(); } + + public function isTestAdapterForObject($object) { + return ($object instanceof PonderQuestion); + } + + public function getAdapterTestDescription() { + return pht( + 'Test rules which run when a question is created or updated.'); + } + + public function setObject($object) { + $this->question = $object; + return $this; + } + public function supportsApplicationEmail() { return true; } diff --git a/src/applications/project/view/ProjectBoardTaskCard.php b/src/applications/project/view/ProjectBoardTaskCard.php index a7ee93dd76..ba5213a623 100644 --- a/src/applications/project/view/ProjectBoardTaskCard.php +++ b/src/applications/project/view/ProjectBoardTaskCard.php @@ -128,6 +128,7 @@ final class ProjectBoardTaskCard extends Phobject { } if ($project_handles) { + $project_handles = array_reverse($project_handles); $tag_list = id(new PHUIHandleTagListView()) ->setSlim(true) ->setHandles($project_handles); diff --git a/src/applications/search/controller/PhabricatorApplicationSearchController.php b/src/applications/search/controller/PhabricatorApplicationSearchController.php index b9ed90e80e..164e9aabbc 100644 --- a/src/applications/search/controller/PhabricatorApplicationSearchController.php +++ b/src/applications/search/controller/PhabricatorApplicationSearchController.php @@ -193,10 +193,12 @@ final class PhabricatorApplicationSearchController } $header = id(new PHUIHeaderView()) - ->setHeader($title); + ->setHeader($title) + ->setProfileHeader(true); $box = id(new PHUIObjectBoxView()) - ->setHeader($header); + ->setHeader($header) + ->addClass('application-search-results'); if ($run_query || $named_query) { $box->setShowHide( @@ -281,9 +283,8 @@ final class PhabricatorApplicationSearchController if ($pager->willShowPagingControls()) { $pager_box = id(new PHUIBoxView()) - ->addPadding(PHUI::PADDING_MEDIUM) - ->addMargin(PHUI::MARGIN_LARGE) - ->setBorder(true) + ->setColor(PHUIBoxView::GREY) + ->addClass('application-search-pager') ->appendChild($pager); $body[] = $pager_box; } @@ -308,7 +309,8 @@ final class PhabricatorApplicationSearchController } $crumbs = $parent - ->buildApplicationCrumbs(); + ->buildApplicationCrumbs() + ->setBorder(true); if ($more_crumbs) { $query_uri = $engine->getQueryResultsPageURI($saved_query->getQueryKey()); @@ -321,12 +323,15 @@ final class PhabricatorApplicationSearchController $crumbs->addTextCrumb($title); } + require_celerity_resource('application-search-view-css'); + return $this->newPage() ->setApplicationMenu($this->buildApplicationMenu()) ->setTitle(pht('Query: %s', $title)) ->setCrumbs($crumbs) ->setNavigation($nav) - ->appendChild($body); + ->appendChild($body) + ->addClass('application-search-view'); } private function processEditRequest() { @@ -403,19 +408,28 @@ final class PhabricatorApplicationSearchController $crumbs = $parent ->buildApplicationCrumbs() - ->addTextCrumb(pht('Saved Queries'), $engine->getQueryManagementURI()); + ->addTextCrumb(pht('Saved Queries'), $engine->getQueryManagementURI()) + ->setBorder(true); $nav->selectFilter('query/edit'); + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Saved Queries')) + ->setProfileHeader(true); + $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Saved Queries')) - ->setObjectList($list); + ->setHeader($header) + ->setObjectList($list) + ->addClass('application-search-results'); + + require_celerity_resource('application-search-view-css'); return $this->newPage() ->setApplicationMenu($this->buildApplicationMenu()) ->setTitle(pht('Saved Queries')) ->setCrumbs($crumbs) ->setNavigation($nav) + ->addClass('application-search-view') ->appendChild($box); } diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php index 3d6b4d9cff..d2ce3ad946 100644 --- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php +++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php @@ -1138,6 +1138,11 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { if ($objects) { $field_extensions = $this->getConduitFieldExtensions(); + $extension_data = array(); + foreach ($field_extensions as $key => $extension) { + $extension_data[$key] = $extension->loadExtensionConduitData($objects); + } + $attachment_data = array(); foreach ($attachments as $key => $attachment) { $attachment_data[$key] = $attachment->loadAttachmentData( @@ -1148,7 +1153,8 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { foreach ($objects as $object) { $field_map = $this->getObjectWireFieldsForConduit( $object, - $field_extensions); + $field_extensions, + $extension_data); $attachment_map = array(); foreach ($attachments as $key => $attachment) { @@ -1312,11 +1318,13 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { protected function getObjectWireFieldsForConduit( $object, - array $field_extensions) { + array $field_extensions, + array $extension_data) { $fields = array(); - foreach ($field_extensions as $extension) { - $fields += $extension->getFieldValuesForConduit($object); + foreach ($field_extensions as $key => $extension) { + $data = idx($extension_data, $key, array()); + $fields += $extension->getFieldValuesForConduit($object, $data); } return $fields; diff --git a/src/applications/search/engineextension/PhabricatorLiskSearchEngineExtension.php b/src/applications/search/engineextension/PhabricatorLiskSearchEngineExtension.php index c502ad4cc6..c4d338bc15 100644 --- a/src/applications/search/engineextension/PhabricatorLiskSearchEngineExtension.php +++ b/src/applications/search/engineextension/PhabricatorLiskSearchEngineExtension.php @@ -44,7 +44,7 @@ final class PhabricatorLiskSearchEngineExtension ); } - public function getFieldValuesForConduit($object) { + public function getFieldValuesForConduit($object, $data) { return array( 'dateCreated' => (int)$object->getDateCreated(), 'dateModified' => (int)$object->getDateModified(), diff --git a/src/applications/search/engineextension/PhabricatorSearchEngineExtension.php b/src/applications/search/engineextension/PhabricatorSearchEngineExtension.php index 39af0a3fdd..f93c09cf8f 100644 --- a/src/applications/search/engineextension/PhabricatorSearchEngineExtension.php +++ b/src/applications/search/engineextension/PhabricatorSearchEngineExtension.php @@ -56,7 +56,11 @@ abstract class PhabricatorSearchEngineExtension extends Phobject { return array(); } - public function getFieldValuesForConduit($object) { + public function loadExtensionConduitData(array $objects) { + return null; + } + + public function getFieldValuesForConduit($object, $data) { return array(); } diff --git a/src/applications/search/profilepanel/PhabricatorMotivatorProfilePanel.php b/src/applications/search/profilepanel/PhabricatorMotivatorProfilePanel.php index 34c767e99c..327290deba 100644 --- a/src/applications/search/profilepanel/PhabricatorMotivatorProfilePanel.php +++ b/src/applications/search/profilepanel/PhabricatorMotivatorProfilePanel.php @@ -140,7 +140,10 @@ final class PhabricatorMotivatorProfilePanel 'to launch their attacks. Watch out!'), pht('Cats prefer vanilla ice cream.'), pht('Taco cat spelled backwards is taco cat.'), - ); + pht( + 'Cats will often bring you their prey because they feel sorry '. + 'for your inability to hunt.'), + ); } private function selectFact(array $facts) { diff --git a/src/applications/spaces/engineextension/PhabricatorSpacesSearchEngineExtension.php b/src/applications/spaces/engineextension/PhabricatorSpacesSearchEngineExtension.php index 7bf92370e4..829b7b2b93 100644 --- a/src/applications/spaces/engineextension/PhabricatorSpacesSearchEngineExtension.php +++ b/src/applications/spaces/engineextension/PhabricatorSpacesSearchEngineExtension.php @@ -63,7 +63,7 @@ final class PhabricatorSpacesSearchEngineExtension ); } - public function getFieldValuesForConduit($object) { + public function getFieldValuesForConduit($object, $data) { return array( 'spacePHID' => $object->getSpacePHID(), ); diff --git a/src/docs/user/userguide/diffusion_api.diviner b/src/docs/user/userguide/diffusion_api.diviner index 8ddd6d4bf6..f57d5007aa 100644 --- a/src/docs/user/userguide/diffusion_api.diviner +++ b/src/docs/user/userguide/diffusion_api.diviner @@ -14,7 +14,7 @@ For an introduction to Conduit, see @{article:Conduit API Overview}. In general, you'll use these API methods: - - `diffusion.repository.edit`: Create and edit repositorie. + - `diffusion.repository.edit`: Create and edit repositories. - `diffusion.uri.edit`: Create and edit repository URIs to configure observation, mirroring, and cloning. diff --git a/src/infrastructure/customfield/engineextension/PhabricatorCustomFieldSearchEngineExtension.php b/src/infrastructure/customfield/engineextension/PhabricatorCustomFieldSearchEngineExtension.php index 246ac713fc..309e3d2949 100644 --- a/src/infrastructure/customfield/engineextension/PhabricatorCustomFieldSearchEngineExtension.php +++ b/src/infrastructure/customfield/engineextension/PhabricatorCustomFieldSearchEngineExtension.php @@ -80,17 +80,42 @@ final class PhabricatorCustomFieldSearchEngineExtension return $map; } - public function getFieldValuesForConduit($object) { - // TODO: This is currently very inefficient. We should bulk-load these - // field values instead. + public function loadExtensionConduitData(array $objects) { + $viewer = $this->getViewer(); - $fields = PhabricatorCustomField::getObjectFields( - $object, - PhabricatorCustomField::ROLE_CONDUIT); + $field_map = array(); + foreach ($objects as $object) { + $object_phid = $object->getPHID(); - $fields - ->setViewer($this->getViewer()) - ->readFieldsFromStorage($object); + $fields = PhabricatorCustomField::getObjectFields( + $object, + PhabricatorCustomField::ROLE_CONDUIT); + + $fields + ->setViewer($viewer) + ->readFieldsFromObject($object); + + $field_map[$object_phid] = $fields; + } + + $all_fields = array(); + foreach ($field_map as $field_list) { + foreach ($field_list->getFields() as $field) { + $all_fields[] = $field; + } + } + + id(new PhabricatorCustomFieldStorageQuery()) + ->addFields($all_fields) + ->execute(); + + return array( + 'fields' => $field_map, + ); + } + + public function getFieldValuesForConduit($object, $data) { + $fields = $data['fields'][$object->getPHID()]; $map = array(); foreach ($fields->getFields() as $field) { diff --git a/src/infrastructure/customfield/field/PhabricatorCustomField.php b/src/infrastructure/customfield/field/PhabricatorCustomField.php index cb561fca58..7791700a14 100644 --- a/src/infrastructure/customfield/field/PhabricatorCustomField.php +++ b/src/infrastructure/customfield/field/PhabricatorCustomField.php @@ -112,20 +112,13 @@ abstract class PhabricatorCustomField extends Phobject { $object, array $options = array()) { - PhutilTypeSpec::checkMap( - $options, - array( - 'withDisabled' => 'optional bool', - )); - - $field_objects = id(new PhutilSymbolLoader()) + $field_objects = id(new PhutilClassMapQuery()) ->setAncestorClass($base_class) - ->loadObjects(); + ->execute(); $fields = array(); - $from_map = array(); foreach ($field_objects as $field_object) { - $current_class = get_class($field_object); + $field_object = clone $field_object; foreach ($field_object->createFields($object) as $field) { $key = $field->getFieldKey(); if (isset($fields[$key])) { @@ -133,11 +126,10 @@ abstract class PhabricatorCustomField extends Phobject { pht( "Both '%s' and '%s' define a custom field with ". "field key '%s'. Field keys must be unique.", - $from_map[$key], - $current_class, + get_class($fields[$key]), + get_class($field), $key)); } - $from_map[$key] = $current_class; $fields[$key] = $field; } } @@ -152,11 +144,13 @@ abstract class PhabricatorCustomField extends Phobject { if (empty($options['withDisabled'])) { foreach ($fields as $key => $field) { - $config = idx($spec, $key, array()) + array( - 'disabled' => $field->shouldDisableByDefault(), - ); + if (isset($spec[$key]['disabled'])) { + $is_disabled = $spec[$key]['disabled']; + } else { + $is_disabled = $field->shouldDisableByDefault(); + } - if (!empty($config['disabled'])) { + if ($is_disabled) { if ($field->canDisableField()) { unset($fields[$key]); } diff --git a/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php b/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php index 9fefa52ad4..cb1e56711d 100644 --- a/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php +++ b/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php @@ -29,6 +29,19 @@ final class PhabricatorCustomFieldList extends Phobject { return $this; } + public function readFieldsFromObject( + PhabricatorCustomFieldInterface $object) { + + $fields = $this->getFields(); + + foreach ($fields as $field) { + $field + ->setObject($object) + ->readValueFromObject($object); + } + + return $this; + } /** * Read stored values for all fields which support storage. @@ -39,48 +52,12 @@ final class PhabricatorCustomFieldList extends Phobject { public function readFieldsFromStorage( PhabricatorCustomFieldInterface $object) { - foreach ($this->fields as $field) { - $field->setObject($object); - $field->readValueFromObject($object); - } + $this->readFieldsFromObject($object); - $keys = array(); - foreach ($this->fields as $field) { - if ($field->shouldEnableForRole(PhabricatorCustomField::ROLE_STORAGE)) { - $keys[$field->getFieldIndex()] = $field; - } - } - - if (!$keys) { - return $this; - } - - // NOTE: We assume all fields share the same storage. This isn't guaranteed - // to be true, but always is for now. - - $table = head($keys)->newStorageObject(); - - $objects = array(); - if ($object->getPHID()) { - $objects = $table->loadAllWhere( - 'objectPHID = %s AND fieldIndex IN (%Ls)', - $object->getPHID(), - array_keys($keys)); - $objects = mpull($objects, null, 'getFieldIndex'); - } - - foreach ($keys as $key => $field) { - $storage = idx($objects, $key); - if ($storage) { - $field->setValueFromStorage($storage->getFieldValue()); - $field->didSetValueFromStorage(); - } else if ($object->getPHID()) { - // NOTE: We set this only if the object exists. Otherwise, we allow the - // field to retain any default value it may have. - $field->setValueFromStorage(null); - $field->didSetValueFromStorage(); - } - } + $fields = $this->getFields(); + id(new PhabricatorCustomFieldStorageQuery()) + ->addFields($fields) + ->execute(); return $this; } diff --git a/src/infrastructure/customfield/query/PhabricatorCustomFieldStorageQuery.php b/src/infrastructure/customfield/query/PhabricatorCustomFieldStorageQuery.php new file mode 100644 index 0000000000..9188117dcc --- /dev/null +++ b/src/infrastructure/customfield/query/PhabricatorCustomFieldStorageQuery.php @@ -0,0 +1,84 @@ +addField($field); + } + + return $this; + } + + public function addField(PhabricatorCustomField $field) { + $role_storage = PhabricatorCustomField::ROLE_STORAGE; + + if (!$field->shouldEnableForRole($role_storage)) { + return $this; + } + + $storage = $field->newStorageObject(); + $source_key = $storage->getStorageSourceKey(); + + $this->fieldMap[$source_key][] = $field; + + if (empty($this->storageSources[$source_key])) { + $this->storageSources[$source_key] = $storage; + } + + return $this; + } + + public function execute() { + foreach ($this->storageSources as $source_key => $storage) { + $fields = idx($this->fieldMap, $source_key, array()); + $this->loadFieldsFromStorage($storage, $fields); + } + } + + private function loadFieldsFromStorage($storage, array $fields) { + // Only try to load fields which have a persisted object. + $loadable = array(); + foreach ($fields as $key => $field) { + $object = $field->getObject(); + $phid = $object->getPHID(); + if (!$phid) { + continue; + } + + $loadable[$key] = $field; + } + + if ($loadable) { + $data = $storage->loadStorageSourceData($loadable); + } else { + $data = array(); + } + + foreach ($fields as $key => $field) { + if (array_key_exists($key, $data)) { + $value = $data[$key]; + $field->setValueFromStorage($value); + $field->didSetValueFromStorage(); + } else if (isset($loadable[$key])) { + // NOTE: We set this only if the object exists. Otherwise, we allow + // the field to retain any default value it may have. + $field->setValueFromStorage(null); + $field->didSetValueFromStorage(); + } + } + } + +} diff --git a/src/infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php b/src/infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php index 9817b2e591..0f60d77602 100644 --- a/src/infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php +++ b/src/infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php @@ -23,4 +23,68 @@ abstract class PhabricatorCustomFieldStorage ) + parent::getConfiguration(); } + + /** + * Get a key which uniquely identifies this storage source. + * + * When loading custom fields, fields using sources with the same source key + * are loaded in bulk. + * + * @return string Source identifier. + */ + final public function getStorageSourceKey() { + return $this->getApplicationName().'/'.$this->getTableName(); + } + + + /** + * Load stored data for custom fields. + * + * Given a map of fields, return a map with any stored data for those fields. + * The keys in the result should correspond to the keys in the input. The + * fields in the list may belong to different objects. + * + * @param map Map of fields. + * @return map Map of available field data. + */ + final public function loadStorageSourceData(array $fields) { + $map = array(); + $indexes = array(); + $object_phids = array(); + + foreach ($fields as $key => $field) { + $index = $field->getFieldIndex(); + $object_phid = $field->getObject()->getPHID(); + + $map[$index][$object_phid] = $key; + $indexes[$index] = $index; + $object_phids[$object_phid] = $object_phid; + } + + if (!$indexes) { + return array(); + } + + $conn = $this->establishConnection('r'); + $rows = queryfx_all( + $conn, + 'SELECT objectPHID, fieldIndex, fieldValue FROM %T + WHERE objectPHID IN (%Ls) AND fieldIndex IN (%Ls)', + $this->getTableName(), + $object_phids, + $indexes); + + $result = array(); + foreach ($rows as $row) { + $index = $row['fieldIndex']; + $object_phid = $row['objectPHID']; + $value = $row['fieldValue']; + + $key = $map[$index][$object_phid]; + $result[$key] = $value; + } + + return $result; + } + } diff --git a/src/view/control/AphrontCursorPagerView.php b/src/view/control/AphrontCursorPagerView.php index 3574c07fa9..4608158481 100644 --- a/src/view/control/AphrontCursorPagerView.php +++ b/src/view/control/AphrontCursorPagerView.php @@ -148,7 +148,7 @@ final class AphrontCursorPagerView extends AphrontView { ->setHref($first_uri) ->setIcon($icon) ->addClass('mml') - ->setColor(PHUIButtonView::SIMPLE) + ->setColor(PHUIButtonView::GREY) ->setText(pht('First')); } @@ -161,7 +161,7 @@ final class AphrontCursorPagerView extends AphrontView { ->setHref($prev_uri) ->setIcon($icon) ->addClass('mml') - ->setColor(PHUIButtonView::SIMPLE) + ->setColor(PHUIButtonView::GREY) ->setText(pht('Prev')); } @@ -174,7 +174,7 @@ final class AphrontCursorPagerView extends AphrontView { ->setHref($next_uri) ->setIcon($icon, false) ->addClass('mml') - ->setColor(PHUIButtonView::SIMPLE) + ->setColor(PHUIButtonView::GREY) ->setText(pht('Next')); } diff --git a/src/view/layout/AphrontSideNavFilterView.php b/src/view/layout/AphrontSideNavFilterView.php index d59bf5ccac..eb9f4f72f8 100644 --- a/src/view/layout/AphrontSideNavFilterView.php +++ b/src/view/layout/AphrontSideNavFilterView.php @@ -193,24 +193,18 @@ final class AphrontSideNavFilterView extends AphrontView { } } - require_celerity_resource('phabricator-side-menu-view-css'); + require_celerity_resource('phui-basic-nav-view-css'); return $this->renderFlexNav(); } private function renderFlexNav() { require_celerity_resource('phabricator-nav-view-css'); + require_celerity_resource('phui-profile-menu-css'); $nav_classes = array(); $nav_classes[] = 'phabricator-nav'; - if ($this->getIsProfileMenu()) { - require_celerity_resource('phui-profile-menu-css'); - // No class, we're going to put it on the shell instead. - } else { - $nav_classes[] = 'phabricator-basic-nav'; - } - $nav_id = null; $drag_id = null; $content_id = celerity_generate_unique_node_id(); @@ -260,6 +254,8 @@ final class AphrontSideNavFilterView extends AphrontView { if ($this->flexible) { if (!$this->collapsed) { $nav_classes[] = 'has-drag-nav'; + } else { + $nav_classes[] = 'has-closed-nav'; } Javelin::initBehavior( @@ -284,16 +280,6 @@ final class AphrontSideNavFilterView extends AphrontView { $nav_classes = array_merge($nav_classes, $this->classes); - $footer = $this->footer; - - if ($this->getIsProfileMenu()) { - $internal_footer = $footer; - $external_footer = null; - } else { - $internal_footer = null; - $external_footer = $footer; - } - $menu = phutil_tag( 'div', array( @@ -312,26 +298,28 @@ final class AphrontSideNavFilterView extends AphrontView { array( $crumbs, $this->renderChildren(), - $internal_footer, + $this->footer, )), )); + $classes = array(); + $classes[] = 'phui-navigation-shell'; + if ($this->getIsProfileMenu()) { - $shell = phutil_tag( - 'div', - array( - 'class' => 'phui-navigation-shell phui-profile-menu', - ), - array( - $menu, - )); + $classes[] = 'phui-profile-menu'; } else { - $shell = array( - $menu, - $external_footer, - ); + $classes[] = 'phui-basic-nav'; } + $shell = phutil_tag( + 'div', + array( + 'class' => implode(' ', $classes), + ), + array( + $menu, + )); + return $shell; } diff --git a/src/view/phui/PHUIObjectBoxView.php b/src/view/phui/PHUIObjectBoxView.php index 5c6cef49a3..1e1d9898ec 100644 --- a/src/view/phui/PHUIObjectBoxView.php +++ b/src/view/phui/PHUIObjectBoxView.php @@ -204,6 +204,7 @@ final class PHUIObjectBoxView extends AphrontTagView { ->addSigil('reveal-content') ->setID($hide_action_id) ->setStyle($hide_style) + ->setIcon('fa-search') ->setHref($this->showHideHref) ->setMetaData( array( @@ -216,6 +217,7 @@ final class PHUIObjectBoxView extends AphrontTagView { ->setTag('a') ->addSigil('reveal-content') ->setStyle($show_style) + ->setIcon('fa-search') ->setHref('#') ->setID($show_action_id) ->setMetaData( diff --git a/src/view/phui/PHUIPagerView.php b/src/view/phui/PHUIPagerView.php index 1e14522ca7..232d4ab747 100644 --- a/src/view/phui/PHUIPagerView.php +++ b/src/view/phui/PHUIPagerView.php @@ -204,7 +204,7 @@ final class PHUIPagerView extends AphrontView { $rendered_links[] = id(new PHUIButtonView()) ->setTag('a') ->setHref($link) - ->setColor(PHUIButtonView::SIMPLE) + ->setColor(PHUIButtonView::GREY) ->addClass('mml') ->addClass($class) ->setText($label); diff --git a/src/view/phui/calendar/PHUICalendarDayView.php b/src/view/phui/calendar/PHUICalendarDayView.php index a2cab191a5..5a2a8ee178 100644 --- a/src/view/phui/calendar/PHUICalendarDayView.php +++ b/src/view/phui/calendar/PHUICalendarDayView.php @@ -191,6 +191,10 @@ final class PHUICalendarDayView extends AphrontView { ->addColumn($table_box, 'thirds phui-day-view-column') ->setFluidLayout(true); + $layout = id(new PHUIBoxView()) + ->appendChild($layout) + ->addClass('phui-calendar-box'); + return $layout; } diff --git a/src/view/phui/calendar/PHUICalendarMonthView.php b/src/view/phui/calendar/PHUICalendarMonthView.php index 61fd8b1e99..8272eb007f 100644 --- a/src/view/phui/calendar/PHUICalendarMonthView.php +++ b/src/view/phui/calendar/PHUICalendarMonthView.php @@ -183,7 +183,8 @@ final class PHUICalendarMonthView extends AphrontView { $box = id(new PHUIObjectBoxView()) ->setHeader($this->renderCalendarHeader($this->getDateTime())) ->appendChild($table) - ->setFormErrors($warnings); + ->setFormErrors($warnings) + ->addClass('phui-calendar-box'); if ($this->error) { $box->setInfoView($this->error); diff --git a/webroot/rsrc/css/aphront/phabricator-nav-view.css b/webroot/rsrc/css/aphront/phabricator-nav-view.css index 7dbb56da07..760c2cd372 100644 --- a/webroot/rsrc/css/aphront/phabricator-nav-view.css +++ b/webroot/rsrc/css/aphront/phabricator-nav-view.css @@ -6,8 +6,10 @@ cursor: col-resize; } -.phabricator-nav-local, -.phabricator-nav-drag { +.device-desktop .has-closed-nav div.phabricator-nav-local, +.device-desktop .has-closed-nav div.phabricator-nav-drag, +.device .phui-navigation-shell div.phabricator-nav-local, +.device .phui-navigation-shell div.phabricator-nav-drag { display: none; } @@ -16,27 +18,25 @@ display: block; } -.device .phabricator-side-menu-home .phabricator-nav-local { +.device-phone .phabricator-side-menu-home .phabricator-nav-local { display: block; } -.device-desktop .phabricator-side-menu-home .phabricator-nav-content, -.device-tablet .phabricator-side-menu-home .phabricator-nav-content { - margin-left: 205px; +/* Home Sidenav */ +.phui-basic-nav.phui-navigation-shell + .phabricator-side-menu-home .phabricator-nav-local { + padding-top: 16px; + padding-right: 0; + background-color: transparent; + width: 205px; } -.phabricator-nav-local { - width: 205px; - position: absolute; - left: 0; - white-space: nowrap; - overflow-x: hidden; - overflow-y: auto; - margin-top: 8px; -} - -.phabricator-side-menu-home .phabricator-nav-local { - margin-top: 16px; +.device-phone .phui-basic-nav.phui-navigation-shell + .phabricator-side-menu-home .phabricator-nav-local { + padding-top: 0; + padding-right: 0; + background-color: transparent; + width: auto; } .phabricator-nav-drag { @@ -68,8 +68,10 @@ margin-left: 212px; } -.device-desktop .has-local-nav .phabricator-nav-content { - margin-left: 205px; +.device-desktop .phui-navigation-shell .has-drag-nav .phabricator-nav-local { + width: 205px; + padding: 0; + background: transparent; } .device-phone .phabricator-side-menu-home .phabricator-nav-content { diff --git a/webroot/rsrc/css/application/project/project-view.css b/webroot/rsrc/css/application/project/project-view.css index 0af3ef3490..2c0fd7d6d8 100644 --- a/webroot/rsrc/css/application/project/project-view.css +++ b/webroot/rsrc/css/application/project/project-view.css @@ -91,7 +91,3 @@ .profile-no-badges { padding: 24px 0; } - -.project-view-home .phabricator-remarkup .remarkup-code-block pre { - white-space: pre-wrap; -} diff --git a/webroot/rsrc/css/application/search/application-search-view.css b/webroot/rsrc/css/application/search/application-search-view.css new file mode 100644 index 0000000000..24ee18c847 --- /dev/null +++ b/webroot/rsrc/css/application/search/application-search-view.css @@ -0,0 +1,50 @@ +/** + * @provides application-search-view-css + */ + +.application-search-view { + background-color: #fff; +} + +.application-search-view .application-search-results.phui-object-box { + margin: 0; + padding: 0 16px 24px; +} + +.application-search-view .application-search-results .phui-profile-header { + padding: 16px 8px; + border-bottom: 1px solid {$thinblueborder}; +} + +.application-search-results + .phui-profile-header.phui-header-shell .phui-header-header { + font-size: 20px; +} + +.device-phone.application-search-view .application-search-results + .phui-profile-header { + padding: 12px 0; +} + +.device-phone .application-search-results + .phui-profile-header.phui-header-shell .phui-header-header { + font-size: 16px; +} + +.device-phone.application-search-view + .application-search-results.phui-object-box { + padding: 0 12px; + } + +.application-search-view .phui-box-border { + border: none; +} + +.application-search-pager { + margin: 0 16px 16px 16px; + padding: 8px; +} + +.device-phone .application-search-pager { + margin: 12px; +} diff --git a/webroot/rsrc/css/core/remarkup.css b/webroot/rsrc/css/core/remarkup.css index 034c5a83c6..42d1d86597 100644 --- a/webroot/rsrc/css/core/remarkup.css +++ b/webroot/rsrc/css/core/remarkup.css @@ -51,6 +51,7 @@ overflow: auto; padding: 12px; border-radius: 3px; + white-space: pre-wrap; } .phabricator-remarkup pre.remarkup-counterexample { diff --git a/webroot/rsrc/css/layout/phabricator-side-menu-view.css b/webroot/rsrc/css/layout/phabricator-side-menu-view.css deleted file mode 100644 index b64fe5fbb5..0000000000 --- a/webroot/rsrc/css/layout/phabricator-side-menu-view.css +++ /dev/null @@ -1,59 +0,0 @@ -/** - * @provides phabricator-side-menu-view-css - */ - -.phabricator-basic-nav .phabricator-side-menu .phui-list-item-view { - display: block; - white-space: nowrap; - text-decoration: none; - font-size: 13px; - -webkit-font-smoothing: antialiased; -} - -.phabricator-basic-nav .phabricator-side-menu .phui-list-item-href { - display: block; - padding: 6px 8px 6px 24px; - color: {$darkbluetext}; - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; -} - -.phabricator-basic-nav .phabricator-side-menu .phui-list-item-icon { - margin-left: -12px; - text-align: center; - width: 24px; -} - -.phabricator-basic-nav .phabricator-side-menu .phui-list-item-selected { - background-color: rgba({$alphablack},.05); - border-left: 4px solid {$sky}; - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; - font-weight: bold; -} - -.device-desktop .phabricator-basic-nav .phabricator-side-menu - .phui-list-item-selected - a.phui-list-item-href:hover { - background-color: rgba({$alphablack},.05); -} - -.phabricator-basic-nav .phabricator-side-menu .phui-list-item-selected - .phui-list-item-href { - padding-left: 20px; -} - -.phabricator-basic-nav .phabricator-side-menu .phui-list-item-type-label { - padding: 6px 8px 4px 12px; - color: {$darkbluetext}; - text-transform: uppercase; - font-size: 12px; - font-weight: bold; - border-style: solid; -} - -.device-desktop .phabricator-basic-nav .phabricator-side-menu - a.phui-list-item-href:hover { - text-decoration: none; - background-color: rgba({$alphablack},.07); -} diff --git a/webroot/rsrc/css/phui/calendar/phui-calendar.css b/webroot/rsrc/css/phui/calendar/phui-calendar.css index 6c369b21df..ab59886fd8 100644 --- a/webroot/rsrc/css/phui/calendar/phui-calendar.css +++ b/webroot/rsrc/css/phui/calendar/phui-calendar.css @@ -8,6 +8,13 @@ background: rgba(255, 255, 255, 0.75); } +.application-search-view div.phui-calendar-box { + border-left: 1px solid {$thinblueborder}; + border-right: 1px solid {$thinblueborder}; + border-bottom: 1px solid {$lightblueborder}; + border-radius: 0; +} + .phui-calendar-list a { color: {$greytext}; } diff --git a/webroot/rsrc/css/phui/phui-basic-nav-view.css b/webroot/rsrc/css/phui/phui-basic-nav-view.css new file mode 100644 index 0000000000..30647e3ece --- /dev/null +++ b/webroot/rsrc/css/phui/phui-basic-nav-view.css @@ -0,0 +1,110 @@ +/** + * @provides phui-basic-nav-view-css + */ + +.device-desktop .phui-navigation-shell, +.phabricator-home.device .phui-navigation-shell { + display: table; + width: 100%; + height: calc(100vh - {$menu.main.height}); +} + +.device-desktop .phui-navigation-shell .phabricator-nav, +.phabricator-home.device .phui-navigation-shell .phabricator-nav { + display: table-row; +} + +.device-desktop .phui-navigation-shell .phabricator-nav-local, +.phabricator-home.device .phui-navigation-shell .phabricator-nav-local { + display: table-cell; + position: relative; + vertical-align: top; + width: {$menu.profile.width}; + max-width: {$menu.profile.width}; + margin-top: 0; + overflow: hidden; +} + +.phui-basic-nav.phui-navigation-shell .phabricator-nav-local { + width: 205px; + padding-top: 4px; + padding-right: 8px; +} + +.phui-basic-nav .phabricator-side-menu { + background-color: {$page.sidenav}; +} + +.phui-two-column-view .phui-basic-nav.phui-navigation-shell + .phabricator-nav-local { + width: {$menu.profile.width}; + max-width: {$menu.profile.width}; + padding-right: 0; + padding-top: 0; +} + +.phui-two-column-view .phui-basic-nav .phabricator-side-menu { + background-color: #fff; +} + +.phui-basic-nav .phabricator-side-menu { + background-color: {$page.sidenav}; +} + +.phui-basic-nav .phabricator-side-menu .phui-list-item-view { + display: block; + white-space: nowrap; + text-decoration: none; + font-size: 13px; + -webkit-font-smoothing: antialiased; +} + +.phui-basic-nav .phabricator-side-menu .phui-list-item-href { + display: block; + padding: 6px 8px 6px 24px; + color: {$darkbluetext}; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + overflow: hidden; + text-overflow: ellipsis +} + +.phui-basic-nav .phabricator-side-menu .phui-list-item-icon { + margin-left: -12px; + text-align: center; + width: 24px; +} + +.phui-basic-nav .phabricator-side-menu .phui-list-item-selected { + background-color: rgba({$alphablack},.05); + border-left: 4px solid {$sky}; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + font-weight: bold; +} + +.device-desktop .phui-basic-nav .phabricator-side-menu + .phui-list-item-selected + a.phui-list-item-href:hover { + background-color: rgba({$alphablack},.05); +} + +.phui-basic-nav .phabricator-side-menu .phui-list-item-selected + .phui-list-item-href { + padding-left: 20px; +} + +.phui-basic-nav .phabricator-side-menu .phui-list-item-type-label { + padding: 6px 8px 4px 12px; + color: {$darkbluetext}; + text-transform: uppercase; + font-size: 12px; + font-weight: bold; + border-style: solid; +} + +.device-desktop .phui-basic-nav .phabricator-side-menu + a.phui-list-item-href:hover { + text-decoration: none; + background-color: rgba({$alphablack},.07); +} diff --git a/webroot/rsrc/css/phui/phui-crumbs-view.css b/webroot/rsrc/css/phui/phui-crumbs-view.css index dd070647fb..486ed1d5d3 100644 --- a/webroot/rsrc/css/phui/phui-crumbs-view.css +++ b/webroot/rsrc/css/phui/phui-crumbs-view.css @@ -5,11 +5,12 @@ .phui-crumbs-view { overflow: hidden; vertical-align: top; - padding: 0 20px 0 28px; + padding: 0 12px 0 20px; /* TODO: Position this over the slider for Differential's file tree view. Remove this once that gets sorted out. */ position: relative; -webkit-font-smoothing: antialiased; + background-color: {$page.background}; } .phui-crumbs-view, diff --git a/webroot/rsrc/css/phui/phui-document-pro.css b/webroot/rsrc/css/phui/phui-document-pro.css index 1cedd133f9..1027d03a56 100644 --- a/webroot/rsrc/css/phui/phui-document-pro.css +++ b/webroot/rsrc/css/phui/phui-document-pro.css @@ -37,7 +37,7 @@ } .device-phone .phui-document-view.phui-document-view-pro { - padding: 0 8px; + padding: 0 12px; margin: 0 auto; } @@ -132,6 +132,11 @@ a.button.phui-document-toc { color: #000; } +.device-phone .phui-document-view.phui-document-view-pro .phui-header-tall + .phui-header-header { + font-size: 18px; + } + .device-phone .phui-document-view-pro .phui-header-subheader { display: block; padding: 8px 0 0 0; diff --git a/webroot/rsrc/css/phui/phui-header-view.css b/webroot/rsrc/css/phui/phui-header-view.css index 2336ec3696..e773668f0a 100644 --- a/webroot/rsrc/css/phui/phui-header-view.css +++ b/webroot/rsrc/css/phui/phui-header-view.css @@ -323,6 +323,7 @@ body .phui-header-shell.phui-bleed-header .phui-profile-header.phui-header-shell .phui-header-header { font-family: 'Aleo', {$fontfamily}; font-size: 24px; + color: #000; } .phui-profile-header .phui-header-col3 { diff --git a/webroot/rsrc/css/phui/phui-profile-menu.css b/webroot/rsrc/css/phui/phui-profile-menu.css index 41efc4cfb3..275bff1dde 100644 --- a/webroot/rsrc/css/phui/phui-profile-menu.css +++ b/webroot/rsrc/css/phui/phui-profile-menu.css @@ -2,26 +2,6 @@ * @provides phui-profile-menu-css */ -.device-desktop .phui-navigation-shell.phui-profile-menu { - display: table; - width: 100%; - height: calc(100vh - {$menu.main.height}); -} - -.device-desktop .phui-profile-menu .phabricator-nav { - display: table-row; -} - -.device-desktop .phui-profile-menu .phabricator-nav-local { - display: table-cell; - position: relative; - vertical-align: top; - width: {$menu.profile.width}; - max-width: {$menu.profile.width}; - margin-top: 0; - overflow: hidden; -} - .device-desktop .phui-profile-menu-collapsed .phabricator-nav-local { width: {$menu.profile.width.collapsed}; max-width: {$menu.profile.width.collapsed}; @@ -32,9 +12,12 @@ margin-left: 0; } +.phui-profile-menu .phui-basic-nav { + width: 205px; +} + .phui-profile-menu .phabricator-side-menu { background: #525867; - box-shadow: inset -2px 0 2px rgba({$alphablack}, 0.150); width: 240px; } diff --git a/webroot/rsrc/js/core/behavior-phabricator-nav.js b/webroot/rsrc/js/core/behavior-phabricator-nav.js index d148558b1e..ca7fc0d14a 100644 --- a/webroot/rsrc/js/core/behavior-phabricator-nav.js +++ b/webroot/rsrc/js/core/behavior-phabricator-nav.js @@ -108,6 +108,7 @@ JX.behavior('phabricator-nav', function(config) { collapsed = !collapsed; JX.DOM.alterClass(main, 'has-local-nav', !collapsed); JX.DOM.alterClass(main, 'has-drag-nav', !collapsed); + JX.DOM.alterClass(main, 'has-closed-nav', collapsed); resetdrag(); new JX.Request('/settings/adjust/', JX.bag) .setData({ key : 'nav-collapsed', value : (collapsed ? 1 : 0) })