mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-01 03:02:43 +01:00
(stable) Promote 2016 Week 32
This commit is contained in:
commit
435f756414
80 changed files with 1692 additions and 563 deletions
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"phabricator.uri": "https://secure.phabricator.com/",
|
||||
"load": ["src/"]
|
||||
"load": ["src/"],
|
||||
"history.immutable": false
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventHeraldAdapter extends HeraldAdapter {
|
||||
|
||||
private $object;
|
||||
|
||||
public function getAdapterApplicationClass() {
|
||||
return 'PhabricatorCalendarApplication';
|
||||
}
|
||||
|
||||
public function getAdapterContentDescription() {
|
||||
return pht('React to events being created or updated.');
|
||||
}
|
||||
|
||||
protected function newObject() {
|
||||
return new PhabricatorCalendarEvent();
|
||||
}
|
||||
|
||||
public function isTestAdapterForObject($object) {
|
||||
return ($object instanceof PhabricatorCalendarEvent);
|
||||
}
|
||||
|
||||
public function getAdapterTestDescription() {
|
||||
return pht(
|
||||
'Test rules which run when an event is created or updated.');
|
||||
}
|
||||
|
||||
public function setObject($object) {
|
||||
$this->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();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorCalendarEventHeraldField extends HeraldField {
|
||||
|
||||
public function supportsObject($object) {
|
||||
return ($object instanceof PhabricatorCalendarEvent);
|
||||
}
|
||||
|
||||
public function getFieldGroupKey() {
|
||||
return PhabricatorCalendarEventHeraldFieldGroup::FIELDGROUPKEY;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventHeraldFieldGroup
|
||||
extends HeraldFieldGroup {
|
||||
|
||||
const FIELDGROUPKEY = 'calendar.event';
|
||||
|
||||
public function getGroupLabel() {
|
||||
return pht('Event Fields');
|
||||
}
|
||||
|
||||
protected function getGroupOrder() {
|
||||
return 1000;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventNameHeraldField
|
||||
extends PhabricatorCalendarEventHeraldField {
|
||||
|
||||
const FIELDCONST = 'calendar.event.name';
|
||||
|
||||
public function getHeraldFieldName() {
|
||||
return pht('Name');
|
||||
}
|
||||
|
||||
public function getHeraldFieldValue($object) {
|
||||
return $object->getName();
|
||||
}
|
||||
|
||||
protected function getHeraldFieldStandardType() {
|
||||
return self::STANDARD_TEXT;
|
||||
}
|
||||
|
||||
}
|
|
@ -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(
|
||||
|
|
|
@ -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)',
|
||||
|
|
|
@ -25,7 +25,7 @@ final class ConduitResultSearchEngineExtension
|
|||
return $object->getFieldSpecificationsForConduit();
|
||||
}
|
||||
|
||||
public function getFieldValuesForConduit($object) {
|
||||
public function getFieldValuesForConduit($object, $data) {
|
||||
return $object->getFieldValuesForConduit();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@ final class PhabricatorHarbormasterApplication extends PhabricatorApplication {
|
|||
=> 'HarbormasterBuildableActionController',
|
||||
),
|
||||
'build/' => array(
|
||||
$this->getQueryRoutePattern() => 'HarbormasterBuildListController',
|
||||
'(?P<id>\d+)/' => 'HarbormasterBuildViewController',
|
||||
'(?P<action>pause|resume|restart|abort)/'.
|
||||
'(?P<id>\d+)/(?:(?P<via>[^/]+)/)?'
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterBuildSearchConduitAPIMethod
|
||||
extends PhabricatorSearchEngineAPIMethod {
|
||||
|
||||
public function getAPIMethodName() {
|
||||
return 'harbormaster.build.search';
|
||||
}
|
||||
|
||||
public function newSearchEngine() {
|
||||
return new HarbormasterBuildSearchEngine();
|
||||
}
|
||||
|
||||
public function getMethodSummary() {
|
||||
return pht('Find out information about builds.');
|
||||
}
|
||||
|
||||
}
|
|
@ -11,6 +11,14 @@ final class HarbormasterQueryBuildsConduitAPIMethod
|
|||
return pht('Query Harbormaster builds.');
|
||||
}
|
||||
|
||||
public function getMethodStatus() {
|
||||
return self::METHOD_STATUS_DEPRECATED;
|
||||
}
|
||||
|
||||
public function getMethodStatusDescription() {
|
||||
return pht('Use %s instead.', 'harbormaster.build.search');
|
||||
}
|
||||
|
||||
protected function defineParamTypes() {
|
||||
return array(
|
||||
'ids' => 'optional list<id>',
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterBuildStatus extends Phobject {
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
$map = self::getBuildStatusMap();
|
||||
return idx($map, $status, pht('Unknown ("%s")', $status));
|
||||
}
|
||||
|
||||
public static function getBuildStatusMap() {
|
||||
return array(
|
||||
self::STATUS_INACTIVE => 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,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterBuildListController extends HarbormasterController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
return id(new HarbormasterBuildSearchEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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'));
|
||||
|
|
|
@ -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'));
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterQueryBuildsSearchEngineAttachment
|
||||
extends PhabricatorSearchEngineAttachment {
|
||||
|
||||
public function getAttachmentName() {
|
||||
return pht('Harbormaster Query Builds');
|
||||
}
|
||||
|
||||
public function getAttachmentDescription() {
|
||||
return pht(
|
||||
'This attachment exists solely to provide compatibility with the '.
|
||||
'message format returned by an outdated API method. It will be '.
|
||||
'taken away at some point and you should not rely on these fields '.
|
||||
'being available.');
|
||||
}
|
||||
|
||||
public function getAttachmentForObject($object, $data, $spec) {
|
||||
$status_name = HarbormasterBuildStatus::getBuildStatusName(
|
||||
$object->getBuildStatus());
|
||||
return array(
|
||||
'uri' => PhabricatorEnv::getProductionURI($object->getURI()),
|
||||
'name' => $object->getName(),
|
||||
'buildStatusName' => $status_name,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterBuildSearchEngine
|
||||
extends PhabricatorApplicationSearchEngine {
|
||||
|
||||
public function getResultTypeDescription() {
|
||||
return pht('Harbormaster Builds');
|
||||
}
|
||||
|
||||
public function getApplicationClassName() {
|
||||
return 'PhabricatorHarbormasterApplication';
|
||||
}
|
||||
|
||||
public function newQuery() {
|
||||
return new HarbormasterBuildQuery();
|
||||
}
|
||||
|
||||
protected function buildCustomSearchFields() {
|
||||
return array(
|
||||
id(new PhabricatorSearchDatasourceField())
|
||||
->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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<string, wild>')
|
||||
->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'),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterBuildInitiatorDatasource
|
||||
extends PhabricatorTypeaheadCompositeDatasource {
|
||||
|
||||
public function getBrowseTitle() {
|
||||
return pht('Browse Build Initiators');
|
||||
}
|
||||
|
||||
public function getPlaceholderText() {
|
||||
return pht('Type the name of a user, application or Herald rule...');
|
||||
}
|
||||
|
||||
public function getComponentDatasources() {
|
||||
return array(
|
||||
new PhabricatorApplicationDatasource(),
|
||||
new PhabricatorPeopleUserFunctionDatasource(),
|
||||
new HeraldRuleDatasource(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterBuildStatusDatasource
|
||||
extends PhabricatorTypeaheadDatasource {
|
||||
|
||||
public function getBrowseTitle() {
|
||||
return pht('Choose Build Statuses');
|
||||
}
|
||||
|
||||
public function getPlaceholderText() {
|
||||
return pht('Type a build status name...');
|
||||
}
|
||||
|
||||
public function getDatasourceApplicationClass() {
|
||||
return 'PhabricatorHarbormasterApplication';
|
||||
}
|
||||
|
||||
public function loadResults() {
|
||||
$results = $this->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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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?
|
||||
|
|
|
@ -2,58 +2,42 @@
|
|||
|
||||
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'));
|
||||
|
||||
$e_name = true;
|
||||
$errors = array();
|
||||
if ($request->isFormPost()) {
|
||||
if (!$object_name) {
|
||||
$e_name = pht('Required');
|
||||
$errors[] = pht('An object name is required.');
|
||||
$response = $this->loadTestObject($request);
|
||||
if ($response) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
if (!$errors) {
|
||||
$object = id(new PhabricatorObjectQuery())
|
||||
->setViewer($viewer)
|
||||
->withNames(array($object_name))
|
||||
->executeOne();
|
||||
|
||||
if (!$object) {
|
||||
$e_name = pht('Invalid');
|
||||
$errors[] = pht('No object exists with that name.');
|
||||
$response = $this->loadAdapter($request);
|
||||
if ($response) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
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!'));
|
||||
}
|
||||
$object = $this->getTestObject();
|
||||
$adapter = $this->getTestAdapter();
|
||||
|
||||
$adapter->setIsNewObject(false);
|
||||
|
||||
|
@ -77,6 +61,36 @@ final class HeraldTestConsoleController extends HeraldController {
|
|||
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()) {
|
||||
$v_name = trim($request->getStr('object_name'));
|
||||
if (!$v_name) {
|
||||
$e_name = pht('Required');
|
||||
$errors[] = pht('An object name is required.');
|
||||
}
|
||||
|
||||
if (!$errors) {
|
||||
$object = id(new PhabricatorObjectQuery())
|
||||
->setViewer($viewer)
|
||||
->withNames(array($v_name))
|
||||
->executeOne();
|
||||
|
||||
if (!$object) {
|
||||
$e_name = pht('Invalid');
|
||||
$errors[] = pht('No object exists with that name.');
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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'),
|
||||
),
|
||||
|
|
49
src/applications/herald/typeahead/HeraldRuleDatasource.php
Normal file
49
src/applications/herald/typeahead/HeraldRuleDatasource.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
final class HeraldRuleDatasource
|
||||
extends PhabricatorTypeaheadDatasource {
|
||||
|
||||
public function getPlaceholderText() {
|
||||
return pht('Type a Herald rule name...');
|
||||
}
|
||||
|
||||
public function getBrowseTitle() {
|
||||
return pht('Browse Herald Rules');
|
||||
}
|
||||
|
||||
public function getDatasourceApplicationClass() {
|
||||
return 'PhabricatorHeraldApplication';
|
||||
}
|
||||
|
||||
public function loadResults() {
|
||||
$viewer = $this->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;
|
||||
}
|
||||
}
|
|
@ -53,6 +53,7 @@ final class PhabricatorHomeMainController extends PhabricatorHomeController {
|
|||
|
||||
return $this->newPage()
|
||||
->setTitle('Phabricator')
|
||||
->addClass('phabricator-home')
|
||||
->appendChild($content);
|
||||
|
||||
}
|
||||
|
|
|
@ -31,7 +31,9 @@ final class ManiphestExportController extends ManiphestController {
|
|||
'<p>%s</p>'.
|
||||
'<br />'.
|
||||
'<p>'.
|
||||
'<a href="http://www.phpexcel.net/">http://www.phpexcel.net/</a>'.
|
||||
'<a href="https://github.com/PHPOffice/PHPExcel">'.
|
||||
'https://github.com/PHPOffice/PHPExcel'.
|
||||
'</a>'.
|
||||
'</p>'.
|
||||
'<br />'.
|
||||
'<p>%s</p>',
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ final class PhabricatorPolicySearchEngineExtension
|
|||
);
|
||||
}
|
||||
|
||||
public function getFieldValuesForConduit($object) {
|
||||
public function getFieldValuesForConduit($object, $data) {
|
||||
$capabilities = $object->getCapabilities();
|
||||
|
||||
$map = array();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -140,6 +140,9 @@ 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.'),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ final class PhabricatorSpacesSearchEngineExtension
|
|||
);
|
||||
}
|
||||
|
||||
public function getFieldValuesForConduit($object) {
|
||||
public function getFieldValuesForConduit($object, $data) {
|
||||
return array(
|
||||
'spacePHID' => $object->getSpacePHID(),
|
||||
);
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
$field_map = array();
|
||||
foreach ($objects as $object) {
|
||||
$object_phid = $object->getPHID();
|
||||
|
||||
$fields = PhabricatorCustomField::getObjectFields(
|
||||
$object,
|
||||
PhabricatorCustomField::ROLE_CONDUIT);
|
||||
|
||||
$fields
|
||||
->setViewer($this->getViewer())
|
||||
->readFieldsFromStorage($object);
|
||||
->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) {
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Load custom field data from storage.
|
||||
*
|
||||
* This query loads the data directly into the field objects and does not
|
||||
* return it to the caller. It can bulk load data for any list of fields,
|
||||
* even if they have different objects or object types.
|
||||
*/
|
||||
final class PhabricatorCustomFieldStorageQuery extends Phobject {
|
||||
|
||||
private $fieldMap = array();
|
||||
private $storageSources = array();
|
||||
|
||||
public function addFields(array $fields) {
|
||||
assert_instances_of($fields, 'PhabricatorCustomField');
|
||||
|
||||
foreach ($fields as $field) {
|
||||
$this->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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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<string, PhabricatorCustomField> Map of fields.
|
||||
* @return map<String, PhabricatorCustomField> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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'));
|
||||
}
|
||||
|
||||
|
|
|
@ -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,25 +298,27 @@ final class AphrontSideNavFilterView extends AphrontView {
|
|||
array(
|
||||
$crumbs,
|
||||
$this->renderChildren(),
|
||||
$internal_footer,
|
||||
$this->footer,
|
||||
)),
|
||||
));
|
||||
|
||||
$classes = array();
|
||||
$classes[] = 'phui-navigation-shell';
|
||||
|
||||
if ($this->getIsProfileMenu()) {
|
||||
$classes[] = 'phui-profile-menu';
|
||||
} else {
|
||||
$classes[] = 'phui-basic-nav';
|
||||
}
|
||||
|
||||
$shell = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'phui-navigation-shell phui-profile-menu',
|
||||
'class' => implode(' ', $classes),
|
||||
),
|
||||
array(
|
||||
$menu,
|
||||
));
|
||||
} else {
|
||||
$shell = array(
|
||||
$menu,
|
||||
$external_footer,
|
||||
);
|
||||
}
|
||||
|
||||
return $shell;
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
.phabricator-nav-local {
|
||||
width: 205px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* Home Sidenav */
|
||||
.phui-basic-nav.phui-navigation-shell
|
||||
.phabricator-side-menu-home .phabricator-nav-local {
|
||||
margin-top: 16px;
|
||||
padding-top: 16px;
|
||||
padding-right: 0;
|
||||
background-color: transparent;
|
||||
width: 205px;
|
||||
}
|
||||
|
||||
.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 {
|
||||
|
|
|
@ -91,7 +91,3 @@
|
|||
.profile-no-badges {
|
||||
padding: 24px 0;
|
||||
}
|
||||
|
||||
.project-view-home .phabricator-remarkup .remarkup-code-block pre {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -51,6 +51,7 @@
|
|||
overflow: auto;
|
||||
padding: 12px;
|
||||
border-radius: 3px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.phabricator-remarkup pre.remarkup-counterexample {
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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};
|
||||
}
|
||||
|
|
110
webroot/rsrc/css/phui/phui-basic-nav-view.css
Normal file
110
webroot/rsrc/css/phui/phui-basic-nav-view.css
Normal file
|
@ -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);
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) })
|
||||
|
|
Loading…
Reference in a new issue