mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-10 14:51:06 +01:00
(stable) Promote 2016 Week 6
This commit is contained in:
commit
b3dd0fd860
121 changed files with 4039 additions and 1388 deletions
BIN
resources/builtin/image-200x200.png
Normal file
BIN
resources/builtin/image-200x200.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
resources/builtin/projects/fa-map-marker.png
Normal file
BIN
resources/builtin/projects/fa-map-marker.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3 KiB |
|
@ -7,8 +7,8 @@
|
||||||
*/
|
*/
|
||||||
return array(
|
return array(
|
||||||
'names' => array(
|
'names' => array(
|
||||||
'core.pkg.css' => '5e4df064',
|
'core.pkg.css' => 'e33b14a4',
|
||||||
'core.pkg.js' => 'a79eed25',
|
'core.pkg.js' => 'ef5e33db',
|
||||||
'darkconsole.pkg.js' => 'e7393ebb',
|
'darkconsole.pkg.js' => 'e7393ebb',
|
||||||
'differential.pkg.css' => '2de124c9',
|
'differential.pkg.css' => '2de124c9',
|
||||||
'differential.pkg.js' => '5c2ba922',
|
'differential.pkg.js' => '5c2ba922',
|
||||||
|
@ -36,7 +36,7 @@ return array(
|
||||||
'rsrc/css/application/base/notification-menu.css' => 'f31c0bde',
|
'rsrc/css/application/base/notification-menu.css' => 'f31c0bde',
|
||||||
'rsrc/css/application/base/phabricator-application-launch-view.css' => '95351601',
|
'rsrc/css/application/base/phabricator-application-launch-view.css' => '95351601',
|
||||||
'rsrc/css/application/base/phui-theme.css' => 'ab7b848c',
|
'rsrc/css/application/base/phui-theme.css' => 'ab7b848c',
|
||||||
'rsrc/css/application/base/standard-page-view.css' => '7b0d68d8',
|
'rsrc/css/application/base/standard-page-view.css' => 'c4467133',
|
||||||
'rsrc/css/application/chatlog/chatlog.css' => 'd295b020',
|
'rsrc/css/application/chatlog/chatlog.css' => 'd295b020',
|
||||||
'rsrc/css/application/conduit/conduit-api.css' => '7bc725c4',
|
'rsrc/css/application/conduit/conduit-api.css' => '7bc725c4',
|
||||||
'rsrc/css/application/config/config-options.css' => '0ede4c9b',
|
'rsrc/css/application/config/config-options.css' => '0ede4c9b',
|
||||||
|
@ -81,7 +81,7 @@ return array(
|
||||||
'rsrc/css/application/owners/owners-path-editor.css' => '2f00933b',
|
'rsrc/css/application/owners/owners-path-editor.css' => '2f00933b',
|
||||||
'rsrc/css/application/paste/paste.css' => 'a5157c48',
|
'rsrc/css/application/paste/paste.css' => 'a5157c48',
|
||||||
'rsrc/css/application/people/people-profile.css' => '2473d929',
|
'rsrc/css/application/people/people-profile.css' => '2473d929',
|
||||||
'rsrc/css/application/phame/phame.css' => '6d5b3682',
|
'rsrc/css/application/phame/phame.css' => '1dbbacf9',
|
||||||
'rsrc/css/application/pholio/pholio-edit.css' => '3ad9d1ee',
|
'rsrc/css/application/pholio/pholio-edit.css' => '3ad9d1ee',
|
||||||
'rsrc/css/application/pholio/pholio-inline-comments.css' => '8e545e49',
|
'rsrc/css/application/pholio/pholio-inline-comments.css' => '8e545e49',
|
||||||
'rsrc/css/application/pholio/pholio.css' => '95174bdd',
|
'rsrc/css/application/pholio/pholio.css' => '95174bdd',
|
||||||
|
@ -93,7 +93,8 @@ return array(
|
||||||
'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43',
|
'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43',
|
||||||
'rsrc/css/application/policy/policy.css' => '957ea14c',
|
'rsrc/css/application/policy/policy.css' => '957ea14c',
|
||||||
'rsrc/css/application/ponder/ponder-view.css' => '7b0df4da',
|
'rsrc/css/application/ponder/ponder-view.css' => '7b0df4da',
|
||||||
'rsrc/css/application/project/project-view.css' => '22f7ed0e',
|
'rsrc/css/application/project/project-card-view.css' => '9c3631e5',
|
||||||
|
'rsrc/css/application/project/project-view.css' => '4693497c',
|
||||||
'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733',
|
'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733',
|
||||||
'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5',
|
'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-differential-create-dialog.css' => '8d8b92cd',
|
||||||
|
@ -105,14 +106,13 @@ return array(
|
||||||
'rsrc/css/core/core.css' => '5b3563c8',
|
'rsrc/css/core/core.css' => '5b3563c8',
|
||||||
'rsrc/css/core/remarkup.css' => 'e1c8b32f',
|
'rsrc/css/core/remarkup.css' => 'e1c8b32f',
|
||||||
'rsrc/css/core/syntax.css' => '9fd11da8',
|
'rsrc/css/core/syntax.css' => '9fd11da8',
|
||||||
'rsrc/css/core/z-index.css' => 'a36a45da',
|
'rsrc/css/core/z-index.css' => '5c7025bf',
|
||||||
'rsrc/css/diviner/diviner-shared.css' => 'aa3656aa',
|
'rsrc/css/diviner/diviner-shared.css' => 'aa3656aa',
|
||||||
'rsrc/css/font/font-aleo.css' => '8bdb2835',
|
'rsrc/css/font/font-aleo.css' => '8bdb2835',
|
||||||
'rsrc/css/font/font-awesome.css' => 'c43323c5',
|
'rsrc/css/font/font-awesome.css' => 'c43323c5',
|
||||||
'rsrc/css/font/font-lato.css' => 'c7ccd872',
|
'rsrc/css/font/font-lato.css' => 'c7ccd872',
|
||||||
'rsrc/css/font/phui-font-icon-base.css' => 'ecbbb4c2',
|
'rsrc/css/font/phui-font-icon-base.css' => 'ecbbb4c2',
|
||||||
'rsrc/css/layout/phabricator-filetree-view.css' => 'fccf9f82',
|
'rsrc/css/layout/phabricator-filetree-view.css' => 'fccf9f82',
|
||||||
'rsrc/css/layout/phabricator-hovercard-view.css' => '1239cd52',
|
|
||||||
'rsrc/css/layout/phabricator-side-menu-view.css' => '3a3d9f41',
|
'rsrc/css/layout/phabricator-side-menu-view.css' => '3a3d9f41',
|
||||||
'rsrc/css/layout/phabricator-source-code-view.css' => 'cbeef983',
|
'rsrc/css/layout/phabricator-source-code-view.css' => 'cbeef983',
|
||||||
'rsrc/css/phui/calendar/phui-calendar-day.css' => 'd1cf6f93',
|
'rsrc/css/phui/calendar/phui-calendar-day.css' => 'd1cf6f93',
|
||||||
|
@ -125,15 +125,17 @@ return array(
|
||||||
'rsrc/css/phui/phui-big-info-view.css' => 'bd903741',
|
'rsrc/css/phui/phui-big-info-view.css' => 'bd903741',
|
||||||
'rsrc/css/phui/phui-box.css' => '6e8ac7fd',
|
'rsrc/css/phui/phui-box.css' => '6e8ac7fd',
|
||||||
'rsrc/css/phui/phui-button.css' => 'd6ac72db',
|
'rsrc/css/phui/phui-button.css' => 'd6ac72db',
|
||||||
|
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
|
||||||
'rsrc/css/phui/phui-crumbs-view.css' => '414406b5',
|
'rsrc/css/phui/phui-crumbs-view.css' => '414406b5',
|
||||||
'rsrc/css/phui/phui-document-pro.css' => '8799acf7',
|
'rsrc/css/phui/phui-document-pro.css' => '8799acf7',
|
||||||
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
|
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
|
||||||
'rsrc/css/phui/phui-document.css' => '9c71d2bf',
|
'rsrc/css/phui/phui-document.css' => '9c71d2bf',
|
||||||
'rsrc/css/phui/phui-feed-story.css' => 'b7b26d23',
|
'rsrc/css/phui/phui-feed-story.css' => '04aec08f',
|
||||||
'rsrc/css/phui/phui-fontkit.css' => '9cda225e',
|
'rsrc/css/phui/phui-fontkit.css' => '9cda225e',
|
||||||
'rsrc/css/phui/phui-form-view.css' => '4a1a0f5e',
|
'rsrc/css/phui/phui-form-view.css' => '4a1a0f5e',
|
||||||
'rsrc/css/phui/phui-form.css' => '0b98e572',
|
'rsrc/css/phui/phui-form.css' => '0b98e572',
|
||||||
'rsrc/css/phui/phui-header-view.css' => 'd53cc835',
|
'rsrc/css/phui/phui-header-view.css' => 'd53cc835',
|
||||||
|
'rsrc/css/phui/phui-hovercard.css' => 'de1a2119',
|
||||||
'rsrc/css/phui/phui-icon-set-selector.css' => '1ab67aad',
|
'rsrc/css/phui/phui-icon-set-selector.css' => '1ab67aad',
|
||||||
'rsrc/css/phui/phui-icon.css' => '3f33ab57',
|
'rsrc/css/phui/phui-icon.css' => '3f33ab57',
|
||||||
'rsrc/css/phui/phui-image-mask.css' => '5a8b09c8',
|
'rsrc/css/phui/phui-image-mask.css' => '5a8b09c8',
|
||||||
|
@ -141,7 +143,7 @@ return array(
|
||||||
'rsrc/css/phui/phui-info-view.css' => '6d7c3509',
|
'rsrc/css/phui/phui-info-view.css' => '6d7c3509',
|
||||||
'rsrc/css/phui/phui-list.css' => '9da2aa00',
|
'rsrc/css/phui/phui-list.css' => '9da2aa00',
|
||||||
'rsrc/css/phui/phui-object-box.css' => '407eaf5a',
|
'rsrc/css/phui/phui-object-box.css' => '407eaf5a',
|
||||||
'rsrc/css/phui/phui-object-item-list-view.css' => '0d484a97',
|
'rsrc/css/phui/phui-object-item-list-view.css' => 'fe594a65',
|
||||||
'rsrc/css/phui/phui-pager.css' => 'bea33d23',
|
'rsrc/css/phui/phui-pager.css' => 'bea33d23',
|
||||||
'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
|
'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
|
||||||
'rsrc/css/phui/phui-profile-menu.css' => 'ab4fcf5f',
|
'rsrc/css/phui/phui-profile-menu.css' => 'ab4fcf5f',
|
||||||
|
@ -149,15 +151,16 @@ return array(
|
||||||
'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591',
|
'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591',
|
||||||
'rsrc/css/phui/phui-spacing.css' => '042804d6',
|
'rsrc/css/phui/phui-spacing.css' => '042804d6',
|
||||||
'rsrc/css/phui/phui-status.css' => '888cedb8',
|
'rsrc/css/phui/phui-status.css' => '888cedb8',
|
||||||
'rsrc/css/phui/phui-tag-view.css' => 'e60e227b',
|
'rsrc/css/phui/phui-tag-view.css' => '9d5d4400',
|
||||||
'rsrc/css/phui/phui-timeline-view.css' => '2efceff8',
|
'rsrc/css/phui/phui-timeline-view.css' => '2efceff8',
|
||||||
'rsrc/css/phui/phui-two-column-view.css' => 'c75bfc5b',
|
'rsrc/css/phui/phui-two-column-view.css' => 'c75bfc5b',
|
||||||
'rsrc/css/phui/workboards/phui-workboard.css' => 'b07a5524',
|
'rsrc/css/phui/workboards/phui-workboard.css' => 'b07a5524',
|
||||||
'rsrc/css/phui/workboards/phui-workcard.css' => 'ffb55371',
|
'rsrc/css/phui/workboards/phui-workcard.css' => 'b4322ca7',
|
||||||
'rsrc/css/phui/workboards/phui-workpanel.css' => 'e9339dc3',
|
'rsrc/css/phui/workboards/phui-workpanel.css' => 'e1bd8d04',
|
||||||
'rsrc/css/sprite-login.css' => '60e8560e',
|
'rsrc/css/sprite-login.css' => '60e8560e',
|
||||||
'rsrc/css/sprite-menu.css' => '9dd65b92',
|
'rsrc/css/sprite-menu.css' => '9dd65b92',
|
||||||
'rsrc/css/sprite-tokens.css' => '4f399012',
|
'rsrc/css/sprite-tokens.css' => '4f399012',
|
||||||
|
'rsrc/externals/d3/d3.min.js' => 'a11a5ff2',
|
||||||
'rsrc/externals/font/aleo/aleo-bold.eot' => 'd3d3bed7',
|
'rsrc/externals/font/aleo/aleo-bold.eot' => 'd3d3bed7',
|
||||||
'rsrc/externals/font/aleo/aleo-bold.svg' => '45899c8e',
|
'rsrc/externals/font/aleo/aleo-bold.svg' => '45899c8e',
|
||||||
'rsrc/externals/font/aleo/aleo-bold.ttf' => '4b08bef0',
|
'rsrc/externals/font/aleo/aleo-bold.ttf' => '4b08bef0',
|
||||||
|
@ -252,9 +255,6 @@ return array(
|
||||||
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js' => '54f314a0',
|
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js' => '54f314a0',
|
||||||
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js' => '1bc11c4a',
|
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js' => '1bc11c4a',
|
||||||
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadStaticSource.js' => '6c0e62fa',
|
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadStaticSource.js' => '6c0e62fa',
|
||||||
'rsrc/externals/raphael/g.raphael.js' => '40dde778',
|
|
||||||
'rsrc/externals/raphael/g.raphael.line.js' => '40da039e',
|
|
||||||
'rsrc/externals/raphael/raphael.js' => '51ee6b43',
|
|
||||||
'rsrc/favicons/apple-touch-icon-120x120.png' => '43742962',
|
'rsrc/favicons/apple-touch-icon-120x120.png' => '43742962',
|
||||||
'rsrc/favicons/apple-touch-icon-152x152.png' => '669eaec3',
|
'rsrc/favicons/apple-touch-icon-152x152.png' => '669eaec3',
|
||||||
'rsrc/favicons/apple-touch-icon-76x76.png' => 'ecdef672',
|
'rsrc/favicons/apple-touch-icon-76x76.png' => 'ecdef672',
|
||||||
|
@ -369,7 +369,7 @@ return array(
|
||||||
'rsrc/js/application/countdown/timer.js' => 'e4cc26b3',
|
'rsrc/js/application/countdown/timer.js' => 'e4cc26b3',
|
||||||
'rsrc/js/application/daemon/behavior-bulk-job-reload.js' => 'edf8a145',
|
'rsrc/js/application/daemon/behavior-bulk-job-reload.js' => 'edf8a145',
|
||||||
'rsrc/js/application/dashboard/behavior-dashboard-async-panel.js' => '469c0d9e',
|
'rsrc/js/application/dashboard/behavior-dashboard-async-panel.js' => '469c0d9e',
|
||||||
'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '82439934',
|
'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '019f36c4',
|
||||||
'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375',
|
'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375',
|
||||||
'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63',
|
'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63',
|
||||||
'rsrc/js/application/differential/ChangesetViewManager.js' => 'a2828756',
|
'rsrc/js/application/differential/ChangesetViewManager.js' => 'a2828756',
|
||||||
|
@ -401,7 +401,7 @@ return array(
|
||||||
'rsrc/js/application/herald/herald-rule-editor.js' => '7ebaeed3',
|
'rsrc/js/application/herald/herald-rule-editor.js' => '7ebaeed3',
|
||||||
'rsrc/js/application/maniphest/behavior-batch-editor.js' => '782ab6e7',
|
'rsrc/js/application/maniphest/behavior-batch-editor.js' => '782ab6e7',
|
||||||
'rsrc/js/application/maniphest/behavior-batch-selector.js' => '7b98d7c5',
|
'rsrc/js/application/maniphest/behavior-batch-selector.js' => '7b98d7c5',
|
||||||
'rsrc/js/application/maniphest/behavior-line-chart.js' => '88f0c5b3',
|
'rsrc/js/application/maniphest/behavior-line-chart.js' => 'e4232876',
|
||||||
'rsrc/js/application/maniphest/behavior-list-edit.js' => 'a9f88de2',
|
'rsrc/js/application/maniphest/behavior-list-edit.js' => 'a9f88de2',
|
||||||
'rsrc/js/application/maniphest/behavior-subpriorityeditor.js' => '71237763',
|
'rsrc/js/application/maniphest/behavior-subpriorityeditor.js' => '71237763',
|
||||||
'rsrc/js/application/owners/OwnersPathEditor.js' => 'aa1733d0',
|
'rsrc/js/application/owners/OwnersPathEditor.js' => 'aa1733d0',
|
||||||
|
@ -412,9 +412,9 @@ return array(
|
||||||
'rsrc/js/application/phortune/behavior-stripe-payment-form.js' => '3f5d6dbf',
|
'rsrc/js/application/phortune/behavior-stripe-payment-form.js' => '3f5d6dbf',
|
||||||
'rsrc/js/application/phortune/behavior-test-payment-form.js' => 'fc91ab6c',
|
'rsrc/js/application/phortune/behavior-test-payment-form.js' => 'fc91ab6c',
|
||||||
'rsrc/js/application/phortune/phortune-credit-card-form.js' => '2290aeef',
|
'rsrc/js/application/phortune/phortune-credit-card-form.js' => '2290aeef',
|
||||||
'rsrc/js/application/policy/behavior-policy-control.js' => 'ae45872f',
|
'rsrc/js/application/policy/behavior-policy-control.js' => 'd0c516d5',
|
||||||
'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '5e9f347c',
|
'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '5e9f347c',
|
||||||
'rsrc/js/application/projects/behavior-project-boards.js' => 'ba4fa35c',
|
'rsrc/js/application/projects/behavior-project-boards.js' => '48470f95',
|
||||||
'rsrc/js/application/projects/behavior-project-create.js' => '065227cc',
|
'rsrc/js/application/projects/behavior-project-create.js' => '065227cc',
|
||||||
'rsrc/js/application/projects/behavior-reorder-columns.js' => 'e1d25dfb',
|
'rsrc/js/application/projects/behavior-reorder-columns.js' => 'e1d25dfb',
|
||||||
'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf',
|
'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf',
|
||||||
|
@ -447,9 +447,9 @@ return array(
|
||||||
'rsrc/js/application/uiexample/notification-example.js' => '8ce821c5',
|
'rsrc/js/application/uiexample/notification-example.js' => '8ce821c5',
|
||||||
'rsrc/js/core/Busy.js' => '59a7976a',
|
'rsrc/js/core/Busy.js' => '59a7976a',
|
||||||
'rsrc/js/core/DragAndDropFileUpload.js' => 'ad10aeac',
|
'rsrc/js/core/DragAndDropFileUpload.js' => 'ad10aeac',
|
||||||
'rsrc/js/core/DraggableList.js' => 'a16ec1c6',
|
'rsrc/js/core/DraggableList.js' => '8905523d',
|
||||||
'rsrc/js/core/FileUpload.js' => '477359c8',
|
'rsrc/js/core/FileUpload.js' => '477359c8',
|
||||||
'rsrc/js/core/Hovercard.js' => 'c6f720ff',
|
'rsrc/js/core/Hovercard.js' => '1bd28176',
|
||||||
'rsrc/js/core/KeyboardShortcut.js' => '1ae869f2',
|
'rsrc/js/core/KeyboardShortcut.js' => '1ae869f2',
|
||||||
'rsrc/js/core/KeyboardShortcutManager.js' => 'c1700f6f',
|
'rsrc/js/core/KeyboardShortcutManager.js' => 'c1700f6f',
|
||||||
'rsrc/js/core/MultirowRowManager.js' => 'b5d57730',
|
'rsrc/js/core/MultirowRowManager.js' => 'b5d57730',
|
||||||
|
@ -458,7 +458,7 @@ return array(
|
||||||
'rsrc/js/core/ShapedRequest.js' => '7cbe244b',
|
'rsrc/js/core/ShapedRequest.js' => '7cbe244b',
|
||||||
'rsrc/js/core/TextAreaUtils.js' => '9e54692d',
|
'rsrc/js/core/TextAreaUtils.js' => '9e54692d',
|
||||||
'rsrc/js/core/Title.js' => 'df5e11d2',
|
'rsrc/js/core/Title.js' => 'df5e11d2',
|
||||||
'rsrc/js/core/ToolTip.js' => '1d298e3a',
|
'rsrc/js/core/ToolTip.js' => '6323f942',
|
||||||
'rsrc/js/core/behavior-active-nav.js' => 'e379b58e',
|
'rsrc/js/core/behavior-active-nav.js' => 'e379b58e',
|
||||||
'rsrc/js/core/behavior-audio-source.js' => '59b251eb',
|
'rsrc/js/core/behavior-audio-source.js' => '59b251eb',
|
||||||
'rsrc/js/core/behavior-autofocus.js' => '7319e029',
|
'rsrc/js/core/behavior-autofocus.js' => '7319e029',
|
||||||
|
@ -475,7 +475,7 @@ return array(
|
||||||
'rsrc/js/core/behavior-global-drag-and-drop.js' => 'c8e57404',
|
'rsrc/js/core/behavior-global-drag-and-drop.js' => 'c8e57404',
|
||||||
'rsrc/js/core/behavior-high-security-warning.js' => 'a464fe03',
|
'rsrc/js/core/behavior-high-security-warning.js' => 'a464fe03',
|
||||||
'rsrc/js/core/behavior-history-install.js' => '7ee2b591',
|
'rsrc/js/core/behavior-history-install.js' => '7ee2b591',
|
||||||
'rsrc/js/core/behavior-hovercard.js' => '66dd6e9e',
|
'rsrc/js/core/behavior-hovercard.js' => 'bcaccd64',
|
||||||
'rsrc/js/core/behavior-keyboard-pager.js' => 'a8da01f0',
|
'rsrc/js/core/behavior-keyboard-pager.js' => 'a8da01f0',
|
||||||
'rsrc/js/core/behavior-keyboard-shortcuts.js' => 'd75709e6',
|
'rsrc/js/core/behavior-keyboard-shortcuts.js' => 'd75709e6',
|
||||||
'rsrc/js/core/behavior-lightbox-attachments.js' => 'f8ba29d7',
|
'rsrc/js/core/behavior-lightbox-attachments.js' => 'f8ba29d7',
|
||||||
|
@ -490,7 +490,7 @@ return array(
|
||||||
'rsrc/js/core/behavior-reorder-applications.js' => '76b9fc3e',
|
'rsrc/js/core/behavior-reorder-applications.js' => '76b9fc3e',
|
||||||
'rsrc/js/core/behavior-reveal-content.js' => '60821bc7',
|
'rsrc/js/core/behavior-reveal-content.js' => '60821bc7',
|
||||||
'rsrc/js/core/behavior-scrollbar.js' => '834a1173',
|
'rsrc/js/core/behavior-scrollbar.js' => '834a1173',
|
||||||
'rsrc/js/core/behavior-search-typeahead.js' => '0b7a4f6e',
|
'rsrc/js/core/behavior-search-typeahead.js' => '06c32383',
|
||||||
'rsrc/js/core/behavior-select-on-click.js' => '4e3e79a6',
|
'rsrc/js/core/behavior-select-on-click.js' => '4e3e79a6',
|
||||||
'rsrc/js/core/behavior-time-typeahead.js' => 'f80d6bf0',
|
'rsrc/js/core/behavior-time-typeahead.js' => 'f80d6bf0',
|
||||||
'rsrc/js/core/behavior-toggle-class.js' => '5d7c9f33',
|
'rsrc/js/core/behavior-toggle-class.js' => '5d7c9f33',
|
||||||
|
@ -535,6 +535,7 @@ return array(
|
||||||
'conpherence-transaction-css' => '85d0974c',
|
'conpherence-transaction-css' => '85d0974c',
|
||||||
'conpherence-update-css' => 'faf6be09',
|
'conpherence-update-css' => 'faf6be09',
|
||||||
'conpherence-widget-pane-css' => '775eaaba',
|
'conpherence-widget-pane-css' => '775eaaba',
|
||||||
|
'd3' => 'a11a5ff2',
|
||||||
'differential-changeset-view-css' => 'b6b0d1bb',
|
'differential-changeset-view-css' => 'b6b0d1bb',
|
||||||
'differential-core-view-css' => '7ac3cabc',
|
'differential-core-view-css' => '7ac3cabc',
|
||||||
'differential-inline-comment-editor' => '64a5550f',
|
'differential-inline-comment-editor' => '64a5550f',
|
||||||
|
@ -579,7 +580,7 @@ return array(
|
||||||
'javelin-behavior-countdown-timer' => 'e4cc26b3',
|
'javelin-behavior-countdown-timer' => 'e4cc26b3',
|
||||||
'javelin-behavior-dark-console' => 'f411b6ae',
|
'javelin-behavior-dark-console' => 'f411b6ae',
|
||||||
'javelin-behavior-dashboard-async-panel' => '469c0d9e',
|
'javelin-behavior-dashboard-async-panel' => '469c0d9e',
|
||||||
'javelin-behavior-dashboard-move-panels' => '82439934',
|
'javelin-behavior-dashboard-move-panels' => '019f36c4',
|
||||||
'javelin-behavior-dashboard-query-panel-select' => '453c5375',
|
'javelin-behavior-dashboard-query-panel-select' => '453c5375',
|
||||||
'javelin-behavior-dashboard-tab-panel' => 'd4eecc63',
|
'javelin-behavior-dashboard-tab-panel' => 'd4eecc63',
|
||||||
'javelin-behavior-day-view' => '5c46cff2',
|
'javelin-behavior-day-view' => '5c46cff2',
|
||||||
|
@ -615,7 +616,7 @@ return array(
|
||||||
'javelin-behavior-icon-composer' => '8499b6ab',
|
'javelin-behavior-icon-composer' => '8499b6ab',
|
||||||
'javelin-behavior-launch-icon-composer' => '48086888',
|
'javelin-behavior-launch-icon-composer' => '48086888',
|
||||||
'javelin-behavior-lightbox-attachments' => 'f8ba29d7',
|
'javelin-behavior-lightbox-attachments' => 'f8ba29d7',
|
||||||
'javelin-behavior-line-chart' => '88f0c5b3',
|
'javelin-behavior-line-chart' => 'e4232876',
|
||||||
'javelin-behavior-load-blame' => '42126667',
|
'javelin-behavior-load-blame' => '42126667',
|
||||||
'javelin-behavior-maniphest-batch-editor' => '782ab6e7',
|
'javelin-behavior-maniphest-batch-editor' => '782ab6e7',
|
||||||
'javelin-behavior-maniphest-batch-selector' => '7b98d7c5',
|
'javelin-behavior-maniphest-batch-selector' => '7b98d7c5',
|
||||||
|
@ -630,7 +631,6 @@ return array(
|
||||||
'javelin-behavior-phabricator-file-tree' => '88236f00',
|
'javelin-behavior-phabricator-file-tree' => '88236f00',
|
||||||
'javelin-behavior-phabricator-gesture' => '3ab51e2c',
|
'javelin-behavior-phabricator-gesture' => '3ab51e2c',
|
||||||
'javelin-behavior-phabricator-gesture-example' => '558829c2',
|
'javelin-behavior-phabricator-gesture-example' => '558829c2',
|
||||||
'javelin-behavior-phabricator-hovercards' => '66dd6e9e',
|
|
||||||
'javelin-behavior-phabricator-keyboard-pager' => 'a8da01f0',
|
'javelin-behavior-phabricator-keyboard-pager' => 'a8da01f0',
|
||||||
'javelin-behavior-phabricator-keyboard-shortcuts' => 'd75709e6',
|
'javelin-behavior-phabricator-keyboard-shortcuts' => 'd75709e6',
|
||||||
'javelin-behavior-phabricator-line-linker' => '1499a8cb',
|
'javelin-behavior-phabricator-line-linker' => '1499a8cb',
|
||||||
|
@ -640,7 +640,7 @@ return array(
|
||||||
'javelin-behavior-phabricator-oncopy' => '2926fff2',
|
'javelin-behavior-phabricator-oncopy' => '2926fff2',
|
||||||
'javelin-behavior-phabricator-remarkup-assist' => '340c8eff',
|
'javelin-behavior-phabricator-remarkup-assist' => '340c8eff',
|
||||||
'javelin-behavior-phabricator-reveal-content' => '60821bc7',
|
'javelin-behavior-phabricator-reveal-content' => '60821bc7',
|
||||||
'javelin-behavior-phabricator-search-typeahead' => '0b7a4f6e',
|
'javelin-behavior-phabricator-search-typeahead' => '06c32383',
|
||||||
'javelin-behavior-phabricator-show-older-transactions' => 'dbbf48b6',
|
'javelin-behavior-phabricator-show-older-transactions' => 'dbbf48b6',
|
||||||
'javelin-behavior-phabricator-tooltips' => '3ee3408b',
|
'javelin-behavior-phabricator-tooltips' => '3ee3408b',
|
||||||
'javelin-behavior-phabricator-transaction-comment-form' => 'b23b49e6',
|
'javelin-behavior-phabricator-transaction-comment-form' => 'b23b49e6',
|
||||||
|
@ -649,11 +649,12 @@ return array(
|
||||||
'javelin-behavior-pholio-mock-edit' => '246dc085',
|
'javelin-behavior-pholio-mock-edit' => '246dc085',
|
||||||
'javelin-behavior-pholio-mock-view' => 'fbe497e7',
|
'javelin-behavior-pholio-mock-view' => 'fbe497e7',
|
||||||
'javelin-behavior-phui-dropdown-menu' => '54733475',
|
'javelin-behavior-phui-dropdown-menu' => '54733475',
|
||||||
|
'javelin-behavior-phui-hovercards' => 'bcaccd64',
|
||||||
'javelin-behavior-phui-object-box-tabs' => '2bfa2836',
|
'javelin-behavior-phui-object-box-tabs' => '2bfa2836',
|
||||||
'javelin-behavior-phui-profile-menu' => '12884df9',
|
'javelin-behavior-phui-profile-menu' => '12884df9',
|
||||||
'javelin-behavior-policy-control' => 'ae45872f',
|
'javelin-behavior-policy-control' => 'd0c516d5',
|
||||||
'javelin-behavior-policy-rule-editor' => '5e9f347c',
|
'javelin-behavior-policy-rule-editor' => '5e9f347c',
|
||||||
'javelin-behavior-project-boards' => 'ba4fa35c',
|
'javelin-behavior-project-boards' => '48470f95',
|
||||||
'javelin-behavior-project-create' => '065227cc',
|
'javelin-behavior-project-create' => '065227cc',
|
||||||
'javelin-behavior-quicksand-blacklist' => '7927a7d3',
|
'javelin-behavior-quicksand-blacklist' => '7927a7d3',
|
||||||
'javelin-behavior-recurring-edit' => '5f1c4d5f',
|
'javelin-behavior-recurring-edit' => '5f1c4d5f',
|
||||||
|
@ -741,14 +742,12 @@ return array(
|
||||||
'phabricator-countdown-css' => 'e7544472',
|
'phabricator-countdown-css' => 'e7544472',
|
||||||
'phabricator-dashboard-css' => 'eb458607',
|
'phabricator-dashboard-css' => 'eb458607',
|
||||||
'phabricator-drag-and-drop-file-upload' => 'ad10aeac',
|
'phabricator-drag-and-drop-file-upload' => 'ad10aeac',
|
||||||
'phabricator-draggable-list' => 'a16ec1c6',
|
'phabricator-draggable-list' => '8905523d',
|
||||||
'phabricator-fatal-config-template-css' => '8e6c6fcd',
|
'phabricator-fatal-config-template-css' => '8e6c6fcd',
|
||||||
'phabricator-feed-css' => 'ecd4ec57',
|
'phabricator-feed-css' => 'ecd4ec57',
|
||||||
'phabricator-file-upload' => '477359c8',
|
'phabricator-file-upload' => '477359c8',
|
||||||
'phabricator-filetree-view-css' => 'fccf9f82',
|
'phabricator-filetree-view-css' => 'fccf9f82',
|
||||||
'phabricator-flag-css' => '5337623f',
|
'phabricator-flag-css' => '5337623f',
|
||||||
'phabricator-hovercard' => 'c6f720ff',
|
|
||||||
'phabricator-hovercard-view-css' => '1239cd52',
|
|
||||||
'phabricator-keyboard-shortcut' => '1ae869f2',
|
'phabricator-keyboard-shortcut' => '1ae869f2',
|
||||||
'phabricator-keyboard-shortcut-manager' => 'c1700f6f',
|
'phabricator-keyboard-shortcut-manager' => 'c1700f6f',
|
||||||
'phabricator-main-menu-view' => 'd00a795a',
|
'phabricator-main-menu-view' => 'd00a795a',
|
||||||
|
@ -765,10 +764,10 @@ return array(
|
||||||
'phabricator-side-menu-view-css' => '3a3d9f41',
|
'phabricator-side-menu-view-css' => '3a3d9f41',
|
||||||
'phabricator-slowvote-css' => 'da0afb1b',
|
'phabricator-slowvote-css' => 'da0afb1b',
|
||||||
'phabricator-source-code-view-css' => 'cbeef983',
|
'phabricator-source-code-view-css' => 'cbeef983',
|
||||||
'phabricator-standard-page-view' => '7b0d68d8',
|
'phabricator-standard-page-view' => 'c4467133',
|
||||||
'phabricator-textareautils' => '9e54692d',
|
'phabricator-textareautils' => '9e54692d',
|
||||||
'phabricator-title' => 'df5e11d2',
|
'phabricator-title' => 'df5e11d2',
|
||||||
'phabricator-tooltip' => '1d298e3a',
|
'phabricator-tooltip' => '6323f942',
|
||||||
'phabricator-ui-example-css' => '528b19de',
|
'phabricator-ui-example-css' => '528b19de',
|
||||||
'phabricator-uiexample-javelin-view' => 'd4a14807',
|
'phabricator-uiexample-javelin-view' => 'd4a14807',
|
||||||
'phabricator-uiexample-reactor-button' => 'd19198c8',
|
'phabricator-uiexample-reactor-button' => 'd19198c8',
|
||||||
|
@ -780,8 +779,8 @@ return array(
|
||||||
'phabricator-uiexample-reactor-select' => 'a155550f',
|
'phabricator-uiexample-reactor-select' => 'a155550f',
|
||||||
'phabricator-uiexample-reactor-sendclass' => '1def2711',
|
'phabricator-uiexample-reactor-sendclass' => '1def2711',
|
||||||
'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
|
'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
|
||||||
'phabricator-zindex-css' => 'a36a45da',
|
'phabricator-zindex-css' => '5c7025bf',
|
||||||
'phame-css' => '6d5b3682',
|
'phame-css' => '1dbbacf9',
|
||||||
'pholio-css' => '95174bdd',
|
'pholio-css' => '95174bdd',
|
||||||
'pholio-edit-css' => '3ad9d1ee',
|
'pholio-edit-css' => '3ad9d1ee',
|
||||||
'pholio-inline-comments-css' => '8e545e49',
|
'pholio-inline-comments-css' => '8e545e49',
|
||||||
|
@ -799,16 +798,19 @@ return array(
|
||||||
'phui-calendar-day-css' => 'd1cf6f93',
|
'phui-calendar-day-css' => 'd1cf6f93',
|
||||||
'phui-calendar-list-css' => 'c1c7f338',
|
'phui-calendar-list-css' => 'c1c7f338',
|
||||||
'phui-calendar-month-css' => '476be7e0',
|
'phui-calendar-month-css' => '476be7e0',
|
||||||
|
'phui-chart-css' => '6bf6f78e',
|
||||||
'phui-crumbs-view-css' => '414406b5',
|
'phui-crumbs-view-css' => '414406b5',
|
||||||
'phui-document-summary-view-css' => '9ca48bdf',
|
'phui-document-summary-view-css' => '9ca48bdf',
|
||||||
'phui-document-view-css' => '9c71d2bf',
|
'phui-document-view-css' => '9c71d2bf',
|
||||||
'phui-document-view-pro-css' => '8799acf7',
|
'phui-document-view-pro-css' => '8799acf7',
|
||||||
'phui-feed-story-css' => 'b7b26d23',
|
'phui-feed-story-css' => '04aec08f',
|
||||||
'phui-font-icon-base-css' => 'ecbbb4c2',
|
'phui-font-icon-base-css' => 'ecbbb4c2',
|
||||||
'phui-fontkit-css' => '9cda225e',
|
'phui-fontkit-css' => '9cda225e',
|
||||||
'phui-form-css' => '0b98e572',
|
'phui-form-css' => '0b98e572',
|
||||||
'phui-form-view-css' => '4a1a0f5e',
|
'phui-form-view-css' => '4a1a0f5e',
|
||||||
'phui-header-view-css' => 'd53cc835',
|
'phui-header-view-css' => 'd53cc835',
|
||||||
|
'phui-hovercard' => '1bd28176',
|
||||||
|
'phui-hovercard-view-css' => 'de1a2119',
|
||||||
'phui-icon-set-selector-css' => '1ab67aad',
|
'phui-icon-set-selector-css' => '1ab67aad',
|
||||||
'phui-icon-view-css' => '3f33ab57',
|
'phui-icon-view-css' => '3f33ab57',
|
||||||
'phui-image-mask-css' => '5a8b09c8',
|
'phui-image-mask-css' => '5a8b09c8',
|
||||||
|
@ -817,7 +819,7 @@ return array(
|
||||||
'phui-inline-comment-view-css' => '0fdb3667',
|
'phui-inline-comment-view-css' => '0fdb3667',
|
||||||
'phui-list-view-css' => '9da2aa00',
|
'phui-list-view-css' => '9da2aa00',
|
||||||
'phui-object-box-css' => '407eaf5a',
|
'phui-object-box-css' => '407eaf5a',
|
||||||
'phui-object-item-list-view-css' => '0d484a97',
|
'phui-object-item-list-view-css' => 'fe594a65',
|
||||||
'phui-pager-css' => 'bea33d23',
|
'phui-pager-css' => 'bea33d23',
|
||||||
'phui-pinboard-view-css' => '2495140e',
|
'phui-pinboard-view-css' => '2495140e',
|
||||||
'phui-profile-menu-css' => 'ab4fcf5f',
|
'phui-profile-menu-css' => 'ab4fcf5f',
|
||||||
|
@ -825,13 +827,13 @@ return array(
|
||||||
'phui-remarkup-preview-css' => '1a8f2591',
|
'phui-remarkup-preview-css' => '1a8f2591',
|
||||||
'phui-spacing-css' => '042804d6',
|
'phui-spacing-css' => '042804d6',
|
||||||
'phui-status-list-view-css' => '888cedb8',
|
'phui-status-list-view-css' => '888cedb8',
|
||||||
'phui-tag-view-css' => 'e60e227b',
|
'phui-tag-view-css' => '9d5d4400',
|
||||||
'phui-theme-css' => 'ab7b848c',
|
'phui-theme-css' => 'ab7b848c',
|
||||||
'phui-timeline-view-css' => '2efceff8',
|
'phui-timeline-view-css' => '2efceff8',
|
||||||
'phui-two-column-view-css' => 'c75bfc5b',
|
'phui-two-column-view-css' => 'c75bfc5b',
|
||||||
'phui-workboard-view-css' => 'b07a5524',
|
'phui-workboard-view-css' => 'b07a5524',
|
||||||
'phui-workcard-view-css' => 'ffb55371',
|
'phui-workcard-view-css' => 'b4322ca7',
|
||||||
'phui-workpanel-view-css' => 'e9339dc3',
|
'phui-workpanel-view-css' => 'e1bd8d04',
|
||||||
'phuix-action-list-view' => 'b5c256b8',
|
'phuix-action-list-view' => 'b5c256b8',
|
||||||
'phuix-action-view' => '8cf6d262',
|
'phuix-action-view' => '8cf6d262',
|
||||||
'phuix-autocomplete' => '9196fb06',
|
'phuix-autocomplete' => '9196fb06',
|
||||||
|
@ -842,10 +844,8 @@ return array(
|
||||||
'policy-edit-css' => '815c66f7',
|
'policy-edit-css' => '815c66f7',
|
||||||
'policy-transaction-detail-css' => '82100a43',
|
'policy-transaction-detail-css' => '82100a43',
|
||||||
'ponder-view-css' => '7b0df4da',
|
'ponder-view-css' => '7b0df4da',
|
||||||
'project-view-css' => '22f7ed0e',
|
'project-card-view-css' => '9c3631e5',
|
||||||
'raphael-core' => '51ee6b43',
|
'project-view-css' => '4693497c',
|
||||||
'raphael-g' => '40dde778',
|
|
||||||
'raphael-g-line' => '40da039e',
|
|
||||||
'releeph-core' => '9b3c5733',
|
'releeph-core' => '9b3c5733',
|
||||||
'releeph-preview-branch' => 'b7a6f4a5',
|
'releeph-preview-branch' => 'b7a6f4a5',
|
||||||
'releeph-request-differential-create-dialog' => '8d8b92cd',
|
'releeph-request-differential-create-dialog' => '8d8b92cd',
|
||||||
|
@ -877,6 +877,14 @@ return array(
|
||||||
'javelin-behavior-device',
|
'javelin-behavior-device',
|
||||||
'javelin-vector',
|
'javelin-vector',
|
||||||
),
|
),
|
||||||
|
'019f36c4' => array(
|
||||||
|
'javelin-behavior',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-util',
|
||||||
|
'javelin-stratcom',
|
||||||
|
'javelin-workflow',
|
||||||
|
'phabricator-draggable-list',
|
||||||
|
),
|
||||||
'031cee25' => array(
|
'031cee25' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-request',
|
'javelin-request',
|
||||||
|
@ -901,6 +909,17 @@ return array(
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
'javelin-workflow',
|
'javelin-workflow',
|
||||||
),
|
),
|
||||||
|
'06c32383' => array(
|
||||||
|
'javelin-behavior',
|
||||||
|
'javelin-typeahead-ondemand-source',
|
||||||
|
'javelin-typeahead',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-uri',
|
||||||
|
'javelin-util',
|
||||||
|
'javelin-stratcom',
|
||||||
|
'phabricator-prefab',
|
||||||
|
'phuix-icon-view',
|
||||||
|
),
|
||||||
'087e919c' => array(
|
'087e919c' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
@ -914,16 +933,6 @@ return array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'javelin-router',
|
'javelin-router',
|
||||||
),
|
),
|
||||||
'0b7a4f6e' => array(
|
|
||||||
'javelin-behavior',
|
|
||||||
'javelin-typeahead-ondemand-source',
|
|
||||||
'javelin-typeahead',
|
|
||||||
'javelin-dom',
|
|
||||||
'javelin-uri',
|
|
||||||
'javelin-util',
|
|
||||||
'javelin-stratcom',
|
|
||||||
'phabricator-prefab',
|
|
||||||
),
|
|
||||||
'0f764c35' => array(
|
'0f764c35' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
|
@ -964,11 +973,12 @@ return array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'javelin-typeahead-normalizer',
|
'javelin-typeahead-normalizer',
|
||||||
),
|
),
|
||||||
'1d298e3a' => array(
|
'1bd28176' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-util',
|
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'javelin-vector',
|
'javelin-vector',
|
||||||
|
'javelin-request',
|
||||||
|
'javelin-uri',
|
||||||
),
|
),
|
||||||
'1d45c74d' => array(
|
'1d45c74d' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
|
@ -1144,6 +1154,15 @@ return array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'javelin-workflow',
|
'javelin-workflow',
|
||||||
),
|
),
|
||||||
|
'48470f95' => array(
|
||||||
|
'javelin-behavior',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-util',
|
||||||
|
'javelin-vector',
|
||||||
|
'javelin-stratcom',
|
||||||
|
'javelin-workflow',
|
||||||
|
'phabricator-draggable-list',
|
||||||
|
),
|
||||||
'49b73b36' => array(
|
'49b73b36' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
@ -1292,6 +1311,12 @@ return array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
),
|
),
|
||||||
|
'6323f942' => array(
|
||||||
|
'javelin-install',
|
||||||
|
'javelin-util',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-vector',
|
||||||
|
),
|
||||||
'635de1ec' => array(
|
'635de1ec' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
|
@ -1306,13 +1331,6 @@ return array(
|
||||||
'javelin-request',
|
'javelin-request',
|
||||||
'javelin-workflow',
|
'javelin-workflow',
|
||||||
),
|
),
|
||||||
'66dd6e9e' => array(
|
|
||||||
'javelin-behavior',
|
|
||||||
'javelin-behavior-device',
|
|
||||||
'javelin-stratcom',
|
|
||||||
'javelin-vector',
|
|
||||||
'phabricator-hovercard',
|
|
||||||
),
|
|
||||||
'6882e80a' => array(
|
'6882e80a' => array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
),
|
),
|
||||||
|
@ -1432,14 +1450,6 @@ return array(
|
||||||
'javelin-vector',
|
'javelin-vector',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
),
|
),
|
||||||
82439934 => array(
|
|
||||||
'javelin-behavior',
|
|
||||||
'javelin-dom',
|
|
||||||
'javelin-util',
|
|
||||||
'javelin-stratcom',
|
|
||||||
'javelin-workflow',
|
|
||||||
'phabricator-draggable-list',
|
|
||||||
),
|
|
||||||
'834a1173' => array(
|
'834a1173' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-scrollbar',
|
'javelin-scrollbar',
|
||||||
|
@ -1477,10 +1487,13 @@ return array(
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
),
|
),
|
||||||
'88f0c5b3' => array(
|
'8905523d' => array(
|
||||||
'javelin-behavior',
|
'javelin-install',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
'javelin-stratcom',
|
||||||
|
'javelin-util',
|
||||||
'javelin-vector',
|
'javelin-vector',
|
||||||
|
'javelin-magical-init',
|
||||||
),
|
),
|
||||||
'8a41885b' => array(
|
'8a41885b' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
|
@ -1594,14 +1607,6 @@ return array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'javelin-reactor-dom',
|
'javelin-reactor-dom',
|
||||||
),
|
),
|
||||||
'a16ec1c6' => array(
|
|
||||||
'javelin-install',
|
|
||||||
'javelin-dom',
|
|
||||||
'javelin-stratcom',
|
|
||||||
'javelin-util',
|
|
||||||
'javelin-vector',
|
|
||||||
'javelin-magical-init',
|
|
||||||
),
|
|
||||||
'a2828756' => array(
|
'a2828756' => array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
|
@ -1677,15 +1682,6 @@ return array(
|
||||||
'javelin-uri',
|
'javelin-uri',
|
||||||
'phabricator-file-upload',
|
'phabricator-file-upload',
|
||||||
),
|
),
|
||||||
'ae45872f' => array(
|
|
||||||
'javelin-behavior',
|
|
||||||
'javelin-dom',
|
|
||||||
'javelin-util',
|
|
||||||
'phuix-dropdown-menu',
|
|
||||||
'phuix-action-list-view',
|
|
||||||
'phuix-action-view',
|
|
||||||
'javelin-workflow',
|
|
||||||
),
|
|
||||||
'b064af76' => array(
|
'b064af76' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
|
@ -1759,14 +1755,12 @@ return array(
|
||||||
'b6b0d1bb' => array(
|
'b6b0d1bb' => array(
|
||||||
'phui-inline-comment-view-css',
|
'phui-inline-comment-view-css',
|
||||||
),
|
),
|
||||||
'ba4fa35c' => array(
|
'bcaccd64' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-dom',
|
'javelin-behavior-device',
|
||||||
'javelin-util',
|
|
||||||
'javelin-vector',
|
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
'javelin-workflow',
|
'javelin-vector',
|
||||||
'phabricator-draggable-list',
|
'phui-hovercard',
|
||||||
),
|
),
|
||||||
'bd4c8dca' => array(
|
'bd4c8dca' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
|
@ -1792,13 +1786,6 @@ return array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'javelin-vector',
|
'javelin-vector',
|
||||||
),
|
),
|
||||||
'c6f720ff' => array(
|
|
||||||
'javelin-install',
|
|
||||||
'javelin-dom',
|
|
||||||
'javelin-vector',
|
|
||||||
'javelin-request',
|
|
||||||
'javelin-uri',
|
|
||||||
),
|
|
||||||
'c72aa091' => array(
|
'c72aa091' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
@ -1854,6 +1841,16 @@ return array(
|
||||||
'd00a795a' => array(
|
'd00a795a' => array(
|
||||||
'phui-theme-css',
|
'phui-theme-css',
|
||||||
),
|
),
|
||||||
|
'd0c516d5' => array(
|
||||||
|
'javelin-behavior',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-util',
|
||||||
|
'phuix-dropdown-menu',
|
||||||
|
'phuix-action-list-view',
|
||||||
|
'phuix-action-view',
|
||||||
|
'javelin-workflow',
|
||||||
|
'phuix-icon-view',
|
||||||
|
),
|
||||||
'd19198c8' => array(
|
'd19198c8' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
@ -1921,6 +1918,9 @@ return array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'phabricator-prefab',
|
'phabricator-prefab',
|
||||||
),
|
),
|
||||||
|
'e1bd8d04' => array(
|
||||||
|
'phui-workcard-view-css',
|
||||||
|
),
|
||||||
'e1d25dfb' => array(
|
'e1d25dfb' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
|
@ -1950,6 +1950,12 @@ return array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'javelin-uri',
|
'javelin-uri',
|
||||||
),
|
),
|
||||||
|
'e4232876' => array(
|
||||||
|
'javelin-behavior',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-vector',
|
||||||
|
'phui-chart-css',
|
||||||
|
),
|
||||||
'e4cc26b3' => array(
|
'e4cc26b3' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
@ -1982,9 +1988,6 @@ return array(
|
||||||
'e6e25838' => array(
|
'e6e25838' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
),
|
),
|
||||||
'e9339dc3' => array(
|
|
||||||
'phui-workcard-view-css',
|
|
||||||
),
|
|
||||||
'e9581f08' => array(
|
'e9581f08' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
|
@ -2221,8 +2224,8 @@ return array(
|
||||||
'phabricator-file-upload',
|
'phabricator-file-upload',
|
||||||
'javelin-behavior-global-drag-and-drop',
|
'javelin-behavior-global-drag-and-drop',
|
||||||
'javelin-behavior-phabricator-reveal-content',
|
'javelin-behavior-phabricator-reveal-content',
|
||||||
'phabricator-hovercard',
|
'phui-hovercard',
|
||||||
'javelin-behavior-phabricator-hovercards',
|
'javelin-behavior-phui-hovercards',
|
||||||
'javelin-color',
|
'javelin-color',
|
||||||
'javelin-fx',
|
'javelin-fx',
|
||||||
'phabricator-draggable-list',
|
'phabricator-draggable-list',
|
||||||
|
|
|
@ -59,8 +59,8 @@ return array(
|
||||||
'phabricator-file-upload',
|
'phabricator-file-upload',
|
||||||
'javelin-behavior-global-drag-and-drop',
|
'javelin-behavior-global-drag-and-drop',
|
||||||
'javelin-behavior-phabricator-reveal-content',
|
'javelin-behavior-phabricator-reveal-content',
|
||||||
'phabricator-hovercard',
|
'phui-hovercard',
|
||||||
'javelin-behavior-phabricator-hovercards',
|
'javelin-behavior-phui-hovercards',
|
||||||
'javelin-color',
|
'javelin-color',
|
||||||
'javelin-fx',
|
'javelin-fx',
|
||||||
'phabricator-draggable-list',
|
'phabricator-draggable-list',
|
||||||
|
|
2
resources/sql/autopatches/20160202.board.1.proxy.sql
Normal file
2
resources/sql/autopatches/20160202.board.1.proxy.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_project.project_column
|
||||||
|
ADD proxyPHID VARBINARY(64);
|
5
resources/sql/autopatches/20160202.ipv6.1.sql
Normal file
5
resources/sql/autopatches/20160202.ipv6.1.sql
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_repository.repository_pullevent
|
||||||
|
CHANGE remoteAddress remoteAddress VARBINARY(64);
|
||||||
|
|
||||||
|
ALTER TABLE {$NAMESPACE}_repository.repository_pushevent
|
||||||
|
CHANGE remoteAddress remoteAddress VARBINARY(64);
|
39
resources/sql/autopatches/20160202.ipv6.2.php
Normal file
39
resources/sql/autopatches/20160202.ipv6.2.php
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$pull = new PhabricatorRepositoryPullEvent();
|
||||||
|
$push = new PhabricatorRepositoryPushEvent();
|
||||||
|
|
||||||
|
$conn_w = $pull->establishConnection('w');
|
||||||
|
|
||||||
|
$log_types = array($pull, $push);
|
||||||
|
foreach ($log_types as $log) {
|
||||||
|
foreach (new LiskMigrationIterator($log) as $row) {
|
||||||
|
$addr = $row->getRemoteAddress();
|
||||||
|
|
||||||
|
$addr = (string)$addr;
|
||||||
|
if (!strlen($addr)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctype_digit($addr)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(int)$addr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ip = long2ip($addr);
|
||||||
|
if (!is_string($ip) || !strlen($ip)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $row->getID();
|
||||||
|
queryfx(
|
||||||
|
$conn_w,
|
||||||
|
'UPDATE %T SET remoteAddress = %s WHERE id = %d',
|
||||||
|
$log->getTableName(),
|
||||||
|
$ip,
|
||||||
|
$id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1493,6 +1493,8 @@ phutil_register_library_map(array(
|
||||||
'PHUIHandleTagListView' => 'applications/phid/view/PHUIHandleTagListView.php',
|
'PHUIHandleTagListView' => 'applications/phid/view/PHUIHandleTagListView.php',
|
||||||
'PHUIHandleView' => 'applications/phid/view/PHUIHandleView.php',
|
'PHUIHandleView' => 'applications/phid/view/PHUIHandleView.php',
|
||||||
'PHUIHeaderView' => 'view/phui/PHUIHeaderView.php',
|
'PHUIHeaderView' => 'view/phui/PHUIHeaderView.php',
|
||||||
|
'PHUIHovercardUIExample' => 'applications/uiexample/examples/PHUIHovercardUIExample.php',
|
||||||
|
'PHUIHovercardView' => 'view/phui/PHUIHovercardView.php',
|
||||||
'PHUIIconCircleView' => 'view/phui/PHUIIconCircleView.php',
|
'PHUIIconCircleView' => 'view/phui/PHUIIconCircleView.php',
|
||||||
'PHUIIconExample' => 'applications/uiexample/examples/PHUIIconExample.php',
|
'PHUIIconExample' => 'applications/uiexample/examples/PHUIIconExample.php',
|
||||||
'PHUIIconView' => 'view/phui/PHUIIconView.php',
|
'PHUIIconView' => 'view/phui/PHUIIconView.php',
|
||||||
|
@ -1585,6 +1587,7 @@ phutil_register_library_map(array(
|
||||||
'PasteSearchConduitAPIMethod' => 'applications/paste/conduit/PasteSearchConduitAPIMethod.php',
|
'PasteSearchConduitAPIMethod' => 'applications/paste/conduit/PasteSearchConduitAPIMethod.php',
|
||||||
'PeopleBrowseUserDirectoryCapability' => 'applications/people/capability/PeopleBrowseUserDirectoryCapability.php',
|
'PeopleBrowseUserDirectoryCapability' => 'applications/people/capability/PeopleBrowseUserDirectoryCapability.php',
|
||||||
'PeopleCreateUsersCapability' => 'applications/people/capability/PeopleCreateUsersCapability.php',
|
'PeopleCreateUsersCapability' => 'applications/people/capability/PeopleCreateUsersCapability.php',
|
||||||
|
'PeopleHovercardEngineExtension' => 'applications/people/engineextension/PeopleHovercardEngineExtension.php',
|
||||||
'PeopleUserLogGarbageCollector' => 'applications/people/garbagecollector/PeopleUserLogGarbageCollector.php',
|
'PeopleUserLogGarbageCollector' => 'applications/people/garbagecollector/PeopleUserLogGarbageCollector.php',
|
||||||
'Phabricator404Controller' => 'applications/base/controller/Phabricator404Controller.php',
|
'Phabricator404Controller' => 'applications/base/controller/Phabricator404Controller.php',
|
||||||
'PhabricatorAWSConfigOptions' => 'applications/config/option/PhabricatorAWSConfigOptions.php',
|
'PhabricatorAWSConfigOptions' => 'applications/config/option/PhabricatorAWSConfigOptions.php',
|
||||||
|
@ -1811,6 +1814,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorBcryptPasswordHasher' => 'infrastructure/util/password/PhabricatorBcryptPasswordHasher.php',
|
'PhabricatorBcryptPasswordHasher' => 'infrastructure/util/password/PhabricatorBcryptPasswordHasher.php',
|
||||||
'PhabricatorBinariesSetupCheck' => 'applications/config/check/PhabricatorBinariesSetupCheck.php',
|
'PhabricatorBinariesSetupCheck' => 'applications/config/check/PhabricatorBinariesSetupCheck.php',
|
||||||
'PhabricatorBitbucketAuthProvider' => 'applications/auth/provider/PhabricatorBitbucketAuthProvider.php',
|
'PhabricatorBitbucketAuthProvider' => 'applications/auth/provider/PhabricatorBitbucketAuthProvider.php',
|
||||||
|
'PhabricatorBoardLayoutEngine' => 'applications/project/engine/PhabricatorBoardLayoutEngine.php',
|
||||||
'PhabricatorBot' => 'infrastructure/daemon/bot/PhabricatorBot.php',
|
'PhabricatorBot' => 'infrastructure/daemon/bot/PhabricatorBot.php',
|
||||||
'PhabricatorBotChannel' => 'infrastructure/daemon/bot/target/PhabricatorBotChannel.php',
|
'PhabricatorBotChannel' => 'infrastructure/daemon/bot/target/PhabricatorBotChannel.php',
|
||||||
'PhabricatorBotDebugLogHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotDebugLogHandler.php',
|
'PhabricatorBotDebugLogHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotDebugLogHandler.php',
|
||||||
|
@ -1885,6 +1889,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorChatLogQuery' => 'applications/chatlog/query/PhabricatorChatLogQuery.php',
|
'PhabricatorChatLogQuery' => 'applications/chatlog/query/PhabricatorChatLogQuery.php',
|
||||||
'PhabricatorChunkedFileStorageEngine' => 'applications/files/engine/PhabricatorChunkedFileStorageEngine.php',
|
'PhabricatorChunkedFileStorageEngine' => 'applications/files/engine/PhabricatorChunkedFileStorageEngine.php',
|
||||||
'PhabricatorClusterConfigOptions' => 'applications/config/option/PhabricatorClusterConfigOptions.php',
|
'PhabricatorClusterConfigOptions' => 'applications/config/option/PhabricatorClusterConfigOptions.php',
|
||||||
|
'PhabricatorColumnProxyInterface' => 'applications/project/interface/PhabricatorColumnProxyInterface.php',
|
||||||
'PhabricatorCommentEditEngineExtension' => 'applications/transactions/engineextension/PhabricatorCommentEditEngineExtension.php',
|
'PhabricatorCommentEditEngineExtension' => 'applications/transactions/engineextension/PhabricatorCommentEditEngineExtension.php',
|
||||||
'PhabricatorCommentEditField' => 'applications/transactions/editfield/PhabricatorCommentEditField.php',
|
'PhabricatorCommentEditField' => 'applications/transactions/editfield/PhabricatorCommentEditField.php',
|
||||||
'PhabricatorCommentEditType' => 'applications/transactions/edittype/PhabricatorCommentEditType.php',
|
'PhabricatorCommentEditType' => 'applications/transactions/edittype/PhabricatorCommentEditType.php',
|
||||||
|
@ -2388,8 +2393,6 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorHomeQuickCreateController' => 'applications/home/controller/PhabricatorHomeQuickCreateController.php',
|
'PhabricatorHomeQuickCreateController' => 'applications/home/controller/PhabricatorHomeQuickCreateController.php',
|
||||||
'PhabricatorHovercardEngineExtension' => 'applications/search/engineextension/PhabricatorHovercardEngineExtension.php',
|
'PhabricatorHovercardEngineExtension' => 'applications/search/engineextension/PhabricatorHovercardEngineExtension.php',
|
||||||
'PhabricatorHovercardEngineExtensionModule' => 'applications/search/engineextension/PhabricatorHovercardEngineExtensionModule.php',
|
'PhabricatorHovercardEngineExtensionModule' => 'applications/search/engineextension/PhabricatorHovercardEngineExtensionModule.php',
|
||||||
'PhabricatorHovercardUIExample' => 'applications/uiexample/examples/PhabricatorHovercardUIExample.php',
|
|
||||||
'PhabricatorHovercardView' => 'view/widget/hovercard/PhabricatorHovercardView.php',
|
|
||||||
'PhabricatorHunksManagementMigrateWorkflow' => 'applications/differential/management/PhabricatorHunksManagementMigrateWorkflow.php',
|
'PhabricatorHunksManagementMigrateWorkflow' => 'applications/differential/management/PhabricatorHunksManagementMigrateWorkflow.php',
|
||||||
'PhabricatorHunksManagementWorkflow' => 'applications/differential/management/PhabricatorHunksManagementWorkflow.php',
|
'PhabricatorHunksManagementWorkflow' => 'applications/differential/management/PhabricatorHunksManagementWorkflow.php',
|
||||||
'PhabricatorIDsSearchEngineExtension' => 'applications/search/engineextension/PhabricatorIDsSearchEngineExtension.php',
|
'PhabricatorIDsSearchEngineExtension' => 'applications/search/engineextension/PhabricatorIDsSearchEngineExtension.php',
|
||||||
|
@ -2735,7 +2738,6 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPeopleDisableController' => 'applications/people/controller/PhabricatorPeopleDisableController.php',
|
'PhabricatorPeopleDisableController' => 'applications/people/controller/PhabricatorPeopleDisableController.php',
|
||||||
'PhabricatorPeopleEmpowerController' => 'applications/people/controller/PhabricatorPeopleEmpowerController.php',
|
'PhabricatorPeopleEmpowerController' => 'applications/people/controller/PhabricatorPeopleEmpowerController.php',
|
||||||
'PhabricatorPeopleExternalPHIDType' => 'applications/people/phid/PhabricatorPeopleExternalPHIDType.php',
|
'PhabricatorPeopleExternalPHIDType' => 'applications/people/phid/PhabricatorPeopleExternalPHIDType.php',
|
||||||
'PhabricatorPeopleHovercardEngineExtension' => 'applications/people/engineextension/PhabricatorPeopleHovercardEngineExtension.php',
|
|
||||||
'PhabricatorPeopleIconSet' => 'applications/people/icon/PhabricatorPeopleIconSet.php',
|
'PhabricatorPeopleIconSet' => 'applications/people/icon/PhabricatorPeopleIconSet.php',
|
||||||
'PhabricatorPeopleInviteController' => 'applications/people/controller/PhabricatorPeopleInviteController.php',
|
'PhabricatorPeopleInviteController' => 'applications/people/controller/PhabricatorPeopleInviteController.php',
|
||||||
'PhabricatorPeopleInviteListController' => 'applications/people/controller/PhabricatorPeopleInviteListController.php',
|
'PhabricatorPeopleInviteListController' => 'applications/people/controller/PhabricatorPeopleInviteListController.php',
|
||||||
|
@ -2860,6 +2862,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectBoardImportController' => 'applications/project/controller/PhabricatorProjectBoardImportController.php',
|
'PhabricatorProjectBoardImportController' => 'applications/project/controller/PhabricatorProjectBoardImportController.php',
|
||||||
'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php',
|
'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php',
|
||||||
'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php',
|
'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php',
|
||||||
|
'PhabricatorProjectCardView' => 'applications/project/view/PhabricatorProjectCardView.php',
|
||||||
'PhabricatorProjectColorsConfigOptionType' => 'applications/project/config/PhabricatorProjectColorsConfigOptionType.php',
|
'PhabricatorProjectColorsConfigOptionType' => 'applications/project/config/PhabricatorProjectColorsConfigOptionType.php',
|
||||||
'PhabricatorProjectColumn' => 'applications/project/storage/PhabricatorProjectColumn.php',
|
'PhabricatorProjectColumn' => 'applications/project/storage/PhabricatorProjectColumn.php',
|
||||||
'PhabricatorProjectColumnDetailController' => 'applications/project/controller/PhabricatorProjectColumnDetailController.php',
|
'PhabricatorProjectColumnDetailController' => 'applications/project/controller/PhabricatorProjectColumnDetailController.php',
|
||||||
|
@ -2913,7 +2916,6 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectMembersProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectMembersProfilePanel.php',
|
'PhabricatorProjectMembersProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectMembersProfilePanel.php',
|
||||||
'PhabricatorProjectMembersRemoveController' => 'applications/project/controller/PhabricatorProjectMembersRemoveController.php',
|
'PhabricatorProjectMembersRemoveController' => 'applications/project/controller/PhabricatorProjectMembersRemoveController.php',
|
||||||
'PhabricatorProjectMembersViewController' => 'applications/project/controller/PhabricatorProjectMembersViewController.php',
|
'PhabricatorProjectMembersViewController' => 'applications/project/controller/PhabricatorProjectMembersViewController.php',
|
||||||
'PhabricatorProjectMilestonesController' => 'applications/project/controller/PhabricatorProjectMilestonesController.php',
|
|
||||||
'PhabricatorProjectMoveController' => 'applications/project/controller/PhabricatorProjectMoveController.php',
|
'PhabricatorProjectMoveController' => 'applications/project/controller/PhabricatorProjectMoveController.php',
|
||||||
'PhabricatorProjectNameContextFreeGrammar' => 'applications/project/lipsum/PhabricatorProjectNameContextFreeGrammar.php',
|
'PhabricatorProjectNameContextFreeGrammar' => 'applications/project/lipsum/PhabricatorProjectNameContextFreeGrammar.php',
|
||||||
'PhabricatorProjectNoProjectsDatasource' => 'applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php',
|
'PhabricatorProjectNoProjectsDatasource' => 'applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php',
|
||||||
|
@ -2937,7 +2939,9 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectSlug' => 'applications/project/storage/PhabricatorProjectSlug.php',
|
'PhabricatorProjectSlug' => 'applications/project/storage/PhabricatorProjectSlug.php',
|
||||||
'PhabricatorProjectStandardCustomField' => 'applications/project/customfield/PhabricatorProjectStandardCustomField.php',
|
'PhabricatorProjectStandardCustomField' => 'applications/project/customfield/PhabricatorProjectStandardCustomField.php',
|
||||||
'PhabricatorProjectStatus' => 'applications/project/constants/PhabricatorProjectStatus.php',
|
'PhabricatorProjectStatus' => 'applications/project/constants/PhabricatorProjectStatus.php',
|
||||||
|
'PhabricatorProjectSubprojectWarningController' => 'applications/project/controller/PhabricatorProjectSubprojectWarningController.php',
|
||||||
'PhabricatorProjectSubprojectsController' => 'applications/project/controller/PhabricatorProjectSubprojectsController.php',
|
'PhabricatorProjectSubprojectsController' => 'applications/project/controller/PhabricatorProjectSubprojectsController.php',
|
||||||
|
'PhabricatorProjectSubprojectsProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectSubprojectsProfilePanel.php',
|
||||||
'PhabricatorProjectTestDataGenerator' => 'applications/project/lipsum/PhabricatorProjectTestDataGenerator.php',
|
'PhabricatorProjectTestDataGenerator' => 'applications/project/lipsum/PhabricatorProjectTestDataGenerator.php',
|
||||||
'PhabricatorProjectTransaction' => 'applications/project/storage/PhabricatorProjectTransaction.php',
|
'PhabricatorProjectTransaction' => 'applications/project/storage/PhabricatorProjectTransaction.php',
|
||||||
'PhabricatorProjectTransactionEditor' => 'applications/project/editor/PhabricatorProjectTransactionEditor.php',
|
'PhabricatorProjectTransactionEditor' => 'applications/project/editor/PhabricatorProjectTransactionEditor.php',
|
||||||
|
@ -3813,6 +3817,7 @@ phutil_register_library_map(array(
|
||||||
'ProjectDefaultJoinCapability' => 'applications/project/capability/ProjectDefaultJoinCapability.php',
|
'ProjectDefaultJoinCapability' => 'applications/project/capability/ProjectDefaultJoinCapability.php',
|
||||||
'ProjectDefaultViewCapability' => 'applications/project/capability/ProjectDefaultViewCapability.php',
|
'ProjectDefaultViewCapability' => 'applications/project/capability/ProjectDefaultViewCapability.php',
|
||||||
'ProjectEditConduitAPIMethod' => 'applications/project/conduit/ProjectEditConduitAPIMethod.php',
|
'ProjectEditConduitAPIMethod' => 'applications/project/conduit/ProjectEditConduitAPIMethod.php',
|
||||||
|
'ProjectHovercardEngineExtension' => 'applications/project/events/ProjectHovercardEngineExtension.php',
|
||||||
'ProjectQueryConduitAPIMethod' => 'applications/project/conduit/ProjectQueryConduitAPIMethod.php',
|
'ProjectQueryConduitAPIMethod' => 'applications/project/conduit/ProjectQueryConduitAPIMethod.php',
|
||||||
'ProjectRemarkupRule' => 'applications/project/remarkup/ProjectRemarkupRule.php',
|
'ProjectRemarkupRule' => 'applications/project/remarkup/ProjectRemarkupRule.php',
|
||||||
'ProjectRemarkupRuleTestCase' => 'applications/project/remarkup/__tests__/ProjectRemarkupRuleTestCase.php',
|
'ProjectRemarkupRuleTestCase' => 'applications/project/remarkup/__tests__/ProjectRemarkupRuleTestCase.php',
|
||||||
|
@ -5668,6 +5673,8 @@ phutil_register_library_map(array(
|
||||||
'PHUIHandleTagListView' => 'AphrontTagView',
|
'PHUIHandleTagListView' => 'AphrontTagView',
|
||||||
'PHUIHandleView' => 'AphrontView',
|
'PHUIHandleView' => 'AphrontView',
|
||||||
'PHUIHeaderView' => 'AphrontTagView',
|
'PHUIHeaderView' => 'AphrontTagView',
|
||||||
|
'PHUIHovercardUIExample' => 'PhabricatorUIExample',
|
||||||
|
'PHUIHovercardView' => 'AphrontTagView',
|
||||||
'PHUIIconCircleView' => 'AphrontTagView',
|
'PHUIIconCircleView' => 'AphrontTagView',
|
||||||
'PHUIIconExample' => 'PhabricatorUIExample',
|
'PHUIIconExample' => 'PhabricatorUIExample',
|
||||||
'PHUIIconView' => 'AphrontTagView',
|
'PHUIIconView' => 'AphrontTagView',
|
||||||
|
@ -5769,6 +5776,7 @@ phutil_register_library_map(array(
|
||||||
'PasteSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
'PasteSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
||||||
'PeopleBrowseUserDirectoryCapability' => 'PhabricatorPolicyCapability',
|
'PeopleBrowseUserDirectoryCapability' => 'PhabricatorPolicyCapability',
|
||||||
'PeopleCreateUsersCapability' => 'PhabricatorPolicyCapability',
|
'PeopleCreateUsersCapability' => 'PhabricatorPolicyCapability',
|
||||||
|
'PeopleHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
|
||||||
'PeopleUserLogGarbageCollector' => 'PhabricatorGarbageCollector',
|
'PeopleUserLogGarbageCollector' => 'PhabricatorGarbageCollector',
|
||||||
'Phabricator404Controller' => 'PhabricatorController',
|
'Phabricator404Controller' => 'PhabricatorController',
|
||||||
'PhabricatorAWSConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
'PhabricatorAWSConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||||
|
@ -6034,6 +6042,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorBcryptPasswordHasher' => 'PhabricatorPasswordHasher',
|
'PhabricatorBcryptPasswordHasher' => 'PhabricatorPasswordHasher',
|
||||||
'PhabricatorBinariesSetupCheck' => 'PhabricatorSetupCheck',
|
'PhabricatorBinariesSetupCheck' => 'PhabricatorSetupCheck',
|
||||||
'PhabricatorBitbucketAuthProvider' => 'PhabricatorOAuth1AuthProvider',
|
'PhabricatorBitbucketAuthProvider' => 'PhabricatorOAuth1AuthProvider',
|
||||||
|
'PhabricatorBoardLayoutEngine' => 'Phobject',
|
||||||
'PhabricatorBot' => 'PhabricatorDaemon',
|
'PhabricatorBot' => 'PhabricatorDaemon',
|
||||||
'PhabricatorBotChannel' => 'PhabricatorBotTarget',
|
'PhabricatorBotChannel' => 'PhabricatorBotTarget',
|
||||||
'PhabricatorBotDebugLogHandler' => 'PhabricatorBotHandler',
|
'PhabricatorBotDebugLogHandler' => 'PhabricatorBotHandler',
|
||||||
|
@ -6716,8 +6725,6 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorHomeQuickCreateController' => 'PhabricatorHomeController',
|
'PhabricatorHomeQuickCreateController' => 'PhabricatorHomeController',
|
||||||
'PhabricatorHovercardEngineExtension' => 'Phobject',
|
'PhabricatorHovercardEngineExtension' => 'Phobject',
|
||||||
'PhabricatorHovercardEngineExtensionModule' => 'PhabricatorConfigModule',
|
'PhabricatorHovercardEngineExtensionModule' => 'PhabricatorConfigModule',
|
||||||
'PhabricatorHovercardUIExample' => 'PhabricatorUIExample',
|
|
||||||
'PhabricatorHovercardView' => 'AphrontView',
|
|
||||||
'PhabricatorHunksManagementMigrateWorkflow' => 'PhabricatorHunksManagementWorkflow',
|
'PhabricatorHunksManagementMigrateWorkflow' => 'PhabricatorHunksManagementWorkflow',
|
||||||
'PhabricatorHunksManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
'PhabricatorHunksManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||||
'PhabricatorIDsSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
|
'PhabricatorIDsSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
|
||||||
|
@ -7108,7 +7115,6 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPeopleDisableController' => 'PhabricatorPeopleController',
|
'PhabricatorPeopleDisableController' => 'PhabricatorPeopleController',
|
||||||
'PhabricatorPeopleEmpowerController' => 'PhabricatorPeopleController',
|
'PhabricatorPeopleEmpowerController' => 'PhabricatorPeopleController',
|
||||||
'PhabricatorPeopleExternalPHIDType' => 'PhabricatorPHIDType',
|
'PhabricatorPeopleExternalPHIDType' => 'PhabricatorPHIDType',
|
||||||
'PhabricatorPeopleHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
|
|
||||||
'PhabricatorPeopleIconSet' => 'PhabricatorIconSet',
|
'PhabricatorPeopleIconSet' => 'PhabricatorIconSet',
|
||||||
'PhabricatorPeopleInviteController' => 'PhabricatorPeopleController',
|
'PhabricatorPeopleInviteController' => 'PhabricatorPeopleController',
|
||||||
'PhabricatorPeopleInviteListController' => 'PhabricatorPeopleInviteController',
|
'PhabricatorPeopleInviteListController' => 'PhabricatorPeopleInviteController',
|
||||||
|
@ -7259,6 +7265,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorDestructibleInterface',
|
'PhabricatorDestructibleInterface',
|
||||||
'PhabricatorFulltextInterface',
|
'PhabricatorFulltextInterface',
|
||||||
'PhabricatorConduitResultInterface',
|
'PhabricatorConduitResultInterface',
|
||||||
|
'PhabricatorColumnProxyInterface',
|
||||||
),
|
),
|
||||||
'PhabricatorProjectAddHeraldAction' => 'PhabricatorProjectHeraldAction',
|
'PhabricatorProjectAddHeraldAction' => 'PhabricatorProjectHeraldAction',
|
||||||
'PhabricatorProjectApplication' => 'PhabricatorApplication',
|
'PhabricatorProjectApplication' => 'PhabricatorApplication',
|
||||||
|
@ -7267,6 +7274,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectBoardImportController' => 'PhabricatorProjectBoardController',
|
'PhabricatorProjectBoardImportController' => 'PhabricatorProjectBoardController',
|
||||||
'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController',
|
'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController',
|
||||||
'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController',
|
'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController',
|
||||||
|
'PhabricatorProjectCardView' => 'AphrontTagView',
|
||||||
'PhabricatorProjectColorsConfigOptionType' => 'PhabricatorConfigJSONOptionType',
|
'PhabricatorProjectColorsConfigOptionType' => 'PhabricatorConfigJSONOptionType',
|
||||||
'PhabricatorProjectColumn' => array(
|
'PhabricatorProjectColumn' => array(
|
||||||
'PhabricatorProjectDAO',
|
'PhabricatorProjectDAO',
|
||||||
|
@ -7330,7 +7338,6 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectMembersProfilePanel' => 'PhabricatorProfilePanel',
|
'PhabricatorProjectMembersProfilePanel' => 'PhabricatorProfilePanel',
|
||||||
'PhabricatorProjectMembersRemoveController' => 'PhabricatorProjectController',
|
'PhabricatorProjectMembersRemoveController' => 'PhabricatorProjectController',
|
||||||
'PhabricatorProjectMembersViewController' => 'PhabricatorProjectController',
|
'PhabricatorProjectMembersViewController' => 'PhabricatorProjectController',
|
||||||
'PhabricatorProjectMilestonesController' => 'PhabricatorProjectController',
|
|
||||||
'PhabricatorProjectMoveController' => 'PhabricatorProjectController',
|
'PhabricatorProjectMoveController' => 'PhabricatorProjectController',
|
||||||
'PhabricatorProjectNameContextFreeGrammar' => 'PhutilContextFreeGrammar',
|
'PhabricatorProjectNameContextFreeGrammar' => 'PhutilContextFreeGrammar',
|
||||||
'PhabricatorProjectNoProjectsDatasource' => 'PhabricatorTypeaheadDatasource',
|
'PhabricatorProjectNoProjectsDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||||
|
@ -7357,7 +7364,9 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorStandardCustomFieldInterface',
|
'PhabricatorStandardCustomFieldInterface',
|
||||||
),
|
),
|
||||||
'PhabricatorProjectStatus' => 'Phobject',
|
'PhabricatorProjectStatus' => 'Phobject',
|
||||||
|
'PhabricatorProjectSubprojectWarningController' => 'PhabricatorProjectController',
|
||||||
'PhabricatorProjectSubprojectsController' => 'PhabricatorProjectController',
|
'PhabricatorProjectSubprojectsController' => 'PhabricatorProjectController',
|
||||||
|
'PhabricatorProjectSubprojectsProfilePanel' => 'PhabricatorProfilePanel',
|
||||||
'PhabricatorProjectTestDataGenerator' => 'PhabricatorTestDataGenerator',
|
'PhabricatorProjectTestDataGenerator' => 'PhabricatorTestDataGenerator',
|
||||||
'PhabricatorProjectTransaction' => 'PhabricatorApplicationTransaction',
|
'PhabricatorProjectTransaction' => 'PhabricatorApplicationTransaction',
|
||||||
'PhabricatorProjectTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
|
'PhabricatorProjectTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||||
|
@ -8443,6 +8452,7 @@ phutil_register_library_map(array(
|
||||||
'ProjectDefaultJoinCapability' => 'PhabricatorPolicyCapability',
|
'ProjectDefaultJoinCapability' => 'PhabricatorPolicyCapability',
|
||||||
'ProjectDefaultViewCapability' => 'PhabricatorPolicyCapability',
|
'ProjectDefaultViewCapability' => 'PhabricatorPolicyCapability',
|
||||||
'ProjectEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
|
'ProjectEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
|
||||||
|
'ProjectHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
|
||||||
'ProjectQueryConduitAPIMethod' => 'ProjectConduitAPIMethod',
|
'ProjectQueryConduitAPIMethod' => 'ProjectConduitAPIMethod',
|
||||||
'ProjectRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
'ProjectRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
||||||
'ProjectRemarkupRuleTestCase' => 'PhabricatorTestCase',
|
'ProjectRemarkupRuleTestCase' => 'PhabricatorTestCase',
|
||||||
|
|
|
@ -542,8 +542,12 @@ final class AphrontRequest extends Phobject {
|
||||||
return $this->isFormPost() && $this->getStr('__dialog__');
|
return $this->isFormPost() && $this->getStr('__dialog__');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRemoteAddr() {
|
public function getRemoteAddress() {
|
||||||
return $_SERVER['REMOTE_ADDR'];
|
$address = $_SERVER['REMOTE_ADDR'];
|
||||||
|
if (!strlen($address)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return substr($address, 0, 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isHTTPS() {
|
public function isHTTPS() {
|
||||||
|
|
|
@ -66,7 +66,7 @@ final class CelerityResourceTransformer extends Phobject {
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some resources won't survive minification (like Raphael.js), and are
|
// Some resources won't survive minification (like d3.min.js), and are
|
||||||
// marked so as not to be minified.
|
// marked so as not to be minified.
|
||||||
if (strpos($data, '@'.'do-not-minify') !== false) {
|
if (strpos($data, '@'.'do-not-minify') !== false) {
|
||||||
return $data;
|
return $data;
|
||||||
|
|
|
@ -322,6 +322,7 @@ abstract class PhabricatorConfigSchemaSpec extends Phobject {
|
||||||
case 'phid':
|
case 'phid':
|
||||||
case 'policy';
|
case 'policy';
|
||||||
case 'hashpath64':
|
case 'hashpath64':
|
||||||
|
case 'ipaddress':
|
||||||
$column_type = 'varbinary(64)';
|
$column_type = 'varbinary(64)';
|
||||||
break;
|
break;
|
||||||
case 'bytes64':
|
case 'bytes64':
|
||||||
|
|
|
@ -35,7 +35,7 @@ final class DifferentialHovercardEngineExtension
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderHovercard(
|
public function renderHovercard(
|
||||||
PhabricatorHovercardView $hovercard,
|
PHUIHovercardView $hovercard,
|
||||||
PhabricatorObjectHandle $handle,
|
PhabricatorObjectHandle $handle,
|
||||||
$object,
|
$object,
|
||||||
$data) {
|
$data) {
|
||||||
|
|
|
@ -1180,7 +1180,7 @@ final class DiffusionBrowseController extends DiffusionController {
|
||||||
|
|
||||||
if ($this->coverage) {
|
if ($this->coverage) {
|
||||||
require_celerity_resource('differential-changeset-view-css');
|
require_celerity_resource('differential-changeset-view-css');
|
||||||
$cov_index = $line['line'] - 1;
|
$cov_index = $line_index;
|
||||||
|
|
||||||
if (isset($this->coverage[$cov_index])) {
|
if (isset($this->coverage[$cov_index])) {
|
||||||
$cov_class = $this->coverage[$cov_index];
|
$cov_class = $this->coverage[$cov_index];
|
||||||
|
|
|
@ -76,8 +76,7 @@ final class DiffusionServeController extends DiffusionController {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$remote_addr = $request->getRemoteAddr();
|
$remote_addr = $request->getRemoteAddress();
|
||||||
$remote_addr = ip2long($remote_addr);
|
|
||||||
|
|
||||||
$pull_event = id(new PhabricatorRepositoryPullEvent())
|
$pull_event = id(new PhabricatorRepositoryPullEvent())
|
||||||
->setEpoch(PhabricatorTime::getNow())
|
->setEpoch(PhabricatorTime::getNow())
|
||||||
|
@ -720,11 +719,11 @@ final class DiffusionServeController extends DiffusionController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getCommonEnvironment(PhabricatorUser $viewer) {
|
private function getCommonEnvironment(PhabricatorUser $viewer) {
|
||||||
$remote_addr = $this->getRequest()->getRemoteAddr();
|
$remote_address = $this->getRequest()->getRemoteAddress();
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
DiffusionCommitHookEngine::ENV_USER => $viewer->getUsername(),
|
DiffusionCommitHookEngine::ENV_USER => $viewer->getUsername(),
|
||||||
DiffusionCommitHookEngine::ENV_REMOTE_ADDRESS => $remote_addr,
|
DiffusionCommitHookEngine::ENV_REMOTE_ADDRESS => $remote_address,
|
||||||
DiffusionCommitHookEngine::ENV_REMOTE_PROTOCOL => 'http',
|
DiffusionCommitHookEngine::ENV_REMOTE_PROTOCOL => 'http',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,15 +56,6 @@ final class DiffusionCommitHookEngine extends Phobject {
|
||||||
return $this->remoteAddress;
|
return $this->remoteAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getRemoteAddressForLog() {
|
|
||||||
// If whatever we have here isn't a valid IPv4 address, just store `null`.
|
|
||||||
// Older versions of PHP return `-1` on failure instead of `false`.
|
|
||||||
$remote_address = $this->getRemoteAddress();
|
|
||||||
$remote_address = max(0, ip2long($remote_address));
|
|
||||||
$remote_address = nonempty($remote_address, null);
|
|
||||||
return $remote_address;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setSubversionTransactionInfo($transaction, $repository) {
|
public function setSubversionTransactionInfo($transaction, $repository) {
|
||||||
$this->subversionTransaction = $transaction;
|
$this->subversionTransaction = $transaction;
|
||||||
$this->subversionRepository = $repository;
|
$this->subversionRepository = $repository;
|
||||||
|
@ -1078,7 +1069,7 @@ final class DiffusionCommitHookEngine extends Phobject {
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
return PhabricatorRepositoryPushEvent::initializeNewEvent($viewer)
|
return PhabricatorRepositoryPushEvent::initializeNewEvent($viewer)
|
||||||
->setRepositoryPHID($this->getRepository()->getPHID())
|
->setRepositoryPHID($this->getRepository()->getPHID())
|
||||||
->setRemoteAddress($this->getRemoteAddressForLog())
|
->setRemoteAddress($this->getRemoteAddress())
|
||||||
->setRemoteProtocol($this->getRemoteProtocol())
|
->setRemoteProtocol($this->getRemoteProtocol())
|
||||||
->setEpoch(time());
|
->setEpoch(time());
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ final class DiffusionHovercardEngineExtension
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderHovercard(
|
public function renderHovercard(
|
||||||
PhabricatorHovercardView $hovercard,
|
PHUIHovercardView $hovercard,
|
||||||
PhabricatorObjectHandle $handle,
|
PhabricatorObjectHandle $handle,
|
||||||
$commit,
|
$commit,
|
||||||
$data) {
|
$data) {
|
||||||
|
|
|
@ -47,7 +47,10 @@ final class DiffusionSymbolDatasource
|
||||||
->setPHID(md5($symbol->getURI())) // Just needs to be unique.
|
->setPHID(md5($symbol->getURI())) // Just needs to be unique.
|
||||||
->setDisplayName($name)
|
->setDisplayName($name)
|
||||||
->setDisplayType(strtoupper($lang).' '.ucwords($type).' ('.$repo.')')
|
->setDisplayType(strtoupper($lang).' '.ucwords($type).' ('.$repo.')')
|
||||||
->setPriorityType('symb');
|
->setPriorityType('symb')
|
||||||
|
->setImageSprite(
|
||||||
|
'phabricator-search-icon phui-font-fa phui-icon-view fa-code '.
|
||||||
|
'lightgreytext');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,12 +42,9 @@ final class DiffusionPushLogListView extends AphrontView {
|
||||||
$repository = $log->getRepository();
|
$repository = $log->getRepository();
|
||||||
|
|
||||||
// Reveal this if it's valid and the user can edit the repository.
|
// Reveal this if it's valid and the user can edit the repository.
|
||||||
$remote_addr = '-';
|
$remote_address = '-';
|
||||||
if (isset($editable_repos[$log->getRepositoryPHID()])) {
|
if (isset($editable_repos[$log->getRepositoryPHID()])) {
|
||||||
$remote_long = $log->getPushEvent()->getRemoteAddress();
|
$remote_address = $log->getPushEvent()->getRemoteAddress();
|
||||||
if ($remote_long) {
|
|
||||||
$remote_addr = long2ip($remote_long);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$event_id = $log->getPushEvent()->getID();
|
$event_id = $log->getPushEvent()->getID();
|
||||||
|
@ -76,7 +73,7 @@ final class DiffusionPushLogListView extends AphrontView {
|
||||||
),
|
),
|
||||||
$repository->getDisplayName()),
|
$repository->getDisplayName()),
|
||||||
$handles[$log->getPusherPHID()]->renderLink(),
|
$handles[$log->getPusherPHID()]->renderLink(),
|
||||||
$remote_addr,
|
$remote_address,
|
||||||
$log->getPushEvent()->getRemoteProtocol(),
|
$log->getPushEvent()->getRemoteProtocol(),
|
||||||
$log->getRefType(),
|
$log->getRefType(),
|
||||||
$log->getRefName(),
|
$log->getRefName(),
|
||||||
|
|
|
@ -30,8 +30,7 @@ final class PhabricatorFactChartController extends PhabricatorFactController {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$points) {
|
if (!$points) {
|
||||||
// NOTE: Raphael crashes Safari if you hand it series with no points.
|
throw new Exception('No data to show!');
|
||||||
throw new Exception(pht('No data to show!'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limit amount of data passed to browser.
|
// Limit amount of data passed to browser.
|
||||||
|
@ -56,16 +55,12 @@ final class PhabricatorFactChartController extends PhabricatorFactController {
|
||||||
'div',
|
'div',
|
||||||
array(
|
array(
|
||||||
'id' => $id,
|
'id' => $id,
|
||||||
'style' => 'border: 1px solid #6f6f6f; '.
|
'style' => 'background: #ffffff; '.
|
||||||
'margin: 1em 2em; '.
|
'height: 480px; ',
|
||||||
'background: #ffffff; '.
|
|
||||||
'height: 400px; ',
|
|
||||||
),
|
),
|
||||||
'');
|
'');
|
||||||
|
|
||||||
require_celerity_resource('raphael-core');
|
require_celerity_resource('d3');
|
||||||
require_celerity_resource('raphael-g');
|
|
||||||
require_celerity_resource('raphael-g-line');
|
|
||||||
|
|
||||||
Javelin::initBehavior('line-chart', array(
|
Javelin::initBehavior('line-chart', array(
|
||||||
'hardpoint' => $id,
|
'hardpoint' => $id,
|
||||||
|
@ -75,9 +70,9 @@ final class PhabricatorFactChartController extends PhabricatorFactController {
|
||||||
'colors' => array('#0000ff'),
|
'colors' => array('#0000ff'),
|
||||||
));
|
));
|
||||||
|
|
||||||
$panel = new PHUIObjectBoxView();
|
$box = id(new PHUIObjectBoxView())
|
||||||
$panel->setHeaderText(pht('Count of %s', $spec->getName()));
|
->setHeaderText(pht('Count of %s', $spec->getName()))
|
||||||
$panel->appendChild($chart);
|
->appendChild($chart);
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
$crumbs->addTextCrumb(pht('Chart'));
|
$crumbs->addTextCrumb(pht('Chart'));
|
||||||
|
@ -85,7 +80,7 @@ final class PhabricatorFactChartController extends PhabricatorFactController {
|
||||||
return $this->buildApplicationPage(
|
return $this->buildApplicationPage(
|
||||||
array(
|
array(
|
||||||
$crumbs,
|
$crumbs,
|
||||||
$panel,
|
$box,
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'title' => pht('Chart'),
|
'title' => pht('Chart'),
|
||||||
|
|
|
@ -179,6 +179,7 @@ final class PhabricatorFilesComposeIconBuiltinFile
|
||||||
'fa-legal' => pht('Hired Protection'),
|
'fa-legal' => pht('Hired Protection'),
|
||||||
'fa-linux' => pht('M\'Lady'),
|
'fa-linux' => pht('M\'Lady'),
|
||||||
'fa-lock' => pht('Policy'),
|
'fa-lock' => pht('Policy'),
|
||||||
|
'fa-map-marker' => pht('Destination Beacon'),
|
||||||
'fa-microphone' => pht('Podcasting'),
|
'fa-microphone' => pht('Podcasting'),
|
||||||
'fa-mobile' => pht('Tiny Pocket Cat Meme Machine'),
|
'fa-mobile' => pht('Tiny Pocket Cat Meme Machine'),
|
||||||
'fa-money' => pht('1 of 99 Problems'),
|
'fa-money' => pht('1 of 99 Problems'),
|
||||||
|
|
|
@ -92,6 +92,11 @@ abstract class PhabricatorFileUploadSource
|
||||||
|
|
||||||
$threshold = PhabricatorFileStorageEngine::getChunkThreshold();
|
$threshold = PhabricatorFileStorageEngine::getChunkThreshold();
|
||||||
|
|
||||||
|
if ($threshold === null) {
|
||||||
|
// If there are no chunk engines available, we clearly can't chunk the
|
||||||
|
// file.
|
||||||
|
$this->shouldChunk = false;
|
||||||
|
} else {
|
||||||
// If we don't know how large the file is, we're going to read some data
|
// If we don't know how large the file is, we're going to read some data
|
||||||
// from it until we know whether it's a small file or not. This will give
|
// from it until we know whether it's a small file or not. This will give
|
||||||
// us enough information to make a decision about chunking.
|
// us enough information to make a decision about chunking.
|
||||||
|
@ -107,6 +112,7 @@ abstract class PhabricatorFileUploadSource
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->shouldChunk = ($length > $threshold);
|
$this->shouldChunk = ($length > $threshold);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->shouldChunk;
|
return $this->shouldChunk;
|
||||||
}
|
}
|
||||||
|
|
|
@ -290,9 +290,8 @@ final class ManiphestReportController extends ManiphestController {
|
||||||
|
|
||||||
list($burn_x, $burn_y) = $this->buildSeries($data);
|
list($burn_x, $burn_y) = $this->buildSeries($data);
|
||||||
|
|
||||||
require_celerity_resource('raphael-core');
|
require_celerity_resource('d3');
|
||||||
require_celerity_resource('raphael-g');
|
require_celerity_resource('phui-chart-css');
|
||||||
require_celerity_resource('raphael-g-line');
|
|
||||||
|
|
||||||
Javelin::initBehavior('line-chart', array(
|
Javelin::initBehavior('line-chart', array(
|
||||||
'hardpoint' => $id,
|
'hardpoint' => $id,
|
||||||
|
@ -306,7 +305,11 @@ final class ManiphestReportController extends ManiphestController {
|
||||||
'yformat' => 'int',
|
'yformat' => 'int',
|
||||||
));
|
));
|
||||||
|
|
||||||
return array($filter, $chart, $panel);
|
$box = id(new PHUIObjectBoxView())
|
||||||
|
->setHeaderText(pht('Burnup Rate'))
|
||||||
|
->appendChild($chart);
|
||||||
|
|
||||||
|
return array($filter, $box, $panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function renderReportFilters(array $tokens, $has_window) {
|
private function renderReportFilters(array $tokens, $has_window) {
|
||||||
|
|
|
@ -280,20 +280,35 @@ final class ManiphestEditEngine
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the workboard's project has been removed from the card's project
|
// If the workboard's project and all descendant projects have been removed
|
||||||
// list, we are going to remove it from the board completely.
|
// from the card's project list, we are going to remove it from the board
|
||||||
|
// completely.
|
||||||
|
|
||||||
|
// TODO: If the user did something sneaky and changed a subproject, we'll
|
||||||
|
// currently leave the card where it was but should really move it to the
|
||||||
|
// proper new column.
|
||||||
|
|
||||||
|
$descendant_projects = id(new PhabricatorProjectQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withAncestorProjectPHIDs(array($column->getProjectPHID()))
|
||||||
|
->execute();
|
||||||
|
$board_phids = mpull($descendant_projects, 'getPHID', 'getPHID');
|
||||||
|
$board_phids[$column->getProjectPHID()] = $column->getProjectPHID();
|
||||||
|
|
||||||
$project_map = array_fuse($task->getProjectPHIDs());
|
$project_map = array_fuse($task->getProjectPHIDs());
|
||||||
$remove_card = empty($project_map[$column->getProjectPHID()]);
|
$remove_card = !array_intersect_key($board_phids, $project_map);
|
||||||
|
|
||||||
$positions = id(new PhabricatorProjectColumnPositionQuery())
|
$positions = id(new PhabricatorProjectColumnPositionQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withColumns(array($column))
|
->withBoardPHIDs(array($column->getProjectPHID()))
|
||||||
|
->withColumnPHIDs(array($column->getPHID()))
|
||||||
->execute();
|
->execute();
|
||||||
$task_phids = mpull($positions, 'getObjectPHID');
|
$task_phids = mpull($positions, 'getObjectPHID');
|
||||||
|
|
||||||
$column_tasks = id(new ManiphestTaskQuery())
|
$column_tasks = id(new ManiphestTaskQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withPHIDs($task_phids)
|
->withPHIDs($task_phids)
|
||||||
|
->needProjectPHIDs(true)
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
if ($order == PhabricatorProjectColumn::ORDER_NATURAL) {
|
if ($order == PhabricatorProjectColumn::ORDER_NATURAL) {
|
||||||
|
@ -329,13 +344,23 @@ final class ManiphestEditEngine
|
||||||
->executeOne();
|
->executeOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$handle_phids = $task->getProjectPHIDs();
|
||||||
|
$handle_phids = array_fuse($handle_phids);
|
||||||
|
$handle_phids = array_diff_key($handle_phids, $board_phids);
|
||||||
|
|
||||||
|
$project_handles = $viewer->loadHandles($handle_phids);
|
||||||
|
$project_handles = iterator_to_array($project_handles);
|
||||||
|
|
||||||
$tasks = id(new ProjectBoardTaskCard())
|
$tasks = id(new ProjectBoardTaskCard())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->setTask($task)
|
->setTask($task)
|
||||||
->setOwner($owner)
|
->setOwner($owner)
|
||||||
|
->setProjectHandles($project_handles)
|
||||||
->setCanEdit(true)
|
->setCanEdit(true)
|
||||||
->getItem();
|
->getItem();
|
||||||
|
|
||||||
|
$tasks->addClass('phui-workcard');
|
||||||
|
|
||||||
$payload = array(
|
$payload = array(
|
||||||
'tasks' => $tasks,
|
'tasks' => $tasks,
|
||||||
'data' => $data,
|
'data' => $data,
|
||||||
|
|
|
@ -200,18 +200,6 @@ final class ManiphestTransactionEditor
|
||||||
'columnPHIDs'));
|
'columnPHIDs'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$columns = id(new PhabricatorProjectColumnQuery())
|
|
||||||
->setViewer($this->requireActor())
|
|
||||||
->withPHIDs($new_phids)
|
|
||||||
->execute();
|
|
||||||
$columns = mpull($columns, null, 'getPHID');
|
|
||||||
|
|
||||||
$positions = id(new PhabricatorProjectColumnPositionQuery())
|
|
||||||
->setViewer($this->requireActor())
|
|
||||||
->withObjectPHIDs(array($object->getPHID()))
|
|
||||||
->withBoardPHIDs(array($board_phid))
|
|
||||||
->execute();
|
|
||||||
|
|
||||||
$before_phid = idx($xaction->getNewValue(), 'beforePHID');
|
$before_phid = idx($xaction->getNewValue(), 'beforePHID');
|
||||||
$after_phid = idx($xaction->getNewValue(), 'afterPHID');
|
$after_phid = idx($xaction->getNewValue(), 'afterPHID');
|
||||||
|
|
||||||
|
@ -227,111 +215,86 @@ final class ManiphestTransactionEditor
|
||||||
// object's position in the "natural" ordering, so we do need to update
|
// object's position in the "natural" ordering, so we do need to update
|
||||||
// some rows.
|
// some rows.
|
||||||
|
|
||||||
// Remove all existing column positions on the board.
|
$object_phid = $object->getPHID();
|
||||||
|
|
||||||
foreach ($positions as $position) {
|
// We're doing layout with the ominpotent viewer to make sure we don't
|
||||||
$position->delete();
|
// remove positions in columns that exist, but which the actual actor
|
||||||
}
|
// can't see.
|
||||||
|
$omnipotent_viewer = PhabricatorUser::getOmnipotentUser();
|
||||||
|
|
||||||
// Add the new column positions.
|
$select_phids = array($board_phid);
|
||||||
|
|
||||||
foreach ($new_phids as $phid) {
|
$descendants = id(new PhabricatorProjectQuery())
|
||||||
$column = idx($columns, $phid);
|
->setViewer($omnipotent_viewer)
|
||||||
if (!$column) {
|
->withAncestorProjectPHIDs($select_phids)
|
||||||
throw new Exception(
|
|
||||||
pht('No such column "%s" exists!', $phid));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the other object positions in the column. Note that we must
|
|
||||||
// skip implicit column creation to avoid generating a new position
|
|
||||||
// if the target column is a backlog column.
|
|
||||||
|
|
||||||
$other_positions = id(new PhabricatorProjectColumnPositionQuery())
|
|
||||||
->setViewer($this->requireActor())
|
|
||||||
->withColumns(array($column))
|
|
||||||
->withBoardPHIDs(array($board_phid))
|
|
||||||
->setSkipImplicitCreate(true)
|
|
||||||
->execute();
|
->execute();
|
||||||
$other_positions = msort($other_positions, 'getOrderingKey');
|
foreach ($descendants as $descendant) {
|
||||||
|
$select_phids[] = $descendant->getPHID();
|
||||||
// Set up the new position object. We're going to figure out the
|
|
||||||
// right sequence number and then persist this object with that
|
|
||||||
// sequence number.
|
|
||||||
$new_position = id(new PhabricatorProjectColumnPosition())
|
|
||||||
->setBoardPHID($board_phid)
|
|
||||||
->setColumnPHID($column->getPHID())
|
|
||||||
->setObjectPHID($object->getPHID());
|
|
||||||
|
|
||||||
$updates = array();
|
|
||||||
$sequence = 0;
|
|
||||||
|
|
||||||
// If we're just dropping this into the column without any specific
|
|
||||||
// position information, put it at the top.
|
|
||||||
if (!$before_phid && !$after_phid) {
|
|
||||||
$new_position->setSequence($sequence)->save();
|
|
||||||
$sequence++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($other_positions as $position) {
|
$board_tasks = id(new ManiphestTaskQuery())
|
||||||
$object_phid = $position->getObjectPHID();
|
->setViewer($omnipotent_viewer)
|
||||||
|
->withEdgeLogicPHIDs(
|
||||||
|
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
|
||||||
|
PhabricatorQueryConstraint::OPERATOR_ANCESTOR,
|
||||||
|
array($select_phids))
|
||||||
|
->execute();
|
||||||
|
|
||||||
// If this is the object we're moving before and we haven't
|
$object_phids = mpull($board_tasks, 'getPHID');
|
||||||
// saved yet, insert here.
|
$object_phids[] = $object_phid;
|
||||||
if (($before_phid == $object_phid) && !$new_position->getID()) {
|
|
||||||
$new_position->setSequence($sequence)->save();
|
$engine = id(new PhabricatorBoardLayoutEngine())
|
||||||
$sequence++;
|
->setViewer($omnipotent_viewer)
|
||||||
|
->setBoardPHIDs(array($board_phid))
|
||||||
|
->setObjectPHIDs($object_phids)
|
||||||
|
->executeLayout();
|
||||||
|
|
||||||
|
// TODO: This logic needs to be revised if we legitimately support
|
||||||
|
// multiple column positions.
|
||||||
|
|
||||||
|
// NOTE: When a task is newly created, it's implicitly added to the
|
||||||
|
// backlog but we don't currently record that in the "$old_phids". Just
|
||||||
|
// clean it up for now.
|
||||||
|
$columns = $engine->getObjectColumns($board_phid, $object_phid);
|
||||||
|
foreach ($columns as $column) {
|
||||||
|
$engine->queueRemovePosition(
|
||||||
|
$board_phid,
|
||||||
|
$column->getPHID(),
|
||||||
|
$object_phid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This object goes here in the sequence; we might need to update
|
// Remove all existing column positions on the board.
|
||||||
// the row.
|
foreach ($old_phids as $column_phid) {
|
||||||
if ($sequence != $position->getSequence()) {
|
$engine->queueRemovePosition(
|
||||||
$updates[$position->getID()] = $sequence;
|
$board_phid,
|
||||||
|
$column_phid,
|
||||||
|
$object_phid);
|
||||||
}
|
}
|
||||||
$sequence++;
|
|
||||||
|
|
||||||
// If this is the object we're moving after and we haven't saved
|
// Add new positions.
|
||||||
// yet, insert here.
|
foreach ($new_phids as $column_phid) {
|
||||||
if (($after_phid == $object_phid) && !$new_position->getID()) {
|
if ($before_phid) {
|
||||||
$new_position->setSequence($sequence)->save();
|
$engine->queueAddPositionBefore(
|
||||||
$sequence++;
|
$board_phid,
|
||||||
|
$column_phid,
|
||||||
|
$object_phid,
|
||||||
|
$before_phid);
|
||||||
|
} else if ($after_phid) {
|
||||||
|
$engine->queueAddPositionAfter(
|
||||||
|
$board_phid,
|
||||||
|
$column_phid,
|
||||||
|
$object_phid,
|
||||||
|
$after_phid);
|
||||||
|
} else {
|
||||||
|
$engine->queueAddPosition(
|
||||||
|
$board_phid,
|
||||||
|
$column_phid,
|
||||||
|
$object_phid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should have found a place to put it.
|
$engine->applyPositionUpdates();
|
||||||
if (!$new_position->getID()) {
|
|
||||||
throw new Exception(
|
|
||||||
pht('Unable to find a place to insert object on column!'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we changed other objects' column positions, bulk reorder them.
|
|
||||||
|
|
||||||
if ($updates) {
|
|
||||||
$position = new PhabricatorProjectColumnPosition();
|
|
||||||
$conn_w = $position->establishConnection('w');
|
|
||||||
|
|
||||||
$pairs = array();
|
|
||||||
foreach ($updates as $id => $sequence) {
|
|
||||||
// This is ugly because MySQL gets upset with us if it is
|
|
||||||
// configured strictly and we attempt inserts which can't work.
|
|
||||||
// We'll never actually do these inserts since they'll always
|
|
||||||
// collide (triggering the ON DUPLICATE KEY logic), so we just
|
|
||||||
// provide dummy values in order to get there.
|
|
||||||
|
|
||||||
$pairs[] = qsprintf(
|
|
||||||
$conn_w,
|
|
||||||
'(%d, %d, "", "", "")',
|
|
||||||
$id,
|
|
||||||
$sequence);
|
|
||||||
}
|
|
||||||
|
|
||||||
queryfx(
|
|
||||||
$conn_w,
|
|
||||||
'INSERT INTO %T (id, sequence, boardPHID, columnPHID, objectPHID)
|
|
||||||
VALUES %Q ON DUPLICATE KEY UPDATE sequence = VALUES(sequence)',
|
|
||||||
$position->getTableName(),
|
|
||||||
implode(', ', $pairs));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -19,7 +19,7 @@ final class ManiphestHovercardEngineExtension
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderHovercard(
|
public function renderHovercard(
|
||||||
PhabricatorHovercardView $hovercard,
|
PHUIHovercardView $hovercard,
|
||||||
PhabricatorObjectHandle $handle,
|
PhabricatorObjectHandle $handle,
|
||||||
$task,
|
$task,
|
||||||
$data) {
|
$data) {
|
||||||
|
|
|
@ -54,9 +54,9 @@ final class PhabricatorPeopleProfileViewController
|
||||||
$feed = $this->buildPeopleFeed($user, $viewer);
|
$feed = $this->buildPeopleFeed($user, $viewer);
|
||||||
$feed = phutil_tag_div('project-view-feed', $feed);
|
$feed = phutil_tag_div('project-view-feed', $feed);
|
||||||
|
|
||||||
|
$projects = $this->buildProjectsView($user);
|
||||||
$badges = $this->buildBadgesView($user);
|
$badges = $this->buildBadgesView($user);
|
||||||
|
|
||||||
if ($badges) {
|
|
||||||
$columns = id(new PHUITwoColumnView())
|
$columns = id(new PHUITwoColumnView())
|
||||||
->addClass('project-view-badges')
|
->addClass('project-view-badges')
|
||||||
->setMainColumn(
|
->setMainColumn(
|
||||||
|
@ -66,11 +66,9 @@ final class PhabricatorPeopleProfileViewController
|
||||||
))
|
))
|
||||||
->setSideColumn(
|
->setSideColumn(
|
||||||
array(
|
array(
|
||||||
|
$projects,
|
||||||
$badges,
|
$badges,
|
||||||
));
|
));
|
||||||
} else {
|
|
||||||
$columns = array($properties, $feed);
|
|
||||||
}
|
|
||||||
|
|
||||||
$nav = $this->getProfileMenu();
|
$nav = $this->getProfileMenu();
|
||||||
$nav->selectFilter(PhabricatorPeopleProfilePanelEngine::PANEL_PROFILE);
|
$nav->selectFilter(PhabricatorPeopleProfilePanelEngine::PANEL_PROFILE);
|
||||||
|
@ -112,7 +110,7 @@ final class PhabricatorPeopleProfileViewController
|
||||||
PhabricatorCustomField::ROLE_VIEW);
|
PhabricatorCustomField::ROLE_VIEW);
|
||||||
$field_list->appendFieldsToPropertyList($user, $viewer, $view);
|
$field_list->appendFieldsToPropertyList($user, $viewer, $view);
|
||||||
|
|
||||||
if ($view->isEmpty()) {
|
if (!$view->hasAnyProperties()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,14 +122,69 @@ final class PhabricatorPeopleProfileViewController
|
||||||
return $view;
|
return $view;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildBadgesView(
|
private function buildProjectsView(
|
||||||
PhabricatorUser $user) {
|
PhabricatorUser $user) {
|
||||||
|
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
$class = 'PhabricatorBadgesApplication';
|
$projects = id(new PhabricatorProjectQuery())
|
||||||
$box = null;
|
->setViewer($viewer)
|
||||||
|
->withMemberPHIDs(array($user->getPHID()))
|
||||||
|
->needImages(true)
|
||||||
|
->withStatus(PhabricatorProjectQuery::STATUS_OPEN)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$header = id(new PHUIHeaderView())
|
||||||
|
->setHeader(pht('Projects'));
|
||||||
|
|
||||||
|
if (!empty($projects)) {
|
||||||
|
$limit = 5;
|
||||||
|
$render_phids = array_slice($projects, 0, $limit);
|
||||||
|
$list = id(new PhabricatorProjectListView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setProjects($render_phids);
|
||||||
|
|
||||||
|
if (count($projects) > $limit) {
|
||||||
|
$header_text = pht(
|
||||||
|
'Projects (%s)',
|
||||||
|
phutil_count($projects));
|
||||||
|
|
||||||
|
$header = id(new PHUIHeaderView())
|
||||||
|
->setHeader($header_text)
|
||||||
|
->addActionLink(
|
||||||
|
id(new PHUIButtonView())
|
||||||
|
->setTag('a')
|
||||||
|
->setIcon('fa-list-ul')
|
||||||
|
->setText(pht('View All'))
|
||||||
|
->setHref('/project/?member='.$user->getPHID()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$error = id(new PHUIBoxView())
|
||||||
|
->addClass('mlb')
|
||||||
|
->appendChild(pht('User does not belong to any projects.'));
|
||||||
|
$list = id(new PHUIInfoView())
|
||||||
|
->setSeverity(PHUIInfoView::SEVERITY_NODATA)
|
||||||
|
->appendChild($error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$box = id(new PHUIObjectBoxView())
|
||||||
|
->setHeader($header)
|
||||||
|
->appendChild($list)
|
||||||
|
->setBackground(PHUIBoxView::GREY);
|
||||||
|
|
||||||
|
return $box;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildBadgesView(PhabricatorUser $user) {
|
||||||
|
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$class = 'PhabricatorBadgesApplication';
|
||||||
|
|
||||||
|
if (!PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) {
|
|
||||||
$badge_phids = $user->getBadgePHIDs();
|
$badge_phids = $user->getBadgePHIDs();
|
||||||
if ($badge_phids) {
|
if ($badge_phids) {
|
||||||
$badges = id(new PhabricatorBadgesQuery())
|
$badges = id(new PhabricatorBadgesQuery())
|
||||||
|
@ -150,12 +203,19 @@ final class PhabricatorPeopleProfileViewController
|
||||||
$flex->addItem($item);
|
$flex->addItem($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$error = id(new PHUIBoxView())
|
||||||
|
->addClass('mlb')
|
||||||
|
->appendChild(pht('User does not have any badges.'));
|
||||||
|
$flex = id(new PHUIInfoView())
|
||||||
|
->setSeverity(PHUIInfoView::SEVERITY_NODATA)
|
||||||
|
->appendChild($error);
|
||||||
|
}
|
||||||
|
|
||||||
$box = id(new PHUIObjectBoxView())
|
$box = id(new PHUIObjectBoxView())
|
||||||
->setHeaderText(pht('Badges'))
|
->setHeaderText(pht('Badges'))
|
||||||
->appendChild($flex)
|
->appendChild($flex)
|
||||||
->setBackground(PHUIBoxView::GREY);
|
->setBackground(PHUIBoxView::GREY);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $box;
|
return $box;
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,13 @@ final class PhabricatorUserBlurbField
|
||||||
->setLabel($this->getFieldName());
|
->setLabel($this->getFieldName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getApplicationTransactionRemarkupBlocks(
|
||||||
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
return array(
|
||||||
|
$xaction->getNewValue(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function renderPropertyViewLabel() {
|
public function renderPropertyViewLabel() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -67,10 +74,11 @@ final class PhabricatorUserBlurbField
|
||||||
if (!strlen($blurb)) {
|
if (!strlen($blurb)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return PhabricatorMarkupEngine::renderOneObject(
|
|
||||||
id(new PhabricatorMarkupOneOff())->setContent($blurb),
|
$viewer = $this->getViewer();
|
||||||
'default',
|
$view = new PHUIRemarkupView($viewer, $blurb);
|
||||||
$this->getViewer());
|
|
||||||
|
return $view;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getStyleForPropertyView() {
|
public function getStyleForPropertyView() {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class PhabricatorPeopleHovercardEngineExtension
|
final class PeopleHovercardEngineExtension
|
||||||
extends PhabricatorHovercardEngineExtension {
|
extends PhabricatorHovercardEngineExtension {
|
||||||
|
|
||||||
const EXTENSIONKEY = 'people';
|
const EXTENSIONKEY = 'people';
|
||||||
|
@ -36,7 +36,7 @@ final class PhabricatorPeopleHovercardEngineExtension
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderHovercard(
|
public function renderHovercard(
|
||||||
PhabricatorHovercardView $hovercard,
|
PHUIHovercardView $hovercard,
|
||||||
PhabricatorObjectHandle $handle,
|
PhabricatorObjectHandle $handle,
|
||||||
$object,
|
$object,
|
||||||
$data) {
|
$data) {
|
|
@ -74,6 +74,26 @@ final class PhabricatorPeopleIconSet
|
||||||
'icon' => 'fa-heart',
|
'icon' => 'fa-heart',
|
||||||
'name' => pht('Resources'),
|
'name' => pht('Resources'),
|
||||||
),
|
),
|
||||||
|
array(
|
||||||
|
'key' => 'camera',
|
||||||
|
'icon' => 'fa-camera-retro',
|
||||||
|
'name' => pht('Design'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'key' => 'music',
|
||||||
|
'icon' => 'fa-headphones',
|
||||||
|
'name' => pht('Musician'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'key' => 'spy',
|
||||||
|
'icon' => 'fa-user-secret',
|
||||||
|
'name' => pht('Spy'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'key' => 'android',
|
||||||
|
'icon' => 'fa-android',
|
||||||
|
'name' => pht('Bot'),
|
||||||
|
),
|
||||||
array(
|
array(
|
||||||
'key' => 'relationships',
|
'key' => 'relationships',
|
||||||
'icon' => 'fa-glass',
|
'icon' => 'fa-glass',
|
||||||
|
|
|
@ -93,7 +93,7 @@ final class PhabricatorMentionRemarkupRule extends PhutilRemarkupRule {
|
||||||
|
|
||||||
if ($exists) {
|
if ($exists) {
|
||||||
$user = $actual_users[$username];
|
$user = $actual_users[$username];
|
||||||
Javelin::initBehavior('phabricator-hovercards');
|
Javelin::initBehavior('phui-hovercards');
|
||||||
|
|
||||||
// Check if the user has view access to the object she was mentioned in
|
// Check if the user has view access to the object she was mentioned in
|
||||||
if ($context_object
|
if ($context_object
|
||||||
|
|
|
@ -54,8 +54,10 @@ final class PhabricatorPeopleUserPHIDType extends PhabricatorPHIDType {
|
||||||
$icon_icon = PhabricatorPeopleIconSet::getIconIcon($icon_key);
|
$icon_icon = PhabricatorPeopleIconSet::getIconIcon($icon_key);
|
||||||
$subtitle = $profile->getDisplayTitle();
|
$subtitle = $profile->getDisplayTitle();
|
||||||
|
|
||||||
$handle->setIcon($icon_icon);
|
$handle
|
||||||
$handle->setSubtitle($subtitle);
|
->setIcon($icon_icon)
|
||||||
|
->setSubtitle($subtitle)
|
||||||
|
->setTokenIcon('fa-user');
|
||||||
}
|
}
|
||||||
|
|
||||||
$availability = null;
|
$availability = null;
|
||||||
|
|
|
@ -41,13 +41,13 @@ final class PhabricatorUserLogView extends AphrontView {
|
||||||
$ip = phutil_tag(
|
$ip = phutil_tag(
|
||||||
'a',
|
'a',
|
||||||
array(
|
array(
|
||||||
'href' => $base_uri.'?ip='.$log->getRemoteAddr().'#R',
|
'href' => $base_uri.'?ip='.$ip.'#R',
|
||||||
),
|
),
|
||||||
$ip);
|
$ip);
|
||||||
$session = phutil_tag(
|
$session = phutil_tag(
|
||||||
'a',
|
'a',
|
||||||
array(
|
array(
|
||||||
'href' => $base_uri.'?sessions='.$log->getSession().'#R',
|
'href' => $base_uri.'?sessions='.$ip.'#R',
|
||||||
),
|
),
|
||||||
$session);
|
$session);
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,20 +55,17 @@ final class PhameHomeController extends PhamePostController {
|
||||||
->addAction($create_button);
|
->addAction($create_button);
|
||||||
}
|
}
|
||||||
|
|
||||||
$actions = $this->renderActions($viewer);
|
$view_all = id(new PHUIButtonView())
|
||||||
$action_button = id(new PHUIButtonView())
|
|
||||||
->setTag('a')
|
->setTag('a')
|
||||||
->setText(pht('Actions'))
|
->setText(pht('View All'))
|
||||||
->setHref('#')
|
->setHref($this->getApplicationURI('post/'))
|
||||||
->setIcon('fa-bars')
|
->setIcon('fa-list-ul');
|
||||||
->addClass('phui-mobile-menu')
|
|
||||||
->setDropdownMenu($actions);
|
|
||||||
|
|
||||||
$title = pht('Recent Posts');
|
$title = pht('Recent Posts');
|
||||||
|
|
||||||
$header = id(new PHUIHeaderView())
|
$header = id(new PHUIHeaderView())
|
||||||
->setHeader($title)
|
->setHeader($title)
|
||||||
->addActionLink($action_button);
|
->addActionLink($view_all);
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
$crumbs->setBorder(true);
|
$crumbs->setBorder(true);
|
||||||
|
@ -108,39 +105,21 @@ final class PhameHomeController extends PhamePostController {
|
||||||
$blog_list,
|
$blog_list,
|
||||||
$draft_list,
|
$draft_list,
|
||||||
))
|
))
|
||||||
->setDisplay(PHUITwoColumnView::DISPLAY_LEFT)
|
->addClass('phame-home-container');
|
||||||
->addClass('phame-home-view');
|
|
||||||
|
$phame_home = phutil_tag_div('phame-home-view', $phame_view);
|
||||||
|
|
||||||
return $this->newPage()
|
return $this->newPage()
|
||||||
->setTitle($title)
|
->setTitle($title)
|
||||||
->setCrumbs($crumbs)
|
->setCrumbs($crumbs)
|
||||||
->appendChild(
|
->appendChild(
|
||||||
array(
|
array(
|
||||||
$phame_view,
|
$phame_home,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function renderActions($viewer) {
|
|
||||||
$actions = id(new PhabricatorActionListView())
|
|
||||||
->setUser($viewer);
|
|
||||||
|
|
||||||
$actions->addAction(
|
|
||||||
id(new PhabricatorActionView())
|
|
||||||
->setIcon('fa-pencil')
|
|
||||||
->setHref($this->getApplicationURI('post/query/draft/'))
|
|
||||||
->setName(pht('My Drafts')));
|
|
||||||
|
|
||||||
$actions->addAction(
|
|
||||||
id(new PhabricatorActionView())
|
|
||||||
->setIcon('fa-pencil-square-o')
|
|
||||||
->setHref($this->getApplicationURI('post/'))
|
|
||||||
->setName(pht('All Posts')));
|
|
||||||
|
|
||||||
return $actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function renderBlogs($viewer, $blogs) {}
|
private function renderBlogs($viewer, $blogs) {}
|
||||||
|
|
||||||
protected function buildApplicationCrumbs() {
|
protected function buildApplicationCrumbs() {
|
||||||
|
|
|
@ -101,9 +101,18 @@ final class PhamePostViewController
|
||||||
$subtitle = pht('Written by %s on %s.', $author, $date);
|
$subtitle = pht('Written by %s on %s.', $author, $date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$user_icon = $blogger_profile->getIcon();
|
||||||
|
$user_icon = PhabricatorPeopleIconSet::getIconIcon($user_icon);
|
||||||
|
$user_icon = id(new PHUIIconView())->setIcon($user_icon);
|
||||||
|
|
||||||
$about = id(new PhameDescriptionView())
|
$about = id(new PhameDescriptionView())
|
||||||
->setTitle($subtitle)
|
->setTitle($subtitle)
|
||||||
->setDescription($blogger_profile->getTitle())
|
->setDescription(
|
||||||
|
array(
|
||||||
|
$user_icon,
|
||||||
|
' ',
|
||||||
|
$blogger_profile->getTitle(),
|
||||||
|
))
|
||||||
->setImage($blogger->getProfileImageURI())
|
->setImage($blogger->getProfileImageURI())
|
||||||
->setImageHref('/p/'.$blogger->getUsername());
|
->setImageHref('/p/'.$blogger->getUsername());
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ final class PhabricatorObjectHandle
|
||||||
private $objectName;
|
private $objectName;
|
||||||
private $policyFiltered;
|
private $policyFiltered;
|
||||||
private $subtitle;
|
private $subtitle;
|
||||||
|
private $tokenIcon;
|
||||||
|
|
||||||
public function setIcon($icon) {
|
public function setIcon($icon) {
|
||||||
$this->icon = $icon;
|
$this->icon = $icon;
|
||||||
|
@ -86,6 +87,19 @@ final class PhabricatorObjectHandle
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setTokenIcon($icon) {
|
||||||
|
$this->tokenIcon = $icon;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTokenIcon() {
|
||||||
|
if ($this->tokenIcon !== null) {
|
||||||
|
return $this->tokenIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->getIcon();
|
||||||
|
}
|
||||||
|
|
||||||
public function getTypeIcon() {
|
public function getTypeIcon() {
|
||||||
if ($this->getPHIDType()) {
|
if ($this->getPHIDType()) {
|
||||||
return $this->getPHIDType()->getTypeIcon();
|
return $this->getPHIDType()->getTypeIcon();
|
||||||
|
@ -262,7 +276,7 @@ final class PhabricatorObjectHandle
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderHovercardLink($name = null) {
|
public function renderHovercardLink($name = null) {
|
||||||
Javelin::initBehavior('phabricator-hovercards');
|
Javelin::initBehavior('phui-hovercards');
|
||||||
|
|
||||||
$attributes = array(
|
$attributes = array(
|
||||||
'sigil' => 'hovercard',
|
'sigil' => 'hovercard',
|
||||||
|
|
|
@ -9,7 +9,7 @@ final class PHUIHandleTagListView extends AphrontTagView {
|
||||||
private $slim;
|
private $slim;
|
||||||
private $showHovercards;
|
private $showHovercards;
|
||||||
|
|
||||||
public function setHandles(array $handles) {
|
public function setHandles($handles) {
|
||||||
$this->handles = $handles;
|
$this->handles = $handles;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ final class PhabricatorPolicyEditController
|
||||||
public function handleRequest(AphrontRequest $request) {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
|
||||||
$object_phid = $request->getURIData('objectPHID');
|
$object_phid = $request->getURIData('objectPHID');
|
||||||
if ($object_phid) {
|
if ($object_phid) {
|
||||||
$object = id(new PhabricatorObjectQuery())
|
$object = id(new PhabricatorObjectQuery())
|
||||||
|
@ -32,6 +31,17 @@ final class PhabricatorPolicyEditController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$phid = $request->getURIData('phid');
|
||||||
|
switch ($phid) {
|
||||||
|
case AphrontFormPolicyControl::getSelectProjectKey():
|
||||||
|
return $this->handleProjectRequest($request);
|
||||||
|
case AphrontFormPolicyControl::getSelectCustomKey():
|
||||||
|
$phid = null;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$action_options = array(
|
$action_options = array(
|
||||||
PhabricatorPolicy::ACTION_ALLOW => pht('Allow'),
|
PhabricatorPolicy::ACTION_ALLOW => pht('Allow'),
|
||||||
PhabricatorPolicy::ACTION_DENY => pht('Deny'),
|
PhabricatorPolicy::ACTION_DENY => pht('Deny'),
|
||||||
|
@ -55,7 +65,6 @@ final class PhabricatorPolicyEditController
|
||||||
'value' => null,
|
'value' => null,
|
||||||
);
|
);
|
||||||
|
|
||||||
$phid = $request->getURIData('phid');
|
|
||||||
if ($phid) {
|
if ($phid) {
|
||||||
$policies = id(new PhabricatorPolicyQuery())
|
$policies = id(new PhabricatorPolicyQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
|
@ -253,4 +262,79 @@ final class PhabricatorPolicyEditController
|
||||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function handleProjectRequest(AphrontRequest $request) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$errors = array();
|
||||||
|
$e_project = true;
|
||||||
|
|
||||||
|
if ($request->isFormPost()) {
|
||||||
|
$project_phids = $request->getArr('projectPHIDs');
|
||||||
|
$project_phid = head($project_phids);
|
||||||
|
|
||||||
|
$project = id(new PhabricatorObjectQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs(array($project_phid))
|
||||||
|
->executeOne();
|
||||||
|
|
||||||
|
if ($project) {
|
||||||
|
// Save this project as one of the user's most recently used projects,
|
||||||
|
// so we'll show it by default in future menus.
|
||||||
|
|
||||||
|
$pref_key = PhabricatorUserPreferences::PREFERENCE_FAVORITE_POLICIES;
|
||||||
|
|
||||||
|
$preferences = $viewer->loadPreferences();
|
||||||
|
$favorites = $preferences->getPreference($pref_key);
|
||||||
|
if (!is_array($favorites)) {
|
||||||
|
$favorites = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add this, or move it to the end of the list.
|
||||||
|
unset($favorites[$project_phid]);
|
||||||
|
$favorites[$project_phid] = true;
|
||||||
|
|
||||||
|
$preferences->setPreference($pref_key, $favorites);
|
||||||
|
$preferences->save();
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'phid' => $project->getPHID(),
|
||||||
|
'info' => array(
|
||||||
|
'name' => $project->getName(),
|
||||||
|
'full' => $project->getName(),
|
||||||
|
'icon' => $project->getDisplayIconIcon(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return id(new AphrontAjaxResponse())->setContent($data);
|
||||||
|
} else {
|
||||||
|
$errors[] = pht('You must choose a project.');
|
||||||
|
$e_project = pht('Required');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$project_datasource = id(new PhabricatorProjectDatasource())
|
||||||
|
->setParameters(
|
||||||
|
array(
|
||||||
|
'policy' => 1,
|
||||||
|
));
|
||||||
|
|
||||||
|
$form = id(new AphrontFormView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->appendControl(
|
||||||
|
id(new AphrontFormTokenizerControl())
|
||||||
|
->setLabel(pht('Members Of'))
|
||||||
|
->setName('projectPHIDs')
|
||||||
|
->setLimit(1)
|
||||||
|
->setError($e_project)
|
||||||
|
->setDatasource($project_datasource));
|
||||||
|
|
||||||
|
return $this->newDialog()
|
||||||
|
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||||
|
->setErrors($errors)
|
||||||
|
->setTitle(pht('Select Project'))
|
||||||
|
->appendForm($form)
|
||||||
|
->addSubmitButton(pht('Done'))
|
||||||
|
->addCancelButton('#');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,10 +195,48 @@ final class PhabricatorPolicyQuery
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
if ($viewer->getPHID()) {
|
if ($viewer->getPHID()) {
|
||||||
|
$pref_key = PhabricatorUserPreferences::PREFERENCE_FAVORITE_POLICIES;
|
||||||
|
|
||||||
|
$favorite_limit = 10;
|
||||||
|
$default_limit = 5;
|
||||||
|
|
||||||
|
// If possible, show the user's 10 most recently used projects.
|
||||||
|
$preferences = $viewer->loadPreferences();
|
||||||
|
$favorites = $preferences->getPreference($pref_key);
|
||||||
|
if (!is_array($favorites)) {
|
||||||
|
$favorites = array();
|
||||||
|
}
|
||||||
|
$favorite_phids = array_keys($favorites);
|
||||||
|
$favorite_phids = array_slice($favorite_phids, -$favorite_limit);
|
||||||
|
|
||||||
|
if ($favorite_phids) {
|
||||||
$projects = id(new PhabricatorProjectQuery())
|
$projects = id(new PhabricatorProjectQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withMemberPHIDs(array($viewer->getPHID()))
|
->withPHIDs($favorite_phids)
|
||||||
|
->withIsMilestone(false)
|
||||||
|
->setLimit($favorite_limit)
|
||||||
->execute();
|
->execute();
|
||||||
|
$projects = mpull($projects, null, 'getPHID');
|
||||||
|
} else {
|
||||||
|
$projects = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we didn't find enough favorites, add some default projects. These
|
||||||
|
// are just arbitrary projects that the viewer is a member of, but may
|
||||||
|
// be useful on smaller installs and for new users until they can use
|
||||||
|
// the control enough time to establish useful favorites.
|
||||||
|
if (count($projects) < $default_limit) {
|
||||||
|
$default_projects = id(new PhabricatorProjectQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withMemberPHIDs(array($viewer->getPHID()))
|
||||||
|
->withIsMilestone(false)
|
||||||
|
->setLimit($default_limit)
|
||||||
|
->execute();
|
||||||
|
$default_projects = mpull($default_projects, null, 'getPHID');
|
||||||
|
$projects = $projects + $default_projects;
|
||||||
|
$projects = array_slice($projects, 0, $default_limit);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($projects as $project) {
|
foreach ($projects as $project) {
|
||||||
$phids[] = $project->getPHID();
|
$phids[] = $project->getPHID();
|
||||||
}
|
}
|
||||||
|
|
|
@ -808,6 +808,318 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase {
|
||||||
pht('Engineering + Scan'));
|
pht('Engineering + Scan'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testTagAncestryConflicts() {
|
||||||
|
$user = $this->createUser();
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
$stonework = $this->createProject($user);
|
||||||
|
$stonework_masonry = $this->createProject($user, $stonework);
|
||||||
|
$stonework_sculpting = $this->createProject($user, $stonework);
|
||||||
|
|
||||||
|
$task = $this->newTask($user, array());
|
||||||
|
$this->assertEqual(array(), $this->getTaskProjects($task));
|
||||||
|
|
||||||
|
$this->addProjectTags($user, $task, array($stonework->getPHID()));
|
||||||
|
$this->assertEqual(
|
||||||
|
array(
|
||||||
|
$stonework->getPHID(),
|
||||||
|
),
|
||||||
|
$this->getTaskProjects($task));
|
||||||
|
|
||||||
|
// Adding a descendant should remove the parent.
|
||||||
|
$this->addProjectTags($user, $task, array($stonework_masonry->getPHID()));
|
||||||
|
$this->assertEqual(
|
||||||
|
array(
|
||||||
|
$stonework_masonry->getPHID(),
|
||||||
|
),
|
||||||
|
$this->getTaskProjects($task));
|
||||||
|
|
||||||
|
// Adding an ancestor should remove the descendant.
|
||||||
|
$this->addProjectTags($user, $task, array($stonework->getPHID()));
|
||||||
|
$this->assertEqual(
|
||||||
|
array(
|
||||||
|
$stonework->getPHID(),
|
||||||
|
),
|
||||||
|
$this->getTaskProjects($task));
|
||||||
|
|
||||||
|
// Adding two tags in the same hierarchy which are not mutual ancestors
|
||||||
|
// should remove the ancestor but otherwise work fine.
|
||||||
|
$this->addProjectTags(
|
||||||
|
$user,
|
||||||
|
$task,
|
||||||
|
array(
|
||||||
|
$stonework_masonry->getPHID(),
|
||||||
|
$stonework_sculpting->getPHID(),
|
||||||
|
));
|
||||||
|
|
||||||
|
$expect = array(
|
||||||
|
$stonework_masonry->getPHID(),
|
||||||
|
$stonework_sculpting->getPHID(),
|
||||||
|
);
|
||||||
|
sort($expect);
|
||||||
|
|
||||||
|
$this->assertEqual($expect, $this->getTaskProjects($task));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTagMilestoneConflicts() {
|
||||||
|
$user = $this->createUser();
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
$stonework = $this->createProject($user);
|
||||||
|
$stonework_1 = $this->createProject($user, $stonework, true);
|
||||||
|
$stonework_2 = $this->createProject($user, $stonework, true);
|
||||||
|
|
||||||
|
$task = $this->newTask($user, array());
|
||||||
|
$this->assertEqual(array(), $this->getTaskProjects($task));
|
||||||
|
|
||||||
|
$this->addProjectTags($user, $task, array($stonework->getPHID()));
|
||||||
|
$this->assertEqual(
|
||||||
|
array(
|
||||||
|
$stonework->getPHID(),
|
||||||
|
),
|
||||||
|
$this->getTaskProjects($task));
|
||||||
|
|
||||||
|
// Adding a milesone should remove the parent.
|
||||||
|
$this->addProjectTags($user, $task, array($stonework_1->getPHID()));
|
||||||
|
$this->assertEqual(
|
||||||
|
array(
|
||||||
|
$stonework_1->getPHID(),
|
||||||
|
),
|
||||||
|
$this->getTaskProjects($task));
|
||||||
|
|
||||||
|
// Adding the parent should remove the milestone.
|
||||||
|
$this->addProjectTags($user, $task, array($stonework->getPHID()));
|
||||||
|
$this->assertEqual(
|
||||||
|
array(
|
||||||
|
$stonework->getPHID(),
|
||||||
|
),
|
||||||
|
$this->getTaskProjects($task));
|
||||||
|
|
||||||
|
// First, add one milestone.
|
||||||
|
$this->addProjectTags($user, $task, array($stonework_1->getPHID()));
|
||||||
|
// Now, adding a second milestone should remove the first milestone.
|
||||||
|
$this->addProjectTags($user, $task, array($stonework_2->getPHID()));
|
||||||
|
$this->assertEqual(
|
||||||
|
array(
|
||||||
|
$stonework_2->getPHID(),
|
||||||
|
),
|
||||||
|
$this->getTaskProjects($task));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBoardMoves() {
|
||||||
|
$user = $this->createUser();
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
$board = $this->createProject($user);
|
||||||
|
|
||||||
|
$backlog = $this->addColumn($user, $board, 0);
|
||||||
|
$column = $this->addColumn($user, $board, 1);
|
||||||
|
|
||||||
|
// New tasks should appear in the backlog.
|
||||||
|
$task1 = $this->newTask($user, array($board));
|
||||||
|
$expect = array(
|
||||||
|
$backlog->getPHID(),
|
||||||
|
);
|
||||||
|
$this->assertColumns($expect, $user, $board, $task1);
|
||||||
|
|
||||||
|
// Moving a task should move it to the destination column.
|
||||||
|
$this->moveToColumn($user, $board, $task1, $backlog, $column);
|
||||||
|
$expect = array(
|
||||||
|
$column->getPHID(),
|
||||||
|
);
|
||||||
|
$this->assertColumns($expect, $user, $board, $task1);
|
||||||
|
|
||||||
|
// Same thing again, with a new task.
|
||||||
|
$task2 = $this->newTask($user, array($board));
|
||||||
|
$expect = array(
|
||||||
|
$backlog->getPHID(),
|
||||||
|
);
|
||||||
|
$this->assertColumns($expect, $user, $board, $task2);
|
||||||
|
|
||||||
|
// Move it, too.
|
||||||
|
$this->moveToColumn($user, $board, $task2, $backlog, $column);
|
||||||
|
$expect = array(
|
||||||
|
$column->getPHID(),
|
||||||
|
);
|
||||||
|
$this->assertColumns($expect, $user, $board, $task2);
|
||||||
|
|
||||||
|
// Now the stuff should be in the column, in order, with the more recently
|
||||||
|
// moved task on top.
|
||||||
|
$expect = array(
|
||||||
|
$task2->getPHID(),
|
||||||
|
$task1->getPHID(),
|
||||||
|
);
|
||||||
|
$this->assertTasksInColumn($expect, $user, $board, $column);
|
||||||
|
|
||||||
|
// Move the second task after the first task.
|
||||||
|
$options = array(
|
||||||
|
'afterPHID' => $task1->getPHID(),
|
||||||
|
);
|
||||||
|
$this->moveToColumn($user, $board, $task2, $column, $column, $options);
|
||||||
|
$expect = array(
|
||||||
|
$task1->getPHID(),
|
||||||
|
$task2->getPHID(),
|
||||||
|
);
|
||||||
|
$this->assertTasksInColumn($expect, $user, $board, $column);
|
||||||
|
|
||||||
|
// Move the second task before the first task.
|
||||||
|
$options = array(
|
||||||
|
'beforePHID' => $task1->getPHID(),
|
||||||
|
);
|
||||||
|
$this->moveToColumn($user, $board, $task2, $column, $column, $options);
|
||||||
|
$expect = array(
|
||||||
|
$task2->getPHID(),
|
||||||
|
$task1->getPHID(),
|
||||||
|
);
|
||||||
|
$this->assertTasksInColumn($expect, $user, $board, $column);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMilestoneMoves() {
|
||||||
|
$user = $this->createUser();
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
$board = $this->createProject($user);
|
||||||
|
|
||||||
|
$backlog = $this->addColumn($user, $board, 0);
|
||||||
|
|
||||||
|
// Create a task into the backlog.
|
||||||
|
$task = $this->newTask($user, array($board));
|
||||||
|
$expect = array(
|
||||||
|
$backlog->getPHID(),
|
||||||
|
);
|
||||||
|
$this->assertColumns($expect, $user, $board, $task);
|
||||||
|
|
||||||
|
$milestone = $this->createProject($user, $board, true);
|
||||||
|
|
||||||
|
$this->addProjectTags($user, $task, array($milestone->getPHID()));
|
||||||
|
|
||||||
|
// We just want the side effect of looking at the board: creation of the
|
||||||
|
// milestone column.
|
||||||
|
$this->loadColumns($user, $board, $task);
|
||||||
|
|
||||||
|
$column = id(new PhabricatorProjectColumnQuery())
|
||||||
|
->setViewer($user)
|
||||||
|
->withProjectPHIDs(array($board->getPHID()))
|
||||||
|
->withProxyPHIDs(array($milestone->getPHID()))
|
||||||
|
->executeOne();
|
||||||
|
|
||||||
|
$this->assertTrue((bool)$column);
|
||||||
|
|
||||||
|
// Moving the task to the milestone should have moved it to the milestone
|
||||||
|
// column.
|
||||||
|
$expect = array(
|
||||||
|
$column->getPHID(),
|
||||||
|
);
|
||||||
|
$this->assertColumns($expect, $user, $board, $task);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function moveToColumn(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
PhabricatorProject $board,
|
||||||
|
ManiphestTask $task,
|
||||||
|
PhabricatorProjectColumn $src,
|
||||||
|
PhabricatorProjectColumn $dst,
|
||||||
|
$options = null) {
|
||||||
|
|
||||||
|
$xactions = array();
|
||||||
|
|
||||||
|
if (!$options) {
|
||||||
|
$options = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$xactions[] = id(new ManiphestTransaction())
|
||||||
|
->setTransactionType(ManiphestTransaction::TYPE_PROJECT_COLUMN)
|
||||||
|
->setOldValue(
|
||||||
|
array(
|
||||||
|
'projectPHID' => $board->getPHID(),
|
||||||
|
'columnPHIDs' => array($src->getPHID()),
|
||||||
|
))
|
||||||
|
->setNewValue(
|
||||||
|
array(
|
||||||
|
'projectPHID' => $board->getPHID(),
|
||||||
|
'columnPHIDs' => array($dst->getPHID()),
|
||||||
|
) + $options);
|
||||||
|
|
||||||
|
$editor = id(new ManiphestTransactionEditor())
|
||||||
|
->setActor($viewer)
|
||||||
|
->setContentSource(PhabricatorContentSource::newConsoleSource())
|
||||||
|
->setContinueOnNoEffect(true)
|
||||||
|
->applyTransactions($task, $xactions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertColumns(
|
||||||
|
array $expect,
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
PhabricatorProject $board,
|
||||||
|
ManiphestTask $task) {
|
||||||
|
$column_phids = $this->loadColumns($viewer, $board, $task);
|
||||||
|
$this->assertEqual($expect, $column_phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadColumns(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
PhabricatorProject $board,
|
||||||
|
ManiphestTask $task) {
|
||||||
|
$engine = id(new PhabricatorBoardLayoutEngine())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->setBoardPHIDs(array($board->getPHID()))
|
||||||
|
->setObjectPHIDs(
|
||||||
|
array(
|
||||||
|
$task->getPHID(),
|
||||||
|
))
|
||||||
|
->executeLayout();
|
||||||
|
|
||||||
|
$columns = $engine->getObjectColumns($board->getPHID(), $task->getPHID());
|
||||||
|
$column_phids = mpull($columns, 'getPHID');
|
||||||
|
$column_phids = array_values($column_phids);
|
||||||
|
|
||||||
|
return $column_phids;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertTasksInColumn(
|
||||||
|
array $expect,
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
PhabricatorProject $board,
|
||||||
|
PhabricatorProjectColumn $column) {
|
||||||
|
|
||||||
|
$engine = id(new PhabricatorBoardLayoutEngine())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->setBoardPHIDs(array($board->getPHID()))
|
||||||
|
->setObjectPHIDs($expect)
|
||||||
|
->executeLayout();
|
||||||
|
|
||||||
|
$object_phids = $engine->getColumnObjectPHIDs(
|
||||||
|
$board->getPHID(),
|
||||||
|
$column->getPHID());
|
||||||
|
$object_phids = array_values($object_phids);
|
||||||
|
|
||||||
|
$this->assertEqual($expect, $object_phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addColumn(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
PhabricatorProject $project,
|
||||||
|
$sequence) {
|
||||||
|
|
||||||
|
$project->setHasWorkboard(1)->save();
|
||||||
|
|
||||||
|
return PhabricatorProjectColumn::initializeNewColumn($viewer)
|
||||||
|
->setSequence(0)
|
||||||
|
->setProperty('isDefault', ($sequence == 0))
|
||||||
|
->setProjectPHID($project->getPHID())
|
||||||
|
->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getTaskProjects(ManiphestTask $task) {
|
||||||
|
$project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
||||||
|
$task->getPHID(),
|
||||||
|
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
|
||||||
|
|
||||||
|
sort($project_phids);
|
||||||
|
|
||||||
|
return $project_phids;
|
||||||
|
}
|
||||||
|
|
||||||
private function attemptProjectEdit(
|
private function attemptProjectEdit(
|
||||||
PhabricatorProject $proj,
|
PhabricatorProject $proj,
|
||||||
PhabricatorUser $user,
|
PhabricatorUser $user,
|
||||||
|
@ -827,6 +1139,30 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function addProjectTags(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
ManiphestTask $task,
|
||||||
|
array $phids) {
|
||||||
|
|
||||||
|
$xactions = array();
|
||||||
|
|
||||||
|
$xactions[] = id(new ManiphestTransaction())
|
||||||
|
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
|
||||||
|
->setMetadataValue(
|
||||||
|
'edge:type',
|
||||||
|
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST)
|
||||||
|
->setNewValue(
|
||||||
|
array(
|
||||||
|
'+' => array_fuse($phids),
|
||||||
|
));
|
||||||
|
|
||||||
|
$editor = id(new ManiphestTransactionEditor())
|
||||||
|
->setActor($viewer)
|
||||||
|
->setContentSource(PhabricatorContentSource::newConsoleSource())
|
||||||
|
->setContinueOnNoEffect(true)
|
||||||
|
->applyTransactions($task, $xactions);
|
||||||
|
}
|
||||||
|
|
||||||
private function newTask(
|
private function newTask(
|
||||||
PhabricatorUser $viewer,
|
PhabricatorUser $viewer,
|
||||||
array $projects,
|
array $projects,
|
||||||
|
@ -945,6 +1281,16 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase {
|
||||||
|
|
||||||
$this->applyTransactions($project, $user, $xactions);
|
$this->applyTransactions($project, $user, $xactions);
|
||||||
|
|
||||||
|
// Force these values immediately; they are normally updated by the
|
||||||
|
// index engine.
|
||||||
|
if ($parent) {
|
||||||
|
if ($is_milestone) {
|
||||||
|
$parent->setHasMilestones(1)->save();
|
||||||
|
} else {
|
||||||
|
$parent->setHasSubprojects(1)->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $project;
|
return $project;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,8 @@ final class PhabricatorProjectApplication extends PhabricatorApplication {
|
||||||
=> 'PhabricatorProjectWatchController',
|
=> 'PhabricatorProjectWatchController',
|
||||||
'silence/(?P<id>[1-9]\d*)/'
|
'silence/(?P<id>[1-9]\d*)/'
|
||||||
=> 'PhabricatorProjectSilenceController',
|
=> 'PhabricatorProjectSilenceController',
|
||||||
|
'warning/(?P<id>[1-9]\d*)/'
|
||||||
|
=> 'PhabricatorProjectSubprojectWarningController',
|
||||||
),
|
),
|
||||||
'/tag/' => array(
|
'/tag/' => array(
|
||||||
'(?P<slug>[^/]+)/' => 'PhabricatorProjectViewController',
|
'(?P<slug>[^/]+)/' => 'PhabricatorProjectViewController',
|
||||||
|
|
|
@ -97,9 +97,20 @@ final class PhabricatorProjectBoardReorderController
|
||||||
->setFlush(true);
|
->setFlush(true);
|
||||||
|
|
||||||
foreach ($columns as $column) {
|
foreach ($columns as $column) {
|
||||||
|
// Don't allow milestone columns to be reordered.
|
||||||
|
$proxy = $column->getProxy();
|
||||||
|
if ($proxy && $proxy->isMilestone()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At least for now, don't show subproject column.
|
||||||
|
if ($proxy) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$item = id(new PHUIObjectItemView())
|
$item = id(new PHUIObjectItemView())
|
||||||
->setHeader($column->getDisplayName())
|
->setHeader($column->getDisplayName())
|
||||||
->addIcon('none', $column->getDisplayType());
|
->addIcon($column->getHeaderIcon(), $column->getDisplayType());
|
||||||
|
|
||||||
if ($column->isHidden()) {
|
if ($column->isHidden()) {
|
||||||
$item->setDisabled(true);
|
$item->setDisabled(true);
|
||||||
|
|
|
@ -28,10 +28,103 @@ final class PhabricatorProjectBoardViewController
|
||||||
$project = $this->getProject();
|
$project = $this->getProject();
|
||||||
|
|
||||||
$this->readRequestState();
|
$this->readRequestState();
|
||||||
$columns = $this->loadColumns($project);
|
|
||||||
|
|
||||||
// TODO: Expand the checks here if we add the ability
|
$board_uri = $this->getApplicationURI('board/'.$project->getID().'/');
|
||||||
// to hide the Backlog column
|
|
||||||
|
$search_engine = id(new ManiphestTaskSearchEngine())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->setBaseURI($board_uri)
|
||||||
|
->setIsBoardView(true);
|
||||||
|
|
||||||
|
if ($request->isFormPost() && !$request->getBool('initialize')) {
|
||||||
|
$saved = $search_engine->buildSavedQueryFromRequest($request);
|
||||||
|
$search_engine->saveQuery($saved);
|
||||||
|
$filter_form = id(new AphrontFormView())
|
||||||
|
->setUser($viewer);
|
||||||
|
$search_engine->buildSearchForm($filter_form, $saved);
|
||||||
|
if ($search_engine->getErrors()) {
|
||||||
|
return $this->newDialog()
|
||||||
|
->setWidth(AphrontDialogView::WIDTH_FULL)
|
||||||
|
->setTitle(pht('Advanced Filter'))
|
||||||
|
->appendChild($filter_form->buildLayoutView())
|
||||||
|
->setErrors($search_engine->getErrors())
|
||||||
|
->setSubmitURI($board_uri)
|
||||||
|
->addSubmitButton(pht('Apply Filter'))
|
||||||
|
->addCancelButton($board_uri);
|
||||||
|
}
|
||||||
|
return id(new AphrontRedirectResponse())->setURI(
|
||||||
|
$this->getURIWithState(
|
||||||
|
$search_engine->getQueryResultsPageURI($saved->getQueryKey())));
|
||||||
|
}
|
||||||
|
|
||||||
|
$query_key = $request->getURIData('queryKey');
|
||||||
|
if (!$query_key) {
|
||||||
|
$query_key = 'open';
|
||||||
|
}
|
||||||
|
$this->queryKey = $query_key;
|
||||||
|
|
||||||
|
$custom_query = null;
|
||||||
|
if ($search_engine->isBuiltinQuery($query_key)) {
|
||||||
|
$saved = $search_engine->buildSavedQueryFromBuiltin($query_key);
|
||||||
|
} else {
|
||||||
|
$saved = id(new PhabricatorSavedQueryQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withQueryKeys(array($query_key))
|
||||||
|
->executeOne();
|
||||||
|
|
||||||
|
if (!$saved) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$custom_query = $saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->getURIData('filter')) {
|
||||||
|
$filter_form = id(new AphrontFormView())
|
||||||
|
->setUser($viewer);
|
||||||
|
$search_engine->buildSearchForm($filter_form, $saved);
|
||||||
|
|
||||||
|
return $this->newDialog()
|
||||||
|
->setWidth(AphrontDialogView::WIDTH_FULL)
|
||||||
|
->setTitle(pht('Advanced Filter'))
|
||||||
|
->appendChild($filter_form->buildLayoutView())
|
||||||
|
->setSubmitURI($board_uri)
|
||||||
|
->addSubmitButton(pht('Apply Filter'))
|
||||||
|
->addCancelButton($board_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
$task_query = $search_engine->buildQueryFromSavedQuery($saved);
|
||||||
|
|
||||||
|
$select_phids = array($project->getPHID());
|
||||||
|
if ($project->getHasSubprojects() || $project->getHasMilestones()) {
|
||||||
|
$descendants = id(new PhabricatorProjectQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withAncestorProjectPHIDs($select_phids)
|
||||||
|
->execute();
|
||||||
|
foreach ($descendants as $descendant) {
|
||||||
|
$select_phids[] = $descendant->getPHID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$tasks = $task_query
|
||||||
|
->withEdgeLogicPHIDs(
|
||||||
|
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
|
||||||
|
PhabricatorQueryConstraint::OPERATOR_ANCESTOR,
|
||||||
|
array($select_phids))
|
||||||
|
->setOrder(ManiphestTaskQuery::ORDER_PRIORITY)
|
||||||
|
->setViewer($viewer)
|
||||||
|
->execute();
|
||||||
|
$tasks = mpull($tasks, null, 'getPHID');
|
||||||
|
|
||||||
|
$board_phid = $project->getPHID();
|
||||||
|
|
||||||
|
$layout_engine = id(new PhabricatorBoardLayoutEngine())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->setBoardPHIDs(array($board_phid))
|
||||||
|
->setObjectPHIDs(array_keys($tasks))
|
||||||
|
->executeLayout();
|
||||||
|
|
||||||
|
$columns = $layout_engine->getColumns($board_phid);
|
||||||
if (!$columns) {
|
if (!$columns) {
|
||||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||||
$viewer,
|
$viewer,
|
||||||
|
@ -56,132 +149,14 @@ final class PhabricatorProjectBoardViewController
|
||||||
return $this->newPage()
|
return $this->newPage()
|
||||||
->setTitle(
|
->setTitle(
|
||||||
array(
|
array(
|
||||||
|
$project->getDisplayName(),
|
||||||
pht('Workboard'),
|
pht('Workboard'),
|
||||||
$project->getName(),
|
|
||||||
))
|
))
|
||||||
->setNavigation($nav)
|
->setNavigation($nav)
|
||||||
->setCrumbs($crumbs)
|
->setCrumbs($crumbs)
|
||||||
->appendChild($content);
|
->appendChild($content);
|
||||||
}
|
}
|
||||||
|
|
||||||
$board_uri = $this->getApplicationURI('board/'.$project->getID().'/');
|
|
||||||
|
|
||||||
$engine = id(new ManiphestTaskSearchEngine())
|
|
||||||
->setViewer($viewer)
|
|
||||||
->setBaseURI($board_uri)
|
|
||||||
->setIsBoardView(true);
|
|
||||||
|
|
||||||
if ($request->isFormPost()) {
|
|
||||||
$saved = $engine->buildSavedQueryFromRequest($request);
|
|
||||||
$engine->saveQuery($saved);
|
|
||||||
$filter_form = id(new AphrontFormView())
|
|
||||||
->setUser($viewer);
|
|
||||||
$engine->buildSearchForm($filter_form, $saved);
|
|
||||||
if ($engine->getErrors()) {
|
|
||||||
return $this->newDialog()
|
|
||||||
->setWidth(AphrontDialogView::WIDTH_FULL)
|
|
||||||
->setTitle(pht('Advanced Filter'))
|
|
||||||
->appendChild($filter_form->buildLayoutView())
|
|
||||||
->setErrors($engine->getErrors())
|
|
||||||
->setSubmitURI($board_uri)
|
|
||||||
->addSubmitButton(pht('Apply Filter'))
|
|
||||||
->addCancelButton($board_uri);
|
|
||||||
}
|
|
||||||
return id(new AphrontRedirectResponse())->setURI(
|
|
||||||
$this->getURIWithState(
|
|
||||||
$engine->getQueryResultsPageURI($saved->getQueryKey())));
|
|
||||||
}
|
|
||||||
|
|
||||||
$query_key = $request->getURIData('queryKey');
|
|
||||||
if (!$query_key) {
|
|
||||||
$query_key = 'open';
|
|
||||||
}
|
|
||||||
$this->queryKey = $query_key;
|
|
||||||
|
|
||||||
$custom_query = null;
|
|
||||||
if ($engine->isBuiltinQuery($query_key)) {
|
|
||||||
$saved = $engine->buildSavedQueryFromBuiltin($query_key);
|
|
||||||
} else {
|
|
||||||
$saved = id(new PhabricatorSavedQueryQuery())
|
|
||||||
->setViewer($viewer)
|
|
||||||
->withQueryKeys(array($query_key))
|
|
||||||
->executeOne();
|
|
||||||
|
|
||||||
if (!$saved) {
|
|
||||||
return new Aphront404Response();
|
|
||||||
}
|
|
||||||
|
|
||||||
$custom_query = $saved;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($request->getURIData('filter')) {
|
|
||||||
$filter_form = id(new AphrontFormView())
|
|
||||||
->setUser($viewer);
|
|
||||||
$engine->buildSearchForm($filter_form, $saved);
|
|
||||||
|
|
||||||
return $this->newDialog()
|
|
||||||
->setWidth(AphrontDialogView::WIDTH_FULL)
|
|
||||||
->setTitle(pht('Advanced Filter'))
|
|
||||||
->appendChild($filter_form->buildLayoutView())
|
|
||||||
->setSubmitURI($board_uri)
|
|
||||||
->addSubmitButton(pht('Apply Filter'))
|
|
||||||
->addCancelButton($board_uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
$task_query = $engine->buildQueryFromSavedQuery($saved);
|
|
||||||
|
|
||||||
$tasks = $task_query
|
|
||||||
->withEdgeLogicPHIDs(
|
|
||||||
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
|
|
||||||
PhabricatorQueryConstraint::OPERATOR_AND,
|
|
||||||
array($project->getPHID()))
|
|
||||||
->setOrder(ManiphestTaskQuery::ORDER_PRIORITY)
|
|
||||||
->setViewer($viewer)
|
|
||||||
->execute();
|
|
||||||
$tasks = mpull($tasks, null, 'getPHID');
|
|
||||||
|
|
||||||
if ($tasks) {
|
|
||||||
$positions = id(new PhabricatorProjectColumnPositionQuery())
|
|
||||||
->setViewer($viewer)
|
|
||||||
->withObjectPHIDs(mpull($tasks, 'getPHID'))
|
|
||||||
->withColumns($columns)
|
|
||||||
->execute();
|
|
||||||
$positions = mpull($positions, null, 'getObjectPHID');
|
|
||||||
} else {
|
|
||||||
$positions = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$task_map = array();
|
|
||||||
foreach ($tasks as $task) {
|
|
||||||
$task_phid = $task->getPHID();
|
|
||||||
if (empty($positions[$task_phid])) {
|
|
||||||
// This shouldn't normally be possible because we create positions on
|
|
||||||
// demand, but we might have raced as an object was removed from the
|
|
||||||
// board. Just drop the task if we don't have a position for it.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$position = $positions[$task_phid];
|
|
||||||
$task_map[$position->getColumnPHID()][] = $task_phid;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're showing the board in "natural" order, sort columns by their
|
|
||||||
// column positions.
|
|
||||||
if ($this->sortKey == PhabricatorProjectColumn::ORDER_NATURAL) {
|
|
||||||
foreach ($task_map as $column_phid => $task_phids) {
|
|
||||||
$order = array();
|
|
||||||
foreach ($task_phids as $task_phid) {
|
|
||||||
if (isset($positions[$task_phid])) {
|
|
||||||
$order[$task_phid] = $positions[$task_phid]->getOrderingKey();
|
|
||||||
} else {
|
|
||||||
$order[$task_phid] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
asort($order);
|
|
||||||
$task_map[$column_phid] = array_keys($order);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$task_can_edit_map = id(new PhabricatorPolicyFilter())
|
$task_can_edit_map = id(new PhabricatorPolicyFilter())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->requireCapabilities(array(PhabricatorPolicyCapability::CAN_EDIT))
|
->requireCapabilities(array(PhabricatorPolicyCapability::CAN_EDIT))
|
||||||
|
@ -198,7 +173,10 @@ final class PhabricatorProjectBoardViewController
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
$batch_task_phids = idx($task_map, $batch_column->getPHID(), array());
|
$batch_task_phids = $layout_engine->getColumnObjectPHIDs(
|
||||||
|
$board_phid,
|
||||||
|
$batch_column->getPHID());
|
||||||
|
|
||||||
foreach ($batch_task_phids as $key => $batch_task_phid) {
|
foreach ($batch_task_phids as $key => $batch_task_phid) {
|
||||||
if (empty($task_can_edit_map[$batch_task_phid])) {
|
if (empty($task_can_edit_map[$batch_task_phid])) {
|
||||||
unset($batch_task_phids[$key]);
|
unset($batch_task_phids[$key]);
|
||||||
|
@ -250,10 +228,46 @@ final class PhabricatorProjectBoardViewController
|
||||||
|
|
||||||
$this->handles = ManiphestTaskListView::loadTaskHandles($viewer, $tasks);
|
$this->handles = ManiphestTaskListView::loadTaskHandles($viewer, $tasks);
|
||||||
|
|
||||||
|
$all_project_phids = array();
|
||||||
|
foreach ($tasks as $task) {
|
||||||
|
foreach ($task->getProjectPHIDs() as $project_phid) {
|
||||||
|
$all_project_phids[$project_phid] = $project_phid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($select_phids as $phid) {
|
||||||
|
unset($all_project_phids[$phid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$all_handles = $viewer->loadHandles($all_project_phids);
|
||||||
|
$all_handles = iterator_to_array($all_handles);
|
||||||
|
|
||||||
foreach ($columns as $column) {
|
foreach ($columns as $column) {
|
||||||
$task_phids = idx($task_map, $column->getPHID(), array());
|
if (!$this->showHidden) {
|
||||||
|
if ($column->isHidden()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$proxy = $column->getProxy();
|
||||||
|
if ($proxy && !$proxy->isMilestone()) {
|
||||||
|
// TODO: For now, don't show subproject columns because we can't
|
||||||
|
// handle tasks with multiple positions yet.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$task_phids = $layout_engine->getColumnObjectPHIDs(
|
||||||
|
$board_phid,
|
||||||
|
$column->getPHID());
|
||||||
|
|
||||||
$column_tasks = array_select_keys($tasks, $task_phids);
|
$column_tasks = array_select_keys($tasks, $task_phids);
|
||||||
|
|
||||||
|
// If we aren't using "natural" order, reorder the column by the original
|
||||||
|
// query order.
|
||||||
|
if ($this->sortKey != PhabricatorProjectColumn::ORDER_NATURAL) {
|
||||||
|
$column_tasks = array_select_keys($column_tasks, array_keys($tasks));
|
||||||
|
}
|
||||||
|
|
||||||
$panel = id(new PHUIWorkpanelView())
|
$panel = id(new PHUIWorkpanelView())
|
||||||
->setHeader($column->getDisplayName())
|
->setHeader($column->getDisplayName())
|
||||||
->setSubHeader($column->getDisplayType())
|
->setSubHeader($column->getDisplayType())
|
||||||
|
@ -264,6 +278,11 @@ final class PhabricatorProjectBoardViewController
|
||||||
$panel->setHeaderIcon($header_icon);
|
$panel->setHeaderIcon($header_icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$display_class = $column->getDisplayClass();
|
||||||
|
if ($display_class) {
|
||||||
|
$panel->addClass($display_class);
|
||||||
|
}
|
||||||
|
|
||||||
if ($column->isHidden()) {
|
if ($column->isHidden()) {
|
||||||
$panel->addClass('project-panel-hidden');
|
$panel->addClass('project-panel-hidden');
|
||||||
}
|
}
|
||||||
|
@ -288,6 +307,7 @@ final class PhabricatorProjectBoardViewController
|
||||||
->setFlush(true)
|
->setFlush(true)
|
||||||
->setAllowEmptyList(true)
|
->setAllowEmptyList(true)
|
||||||
->addSigil('project-column')
|
->addSigil('project-column')
|
||||||
|
->setItemClass('phui-workcard')
|
||||||
->setMetadata(
|
->setMetadata(
|
||||||
array(
|
array(
|
||||||
'columnPHID' => $column->getPHID(),
|
'columnPHID' => $column->getPHID(),
|
||||||
|
@ -302,8 +322,12 @@ final class PhabricatorProjectBoardViewController
|
||||||
$owner = $this->handles[$task->getOwnerPHID()];
|
$owner = $this->handles[$task->getOwnerPHID()];
|
||||||
}
|
}
|
||||||
$can_edit = idx($task_can_edit_map, $task->getPHID(), false);
|
$can_edit = idx($task_can_edit_map, $task->getPHID(), false);
|
||||||
|
|
||||||
|
$handles = array_select_keys($all_handles, $task->getProjectPHIDs());
|
||||||
|
|
||||||
$cards->addItem(id(new ProjectBoardTaskCard())
|
$cards->addItem(id(new ProjectBoardTaskCard())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
|
->setProjectHandles($handles)
|
||||||
->setTask($task)
|
->setTask($task)
|
||||||
->setOwner($owner)
|
->setOwner($owner)
|
||||||
->setCanEdit($can_edit)
|
->setCanEdit($can_edit)
|
||||||
|
@ -320,7 +344,7 @@ final class PhabricatorProjectBoardViewController
|
||||||
$filter_menu = $this->buildFilterMenu(
|
$filter_menu = $this->buildFilterMenu(
|
||||||
$viewer,
|
$viewer,
|
||||||
$custom_query,
|
$custom_query,
|
||||||
$engine,
|
$search_engine,
|
||||||
$query_key);
|
$query_key);
|
||||||
|
|
||||||
$manage_menu = $this->buildManageMenu($project, $this->showHidden);
|
$manage_menu = $this->buildManageMenu($project, $this->showHidden);
|
||||||
|
@ -347,7 +371,11 @@ final class PhabricatorProjectBoardViewController
|
||||||
$crumbs->addAction($manage_menu);
|
$crumbs->addAction($manage_menu);
|
||||||
|
|
||||||
return $this->newPage()
|
return $this->newPage()
|
||||||
->setTitle(pht('%s Board', $project->getName()))
|
->setTitle(
|
||||||
|
array(
|
||||||
|
$project->getDisplayName(),
|
||||||
|
pht('Workboard'),
|
||||||
|
))
|
||||||
->setPageObjectPHIDs(array($project->getPHID()))
|
->setPageObjectPHIDs(array($project->getPHID()))
|
||||||
->setShowFooter(false)
|
->setShowFooter(false)
|
||||||
->setNavigation($nav)
|
->setNavigation($nav)
|
||||||
|
@ -381,25 +409,6 @@ final class PhabricatorProjectBoardViewController
|
||||||
$this->sortKey = $sort_key;
|
$this->sortKey = $sort_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function loadColumns(PhabricatorProject $project) {
|
|
||||||
$viewer = $this->getViewer();
|
|
||||||
|
|
||||||
$column_query = id(new PhabricatorProjectColumnQuery())
|
|
||||||
->setViewer($viewer)
|
|
||||||
->withProjectPHIDs(array($project->getPHID()));
|
|
||||||
|
|
||||||
if (!$this->showHidden) {
|
|
||||||
$column_query->withStatuses(
|
|
||||||
array(PhabricatorProjectColumn::STATUS_ACTIVE));
|
|
||||||
}
|
|
||||||
|
|
||||||
$columns = $column_query->execute();
|
|
||||||
$columns = mpull($columns, null, 'getSequence');
|
|
||||||
ksort($columns);
|
|
||||||
|
|
||||||
return $columns;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function buildSortMenu(
|
private function buildSortMenu(
|
||||||
PhabricatorUser $viewer,
|
PhabricatorUser $viewer,
|
||||||
$sort_key) {
|
$sort_key) {
|
||||||
|
@ -616,6 +625,12 @@ final class PhabricatorProjectBoardViewController
|
||||||
|
|
||||||
$column_items = array();
|
$column_items = array();
|
||||||
|
|
||||||
|
if ($column->getProxyPHID()) {
|
||||||
|
$default_phid = $column->getProxyPHID();
|
||||||
|
} else {
|
||||||
|
$default_phid = $column->getProjectPHID();
|
||||||
|
}
|
||||||
|
|
||||||
$column_items[] = id(new PhabricatorActionView())
|
$column_items[] = id(new PhabricatorActionView())
|
||||||
->setIcon('fa-plus')
|
->setIcon('fa-plus')
|
||||||
->setName(pht('Create Task...'))
|
->setName(pht('Create Task...'))
|
||||||
|
@ -624,6 +639,7 @@ final class PhabricatorProjectBoardViewController
|
||||||
->setMetadata(
|
->setMetadata(
|
||||||
array(
|
array(
|
||||||
'columnPHID' => $column->getPHID(),
|
'columnPHID' => $column->getPHID(),
|
||||||
|
'projectPHID' => $default_phid,
|
||||||
));
|
));
|
||||||
|
|
||||||
$batch_edit_uri = $request->getRequestURI();
|
$batch_edit_uri = $request->getRequestURI();
|
||||||
|
@ -772,6 +788,10 @@ final class PhabricatorProjectBoardViewController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Tailor this UI if the project is already a parent project. We
|
||||||
|
// should not offer options for creating a parent project workboard, since
|
||||||
|
// they can't have their own columns.
|
||||||
|
|
||||||
$new_selector = id(new AphrontFormRadioButtonControl())
|
$new_selector = id(new AphrontFormRadioButtonControl())
|
||||||
->setLabel(pht('Columns'))
|
->setLabel(pht('Columns'))
|
||||||
->setName('initialize-type')
|
->setName('initialize-type')
|
||||||
|
@ -795,6 +815,7 @@ final class PhabricatorProjectBoardViewController
|
||||||
|
|
||||||
$form = id(new AphrontFormView())
|
$form = id(new AphrontFormView())
|
||||||
->setUser($viewer)
|
->setUser($viewer)
|
||||||
|
->addHiddenInput('initialize', 1)
|
||||||
->appendRemarkupInstructions(
|
->appendRemarkupInstructions(
|
||||||
pht('The workboard for this project has not been created yet.'))
|
pht('The workboard for this project has not been created yet.'))
|
||||||
->appendControl($new_selector)
|
->appendControl($new_selector)
|
||||||
|
|
|
@ -22,6 +22,8 @@ final class PhabricatorProjectColumnDetailController
|
||||||
}
|
}
|
||||||
$this->setProject($project);
|
$this->setProject($project);
|
||||||
|
|
||||||
|
$project_id = $project->getID();
|
||||||
|
|
||||||
$column = id(new PhabricatorProjectColumnQuery())
|
$column = id(new PhabricatorProjectColumnQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withIDs(array($id))
|
->withIDs(array($id))
|
||||||
|
@ -45,6 +47,10 @@ final class PhabricatorProjectColumnDetailController
|
||||||
$actions = $this->buildActionView($column);
|
$actions = $this->buildActionView($column);
|
||||||
$properties = $this->buildPropertyView($column, $actions);
|
$properties = $this->buildPropertyView($column, $actions);
|
||||||
|
|
||||||
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
|
$crumbs->addTextCrumb(pht('Workboard'), "/project/board/{$project_id}/");
|
||||||
|
$crumbs->addTextCrumb(pht('Column: %s', $title));
|
||||||
|
|
||||||
$box = id(new PHUIObjectBoxView())
|
$box = id(new PHUIObjectBoxView())
|
||||||
->setHeader($header)
|
->setHeader($header)
|
||||||
->addPropertyList($properties);
|
->addPropertyList($properties);
|
||||||
|
@ -54,6 +60,7 @@ final class PhabricatorProjectColumnDetailController
|
||||||
return $this->newPage()
|
return $this->newPage()
|
||||||
->setTitle($title)
|
->setTitle($title)
|
||||||
->setNavigation($nav)
|
->setNavigation($nav)
|
||||||
|
->setCrumbs($crumbs)
|
||||||
->appendChild(
|
->appendChild(
|
||||||
array(
|
array(
|
||||||
$box,
|
$box,
|
||||||
|
|
|
@ -81,10 +81,12 @@ final class PhabricatorProjectColumnEditController
|
||||||
|
|
||||||
$xactions = array();
|
$xactions = array();
|
||||||
|
|
||||||
|
if (!$column->getProxy()) {
|
||||||
$type_name = PhabricatorProjectColumnTransaction::TYPE_NAME;
|
$type_name = PhabricatorProjectColumnTransaction::TYPE_NAME;
|
||||||
$xactions[] = id(new PhabricatorProjectColumnTransaction())
|
$xactions[] = id(new PhabricatorProjectColumnTransaction())
|
||||||
->setTransactionType($type_name)
|
->setTransactionType($type_name)
|
||||||
->setNewValue($v_name);
|
->setNewValue($v_name);
|
||||||
|
}
|
||||||
|
|
||||||
$type_limit = PhabricatorProjectColumnTransaction::TYPE_LIMIT;
|
$type_limit = PhabricatorProjectColumnTransaction::TYPE_LIMIT;
|
||||||
$xactions[] = id(new PhabricatorProjectColumnTransaction())
|
$xactions[] = id(new PhabricatorProjectColumnTransaction())
|
||||||
|
@ -105,18 +107,19 @@ final class PhabricatorProjectColumnEditController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$form = new AphrontFormView();
|
$form = id(new AphrontFormView())
|
||||||
$form
|
->setUser($request->getUser());
|
||||||
->setUser($request->getUser())
|
|
||||||
->appendChild(
|
if (!$column->getProxy()) {
|
||||||
|
$form->appendChild(
|
||||||
id(new AphrontFormTextControl())
|
id(new AphrontFormTextControl())
|
||||||
->setValue($v_name)
|
->setValue($v_name)
|
||||||
->setLabel(pht('Name'))
|
->setLabel(pht('Name'))
|
||||||
->setName('name')
|
->setName('name')
|
||||||
->setError($e_name)
|
->setError($e_name));
|
||||||
->setCaption(
|
}
|
||||||
pht('This will be displayed as the header of the column.')))
|
|
||||||
->appendChild(
|
$form->appendChild(
|
||||||
id(new AphrontFormTextControl())
|
id(new AphrontFormTextControl())
|
||||||
->setValue($v_limit)
|
->setValue($v_limit)
|
||||||
->setLabel(pht('Point Limit'))
|
->setLabel(pht('Point Limit'))
|
||||||
|
@ -125,7 +128,6 @@ final class PhabricatorProjectColumnEditController
|
||||||
->setCaption(
|
->setCaption(
|
||||||
pht('Maximum number of points of tasks allowed in the column.')));
|
pht('Maximum number of points of tasks allowed in the column.')));
|
||||||
|
|
||||||
|
|
||||||
if ($is_new) {
|
if ($is_new) {
|
||||||
$title = pht('Create Column');
|
$title = pht('Create Column');
|
||||||
$submit = pht('Create Column');
|
$submit = pht('Create Column');
|
||||||
|
|
|
@ -42,6 +42,7 @@ final class PhabricatorProjectEditController
|
||||||
if ($parent_id) {
|
if ($parent_id) {
|
||||||
$query = id(new PhabricatorProjectQuery())
|
$query = id(new PhabricatorProjectQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
|
->needImages(true)
|
||||||
->requireCapabilities(
|
->requireCapabilities(
|
||||||
array(
|
array(
|
||||||
PhabricatorPolicyCapability::CAN_VIEW,
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
@ -58,7 +59,7 @@ final class PhabricatorProjectEditController
|
||||||
|
|
||||||
if ($is_milestone) {
|
if ($is_milestone) {
|
||||||
if (!$parent->supportsMilestones()) {
|
if (!$parent->supportsMilestones()) {
|
||||||
$cancel_uri = "/project/milestones/{$parent_id}/";
|
$cancel_uri = "/project/subprojects/{$parent_id}/";
|
||||||
return $this->newDialog()
|
return $this->newDialog()
|
||||||
->setTitle(pht('No Milestones'))
|
->setTitle(pht('No Milestones'))
|
||||||
->appendParagraph(
|
->appendParagraph(
|
||||||
|
@ -91,20 +92,13 @@ final class PhabricatorProjectEditController
|
||||||
$engine = $this->getEngine();
|
$engine = $this->getEngine();
|
||||||
if ($engine) {
|
if ($engine) {
|
||||||
$parent = $engine->getParentProject();
|
$parent = $engine->getParentProject();
|
||||||
if ($parent) {
|
$milestone = $engine->getMilestoneProject();
|
||||||
$id = $parent->getID();
|
if ($parent || $milestone) {
|
||||||
|
$id = nonempty($parent, $milestone)->getID();
|
||||||
$crumbs->addTextCrumb(
|
$crumbs->addTextCrumb(
|
||||||
pht('Subprojects'),
|
pht('Subprojects'),
|
||||||
$this->getApplicationURI("subprojects/{$id}/"));
|
$this->getApplicationURI("subprojects/{$id}/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
$milestone = $engine->getMilestoneProject();
|
|
||||||
if ($milestone) {
|
|
||||||
$id = $milestone->getID();
|
|
||||||
$crumbs->addTextCrumb(
|
|
||||||
pht('Milestones'),
|
|
||||||
$this->getApplicationURI("milestones/{$id}/"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $crumbs;
|
return $crumbs;
|
||||||
|
|
|
@ -51,7 +51,11 @@ final class PhabricatorProjectManageController
|
||||||
return $this->newPage()
|
return $this->newPage()
|
||||||
->setNavigation($nav)
|
->setNavigation($nav)
|
||||||
->setCrumbs($crumbs)
|
->setCrumbs($crumbs)
|
||||||
->setTitle($project->getName())
|
->setTitle(
|
||||||
|
array(
|
||||||
|
$project->getDisplayName(),
|
||||||
|
pht('Manage'),
|
||||||
|
))
|
||||||
->appendChild(
|
->appendChild(
|
||||||
array(
|
array(
|
||||||
$object_box,
|
$object_box,
|
||||||
|
|
|
@ -48,7 +48,7 @@ final class PhabricatorProjectMembersViewController
|
||||||
return $this->newPage()
|
return $this->newPage()
|
||||||
->setNavigation($nav)
|
->setNavigation($nav)
|
||||||
->setCrumbs($crumbs)
|
->setCrumbs($crumbs)
|
||||||
->setTitle(array($project->getName(), $title))
|
->setTitle(array($project->getDisplayName(), $title))
|
||||||
->appendChild(
|
->appendChild(
|
||||||
array(
|
array(
|
||||||
$object_box,
|
$object_box,
|
||||||
|
|
|
@ -1,92 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class PhabricatorProjectMilestonesController
|
|
||||||
extends PhabricatorProjectController {
|
|
||||||
|
|
||||||
public function shouldAllowPublic() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handleRequest(AphrontRequest $request) {
|
|
||||||
$viewer = $request->getViewer();
|
|
||||||
|
|
||||||
$response = $this->loadProject();
|
|
||||||
if ($response) {
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
$project = $this->getProject();
|
|
||||||
$id = $project->getID();
|
|
||||||
|
|
||||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
|
||||||
$viewer,
|
|
||||||
$project,
|
|
||||||
PhabricatorPolicyCapability::CAN_EDIT);
|
|
||||||
|
|
||||||
$has_support = $project->supportsMilestones();
|
|
||||||
if ($has_support) {
|
|
||||||
$milestones = id(new PhabricatorProjectQuery())
|
|
||||||
->setViewer($viewer)
|
|
||||||
->withParentProjectPHIDs(array($project->getPHID()))
|
|
||||||
->needImages(true)
|
|
||||||
->withIsMilestone(true)
|
|
||||||
->setOrder('newest')
|
|
||||||
->execute();
|
|
||||||
} else {
|
|
||||||
$milestones = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$can_create = $can_edit && $has_support;
|
|
||||||
|
|
||||||
if ($project->getHasMilestones()) {
|
|
||||||
$button_text = pht('Create Next Milestone');
|
|
||||||
} else {
|
|
||||||
$button_text = pht('Add Milestones');
|
|
||||||
}
|
|
||||||
|
|
||||||
$header = id(new PHUIHeaderView())
|
|
||||||
->setHeader(pht('Milestones'))
|
|
||||||
->addActionLink(
|
|
||||||
id(new PHUIButtonView())
|
|
||||||
->setTag('a')
|
|
||||||
->setHref("/project/edit/?milestone={$id}")
|
|
||||||
->setIcon('fa-plus')
|
|
||||||
->setDisabled(!$can_create)
|
|
||||||
->setWorkflow(!$can_create)
|
|
||||||
->setText($button_text));
|
|
||||||
|
|
||||||
$box = id(new PHUIObjectBoxView())
|
|
||||||
->setHeader($header);
|
|
||||||
|
|
||||||
if (!$has_support) {
|
|
||||||
$no_support = pht(
|
|
||||||
'This project is a milestone. Milestones can not have their own '.
|
|
||||||
'milestones.');
|
|
||||||
|
|
||||||
$info_view = id(new PHUIInfoView())
|
|
||||||
->setErrors(array($no_support))
|
|
||||||
->setSeverity(PHUIInfoView::SEVERITY_WARNING);
|
|
||||||
|
|
||||||
$box->setInfoView($info_view);
|
|
||||||
}
|
|
||||||
|
|
||||||
$box->setObjectList(
|
|
||||||
id(new PhabricatorProjectListView())
|
|
||||||
->setUser($viewer)
|
|
||||||
->setProjects($milestones)
|
|
||||||
->renderList());
|
|
||||||
|
|
||||||
$nav = $this->getProfileMenu();
|
|
||||||
$nav->selectFilter(PhabricatorProject::PANEL_MILESTONES);
|
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
|
||||||
$crumbs->addTextCrumb(pht('Milestones'));
|
|
||||||
|
|
||||||
return $this->newPage()
|
|
||||||
->setNavigation($nav)
|
|
||||||
->setCrumbs($crumbs)
|
|
||||||
->setTitle(array($project->getName(), pht('Milestones')))
|
|
||||||
->appendChild($box);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -26,9 +26,12 @@ final class PhabricatorProjectMoveController
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
$object = id(new PhabricatorObjectQuery())
|
$board_phid = $project->getPHID();
|
||||||
|
|
||||||
|
$object = id(new ManiphestTaskQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withPHIDs(array($object_phid))
|
->withPHIDs(array($object_phid))
|
||||||
|
->needProjectPHIDs(true)
|
||||||
->requireCapabilities(
|
->requireCapabilities(
|
||||||
array(
|
array(
|
||||||
PhabricatorPolicyCapability::CAN_VIEW,
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
@ -53,11 +56,14 @@ final class PhabricatorProjectMoveController
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
$positions = id(new PhabricatorProjectColumnPositionQuery())
|
$engine = id(new PhabricatorBoardLayoutEngine())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withColumns($columns)
|
->setBoardPHIDs(array($board_phid))
|
||||||
->withObjectPHIDs(array($object_phid))
|
->setObjectPHIDs(array($object_phid))
|
||||||
->execute();
|
->executeLayout();
|
||||||
|
|
||||||
|
$columns = $engine->getObjectColumns($board_phid, $object_phid);
|
||||||
|
$old_column_phids = mpull($columns, 'getPHID');
|
||||||
|
|
||||||
$xactions = array();
|
$xactions = array();
|
||||||
|
|
||||||
|
@ -79,7 +85,7 @@ final class PhabricatorProjectMoveController
|
||||||
) + $order_params)
|
) + $order_params)
|
||||||
->setOldValue(
|
->setOldValue(
|
||||||
array(
|
array(
|
||||||
'columnPHIDs' => mpull($positions, 'getColumnPHID'),
|
'columnPHIDs' => $old_column_phids,
|
||||||
'projectPHID' => $column->getProjectPHID(),
|
'projectPHID' => $column->getProjectPHID(),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -95,6 +101,7 @@ final class PhabricatorProjectMoveController
|
||||||
$tasks = id(new ManiphestTaskQuery())
|
$tasks = id(new ManiphestTaskQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withPHIDs($task_phids)
|
->withPHIDs($task_phids)
|
||||||
|
->needProjectPHIDs(true)
|
||||||
->requireCapabilities(
|
->requireCapabilities(
|
||||||
array(
|
array(
|
||||||
PhabricatorPolicyCapability::CAN_VIEW,
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
@ -134,6 +141,32 @@ final class PhabricatorProjectMoveController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$proxy = $column->getProxy();
|
||||||
|
if ($proxy) {
|
||||||
|
// We're moving the task into a subproject or milestone column, so add
|
||||||
|
// the subproject or milestone.
|
||||||
|
$add_projects = array($proxy->getPHID());
|
||||||
|
} else if ($project->getHasSubprojects() || $project->getHasMilestones()) {
|
||||||
|
// We're moving the task into the "Backlog" column on the parent project,
|
||||||
|
// so add the parent explicitly. This gets rid of any subproject or
|
||||||
|
// milestone tags.
|
||||||
|
$add_projects = array($project->getPHID());
|
||||||
|
} else {
|
||||||
|
$add_projects = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($add_projects) {
|
||||||
|
$project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
|
||||||
|
|
||||||
|
$xactions[] = id(new ManiphestTransaction())
|
||||||
|
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
|
||||||
|
->setMetadataValue('edge:type', $project_type)
|
||||||
|
->setNewValue(
|
||||||
|
array(
|
||||||
|
'+' => array_fuse($add_projects),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
$editor = id(new ManiphestTransactionEditor())
|
$editor = id(new ManiphestTransactionEditor())
|
||||||
->setActor($viewer)
|
->setActor($viewer)
|
||||||
->setContinueOnMissingFields(true)
|
->setContinueOnMissingFields(true)
|
||||||
|
@ -149,13 +182,47 @@ final class PhabricatorProjectMoveController
|
||||||
->withPHIDs(array($object->getOwnerPHID()))
|
->withPHIDs(array($object->getOwnerPHID()))
|
||||||
->executeOne();
|
->executeOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reload the object so it reflects edits which have been applied.
|
||||||
|
$object = id(new ManiphestTaskQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs(array($object_phid))
|
||||||
|
->needProjectPHIDs(true)
|
||||||
|
->requireCapabilities(
|
||||||
|
array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
))
|
||||||
|
->executeOne();
|
||||||
|
|
||||||
|
$except_phids = array($board_phid);
|
||||||
|
if ($project->getHasSubprojects() || $project->getHasMilestones()) {
|
||||||
|
$descendants = id(new PhabricatorProjectQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withAncestorProjectPHIDs($except_phids)
|
||||||
|
->execute();
|
||||||
|
foreach ($descendants as $descendant) {
|
||||||
|
$except_phids[] = $descendant->getPHID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$except_phids = array_fuse($except_phids);
|
||||||
|
$handle_phids = array_fuse($object->getProjectPHIDs());
|
||||||
|
$handle_phids = array_diff_key($handle_phids, $except_phids);
|
||||||
|
|
||||||
|
$project_handles = $viewer->loadHandles($handle_phids);
|
||||||
|
$project_handles = iterator_to_array($project_handles);
|
||||||
|
|
||||||
$card = id(new ProjectBoardTaskCard())
|
$card = id(new ProjectBoardTaskCard())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->setTask($object)
|
->setTask($object)
|
||||||
->setOwner($owner)
|
->setOwner($owner)
|
||||||
->setCanEdit(true)
|
->setCanEdit(true)
|
||||||
|
->setProjectHandles($project_handles)
|
||||||
->getItem();
|
->getItem();
|
||||||
|
|
||||||
|
$card->addClass('phui-workcard');
|
||||||
|
|
||||||
return id(new AphrontAjaxResponse())->setContent(
|
return id(new AphrontAjaxResponse())->setContent(
|
||||||
array('task' => $card));
|
array('task' => $card));
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,16 @@ final class PhabricatorProjectProfileController
|
||||||
$project = $this->getProject();
|
$project = $this->getProject();
|
||||||
$id = $project->getID();
|
$id = $project->getID();
|
||||||
$picture = $project->getProfileImageURI();
|
$picture = $project->getProfileImageURI();
|
||||||
|
$icon = $project->getDisplayIconIcon();
|
||||||
|
$icon_name = $project->getDisplayIconName();
|
||||||
|
$tag = id(new PHUITagView())
|
||||||
|
->setIcon($icon)
|
||||||
|
->setName($icon_name)
|
||||||
|
->addClass('project-view-header-tag')
|
||||||
|
->setType(PHUITagView::TYPE_SHADE);
|
||||||
|
|
||||||
$header = id(new PHUIHeaderView())
|
$header = id(new PHUIHeaderView())
|
||||||
->setHeader($project->getName())
|
->setHeader(array($project->getDisplayName(), $tag))
|
||||||
->setUser($viewer)
|
->setUser($viewer)
|
||||||
->setPolicyObject($project)
|
->setPolicyObject($project)
|
||||||
->setImage($picture)
|
->setImage($picture)
|
||||||
|
@ -45,6 +52,9 @@ final class PhabricatorProjectProfileController
|
||||||
$watch_action = $this->renderWatchAction($project);
|
$watch_action = $this->renderWatchAction($project);
|
||||||
$header->addActionLink($watch_action);
|
$header->addActionLink($watch_action);
|
||||||
|
|
||||||
|
$milestone_list = $this->buildMilestoneList($project);
|
||||||
|
$subproject_list = $this->buildSubprojectList($project);
|
||||||
|
|
||||||
$member_list = id(new PhabricatorProjectMemberListView())
|
$member_list = id(new PhabricatorProjectMemberListView())
|
||||||
->setUser($viewer)
|
->setUser($viewer)
|
||||||
->setProject($project)
|
->setProject($project)
|
||||||
|
@ -82,6 +92,8 @@ final class PhabricatorProjectProfileController
|
||||||
))
|
))
|
||||||
->setSideColumn(
|
->setSideColumn(
|
||||||
array(
|
array(
|
||||||
|
$milestone_list,
|
||||||
|
$subproject_list,
|
||||||
$member_list,
|
$member_list,
|
||||||
$watcher_list,
|
$watcher_list,
|
||||||
));
|
));
|
||||||
|
@ -103,7 +115,7 @@ final class PhabricatorProjectProfileController
|
||||||
return $this->newPage()
|
return $this->newPage()
|
||||||
->setNavigation($nav)
|
->setNavigation($nav)
|
||||||
->setCrumbs($crumbs)
|
->setCrumbs($crumbs)
|
||||||
->setTitle($project->getName())
|
->setTitle($project->getDisplayName())
|
||||||
->setPageObjectPHIDs(array($project->getPHID()))
|
->setPageObjectPHIDs(array($project->getPHID()))
|
||||||
->appendChild(
|
->appendChild(
|
||||||
array(
|
array(
|
||||||
|
@ -125,7 +137,7 @@ final class PhabricatorProjectProfileController
|
||||||
PhabricatorCustomField::ROLE_VIEW);
|
PhabricatorCustomField::ROLE_VIEW);
|
||||||
$field_list->appendFieldsToPropertyList($project, $viewer, $view);
|
$field_list->appendFieldsToPropertyList($project, $viewer, $view);
|
||||||
|
|
||||||
if ($view->isEmpty()) {
|
if (!$view->hasAnyProperties()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,5 +188,90 @@ final class PhabricatorProjectProfileController
|
||||||
->setHref($watch_href);
|
->setHref($watch_href);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function buildMilestoneList(PhabricatorProject $project) {
|
||||||
|
if (!$project->getHasMilestones()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$id = $project->getID();
|
||||||
|
|
||||||
|
$milestones = id(new PhabricatorProjectQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withParentProjectPHIDs(array($project->getPHID()))
|
||||||
|
->needImages(true)
|
||||||
|
->withIsMilestone(true)
|
||||||
|
->setOrder('newest')
|
||||||
|
->execute();
|
||||||
|
if (!$milestones) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$milestone_list = id(new PhabricatorProjectListView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setProjects($milestones)
|
||||||
|
->renderList();
|
||||||
|
|
||||||
|
$view_all = id(new PHUIButtonView())
|
||||||
|
->setTag('a')
|
||||||
|
->setIcon(
|
||||||
|
id(new PHUIIconView())
|
||||||
|
->setIcon('fa-list-ul'))
|
||||||
|
->setText(pht('View All'))
|
||||||
|
->setHref("/project/subprojects/{$id}/");
|
||||||
|
|
||||||
|
$header = id(new PHUIHeaderView())
|
||||||
|
->setHeader(pht('Milestones'))
|
||||||
|
->addActionLink($view_all);
|
||||||
|
|
||||||
|
return id(new PHUIObjectBoxView())
|
||||||
|
->setHeader($header)
|
||||||
|
->setBackground(PHUIBoxView::GREY)
|
||||||
|
->setObjectList($milestone_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildSubprojectList(PhabricatorProject $project) {
|
||||||
|
if (!$project->getHasSubprojects()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$id = $project->getID();
|
||||||
|
|
||||||
|
$limit = 25;
|
||||||
|
|
||||||
|
$subprojects = id(new PhabricatorProjectQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withParentProjectPHIDs(array($project->getPHID()))
|
||||||
|
->needImages(true)
|
||||||
|
->withIsMilestone(false)
|
||||||
|
->setLimit($limit)
|
||||||
|
->execute();
|
||||||
|
if (!$subprojects) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$subproject_list = id(new PhabricatorProjectListView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setProjects($subprojects)
|
||||||
|
->renderList();
|
||||||
|
|
||||||
|
$view_all = id(new PHUIButtonView())
|
||||||
|
->setTag('a')
|
||||||
|
->setIcon(
|
||||||
|
id(new PHUIIconView())
|
||||||
|
->setIcon('fa-list-ul'))
|
||||||
|
->setText(pht('View All'))
|
||||||
|
->setHref("/project/subprojects/{$id}/");
|
||||||
|
|
||||||
|
$header = id(new PHUIHeaderView())
|
||||||
|
->setHeader(pht('Subprojects'))
|
||||||
|
->addActionLink($view_all);
|
||||||
|
|
||||||
|
return id(new PHUIObjectBoxView())
|
||||||
|
->setHeader($header)
|
||||||
|
->setBackground(PHUIBoxView::GREY)
|
||||||
|
->setObjectList($subproject_list);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorProjectSubprojectWarningController
|
||||||
|
extends PhabricatorProjectController {
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$viewer = $request->getViewer();
|
||||||
|
|
||||||
|
$response = $this->loadProject();
|
||||||
|
if ($response) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
$project = $this->getProject();
|
||||||
|
|
||||||
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||||
|
$viewer,
|
||||||
|
$project,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT);
|
||||||
|
|
||||||
|
if (!$can_edit) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $project->getID();
|
||||||
|
$cancel_uri = "/project/subprojects/{$id}/";
|
||||||
|
$done_uri = "/project/edit/?parent={$id}";
|
||||||
|
|
||||||
|
if ($request->isFormPost()) {
|
||||||
|
return id(new AphrontRedirectResponse())
|
||||||
|
->setURI($done_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
$doc_href = PhabricatorEnv::getDoclink('Projects User Guide');
|
||||||
|
|
||||||
|
$conversion_help = pht(
|
||||||
|
"Creating a project's first subproject **moves all ".
|
||||||
|
"members** to become members of the subproject instead".
|
||||||
|
"\n\n".
|
||||||
|
"See [[ %s | Projects User Guide ]] in the documentation for details. ".
|
||||||
|
"This process can not be undone.",
|
||||||
|
$doc_href);
|
||||||
|
|
||||||
|
return $this->newDialog()
|
||||||
|
->setTitle(pht('Convert to Parent Project'))
|
||||||
|
->appendChild(new PHUIRemarkupView($viewer, $conversion_help))
|
||||||
|
->addCancelButton($cancel_uri)
|
||||||
|
->addSubmitButton(pht('Convert Project'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -23,9 +23,10 @@ final class PhabricatorProjectSubprojectsController
|
||||||
$project,
|
$project,
|
||||||
PhabricatorPolicyCapability::CAN_EDIT);
|
PhabricatorPolicyCapability::CAN_EDIT);
|
||||||
|
|
||||||
$has_support = $project->supportsSubprojects();
|
$allows_subprojects = $project->supportsSubprojects();
|
||||||
|
$allows_milestones = $project->supportsMilestones();
|
||||||
|
|
||||||
if ($has_support) {
|
if ($allows_subprojects) {
|
||||||
$subprojects = id(new PhabricatorProjectQuery())
|
$subprojects = id(new PhabricatorProjectQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withParentProjectPHIDs(array($project->getPHID()))
|
->withParentProjectPHIDs(array($project->getPHID()))
|
||||||
|
@ -36,44 +37,57 @@ final class PhabricatorProjectSubprojectsController
|
||||||
$subprojects = array();
|
$subprojects = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$can_create = $can_edit && $has_support;
|
if ($allows_milestones) {
|
||||||
|
$milestones = id(new PhabricatorProjectQuery())
|
||||||
if ($project->getHasSubprojects()) {
|
->setViewer($viewer)
|
||||||
$button_text = pht('Create Subproject');
|
->withParentProjectPHIDs(array($project->getPHID()))
|
||||||
|
->needImages(true)
|
||||||
|
->withIsMilestone(true)
|
||||||
|
->setOrder('newest')
|
||||||
|
->execute();
|
||||||
} else {
|
} else {
|
||||||
$button_text = pht('Add Subprojects');
|
$milestones = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$header = id(new PHUIHeaderView())
|
if ($milestones) {
|
||||||
->setHeader(pht('Subprojects'))
|
$milestone_list = id(new PHUIObjectBoxView())
|
||||||
->addActionLink(
|
->setHeaderText(pht('Milestones'))
|
||||||
id(new PHUIButtonView())
|
->setObjectList(
|
||||||
->setTag('a')
|
id(new PhabricatorProjectListView())
|
||||||
->setHref("/project/edit/?parent={$id}")
|
->setUser($viewer)
|
||||||
->setIcon('fa-plus')
|
->setProjects($milestones)
|
||||||
->setDisabled(!$can_create)
|
->renderList());
|
||||||
->setWorkflow(!$can_create)
|
} else {
|
||||||
->setText($button_text));
|
$milestone_list = null;
|
||||||
|
|
||||||
$box = id(new PHUIObjectBoxView())
|
|
||||||
->setHeader($header);
|
|
||||||
|
|
||||||
if (!$has_support) {
|
|
||||||
$no_support = pht(
|
|
||||||
'This project is a milestone. Milestones can not have subprojects.');
|
|
||||||
|
|
||||||
$info_view = id(new PHUIInfoView())
|
|
||||||
->setErrors(array($no_support))
|
|
||||||
->setSeverity(PHUIInfoView::SEVERITY_WARNING);
|
|
||||||
|
|
||||||
$box->setInfoView($info_view);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$box->setObjectList(
|
if ($subprojects) {
|
||||||
|
$subproject_list = id(new PHUIObjectBoxView())
|
||||||
|
->setHeaderText(pht('Subprojects'))
|
||||||
|
->setObjectList(
|
||||||
id(new PhabricatorProjectListView())
|
id(new PhabricatorProjectListView())
|
||||||
->setUser($viewer)
|
->setUser($viewer)
|
||||||
->setProjects($subprojects)
|
->setProjects($subprojects)
|
||||||
->renderList());
|
->renderList());
|
||||||
|
} else {
|
||||||
|
$subproject_list = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$property_list = $this->buildPropertyList(
|
||||||
|
$project,
|
||||||
|
$milestones,
|
||||||
|
$subprojects);
|
||||||
|
|
||||||
|
$action_list = $this->buildActionList(
|
||||||
|
$project,
|
||||||
|
$milestones,
|
||||||
|
$subprojects);
|
||||||
|
|
||||||
|
$property_list->setActionList($action_list);
|
||||||
|
|
||||||
|
$header_box = id(new PHUIObjectBoxView())
|
||||||
|
->setHeaderText(pht('Subprojects and Milestones'))
|
||||||
|
->addPropertyList($property_list);
|
||||||
|
|
||||||
$nav = $this->getProfileMenu();
|
$nav = $this->getProfileMenu();
|
||||||
$nav->selectFilter(PhabricatorProject::PANEL_SUBPROJECTS);
|
$nav->selectFilter(PhabricatorProject::PANEL_SUBPROJECTS);
|
||||||
|
@ -85,7 +99,151 @@ final class PhabricatorProjectSubprojectsController
|
||||||
->setNavigation($nav)
|
->setNavigation($nav)
|
||||||
->setCrumbs($crumbs)
|
->setCrumbs($crumbs)
|
||||||
->setTitle(array($project->getName(), pht('Subprojects')))
|
->setTitle(array($project->getName(), pht('Subprojects')))
|
||||||
->appendChild($box);
|
->appendChild(
|
||||||
|
array(
|
||||||
|
$header_box,
|
||||||
|
$milestone_list,
|
||||||
|
$subproject_list,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function buildPropertyList(
|
||||||
|
PhabricatorProject $project,
|
||||||
|
array $milestones,
|
||||||
|
array $subprojects) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$view = id(new PHUIPropertyListView())
|
||||||
|
->setUser($viewer);
|
||||||
|
|
||||||
|
$view->addProperty(
|
||||||
|
pht('Prototype'),
|
||||||
|
$this->renderStatus(
|
||||||
|
'fa-exclamation-triangle red',
|
||||||
|
pht('Warning'),
|
||||||
|
pht('Subprojects and milestones are only partially implemented.')));
|
||||||
|
|
||||||
|
if (!$project->supportsMilestones()) {
|
||||||
|
$milestone_status = $this->renderStatus(
|
||||||
|
'fa-times grey',
|
||||||
|
pht('Already Milestone'),
|
||||||
|
pht(
|
||||||
|
'This project is already a milestone, and milestones may not '.
|
||||||
|
'have their own milestones.'));
|
||||||
|
} else {
|
||||||
|
if (!$milestones) {
|
||||||
|
$milestone_status = $this->renderStatus(
|
||||||
|
'fa-check grey',
|
||||||
|
pht('None Created'),
|
||||||
|
pht(
|
||||||
|
'You can create milestones for this project.'));
|
||||||
|
} else {
|
||||||
|
$milestone_status = $this->renderStatus(
|
||||||
|
'fa-check green',
|
||||||
|
pht('Has Milestones'),
|
||||||
|
pht('This project has milestones.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$view->addProperty(pht('Milestones'), $milestone_status);
|
||||||
|
|
||||||
|
if (!$project->supportsSubprojects()) {
|
||||||
|
$subproject_status = $this->renderStatus(
|
||||||
|
'fa-times grey',
|
||||||
|
pht('Milestone'),
|
||||||
|
pht(
|
||||||
|
'This project is a milestone, and milestones may not have '.
|
||||||
|
'subprojects.'));
|
||||||
|
} else {
|
||||||
|
if (!$subprojects) {
|
||||||
|
$subproject_status = $this->renderStatus(
|
||||||
|
'fa-check grey',
|
||||||
|
pht('None Created'),
|
||||||
|
pht('You can create subprojects for this project.'));
|
||||||
|
} else {
|
||||||
|
$subproject_status = $this->renderStatus(
|
||||||
|
'fa-check green',
|
||||||
|
pht('Has Subprojects'),
|
||||||
|
pht(
|
||||||
|
'This project has subprojects.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$view->addProperty(pht('Subprojects'), $subproject_status);
|
||||||
|
|
||||||
|
return $view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildActionList(
|
||||||
|
PhabricatorProject $project,
|
||||||
|
array $milestones,
|
||||||
|
array $subprojects) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$id = $project->getID();
|
||||||
|
|
||||||
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||||
|
$viewer,
|
||||||
|
$project,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT);
|
||||||
|
|
||||||
|
$allows_milestones = $project->supportsMilestones();
|
||||||
|
$allows_subprojects = $project->supportsSubprojects();
|
||||||
|
|
||||||
|
$view = id(new PhabricatorActionListView())
|
||||||
|
->setUser($viewer);
|
||||||
|
|
||||||
|
if ($allows_milestones && $milestones) {
|
||||||
|
$milestone_text = pht('Create Next Milestone');
|
||||||
|
} else {
|
||||||
|
$milestone_text = pht('Create Milestone');
|
||||||
|
}
|
||||||
|
|
||||||
|
$can_milestone = ($can_edit && $allows_milestones);
|
||||||
|
$milestone_href = "/project/edit/?milestone={$id}";
|
||||||
|
|
||||||
|
$view->addAction(
|
||||||
|
id(new PhabricatorActionView())
|
||||||
|
->setName($milestone_text)
|
||||||
|
->setIcon('fa-plus')
|
||||||
|
->setHref($milestone_href)
|
||||||
|
->setDisabled(!$can_milestone)
|
||||||
|
->setWorkflow(!$can_milestone));
|
||||||
|
|
||||||
|
$can_subproject = ($can_edit && $allows_subprojects);
|
||||||
|
|
||||||
|
// If we're offering to create the first subproject, we're going to warn
|
||||||
|
// the user about the effects before moving forward.
|
||||||
|
if ($can_subproject && !$subprojects) {
|
||||||
|
$subproject_href = "/project/warning/{$id}/";
|
||||||
|
$subproject_disabled = false;
|
||||||
|
$subproject_workflow = true;
|
||||||
|
} else {
|
||||||
|
$subproject_href = "/project/edit/?parent={$id}";
|
||||||
|
$subproject_disabled = !$can_subproject;
|
||||||
|
$subproject_workflow = !$can_subproject;
|
||||||
|
}
|
||||||
|
|
||||||
|
$view->addAction(
|
||||||
|
id(new PhabricatorActionView())
|
||||||
|
->setName(pht('Create Subproject'))
|
||||||
|
->setIcon('fa-plus')
|
||||||
|
->setHref($subproject_href)
|
||||||
|
->setDisabled($subproject_disabled)
|
||||||
|
->setWorkflow($subproject_workflow));
|
||||||
|
|
||||||
|
return $view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function renderStatus($icon, $target, $note) {
|
||||||
|
$item = id(new PHUIStatusItemView())
|
||||||
|
->setIcon($icon)
|
||||||
|
->setTarget(phutil_tag('strong', array(), $target))
|
||||||
|
->setNote($note);
|
||||||
|
|
||||||
|
return id(new PHUIStatusListView())
|
||||||
|
->addItem($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,7 +219,12 @@ final class PhabricatorProjectTransactionEditor
|
||||||
|
|
||||||
foreach ($xactions as $xaction) {
|
foreach ($xactions as $xaction) {
|
||||||
switch ($xaction->getTransactionType()) {
|
switch ($xaction->getTransactionType()) {
|
||||||
case PhabricatorProjectTransaction::TYPE_MEMBERS:
|
case PhabricatorTransactions::TYPE_EDGE:
|
||||||
|
$type = $xaction->getMetadataValue('edge:type');
|
||||||
|
if ($type != PhabricatorProjectProjectHasMemberEdgeType::EDGECONST) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if ($is_parent) {
|
if ($is_parent) {
|
||||||
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
||||||
$xaction->getTransactionType(),
|
$xaction->getTransactionType(),
|
||||||
|
@ -792,27 +797,6 @@ final class PhabricatorProjectTransactionEditor
|
||||||
|
|
||||||
$results = parent::expandTransactions($object, $xactions);
|
$results = parent::expandTransactions($object, $xactions);
|
||||||
|
|
||||||
// Automatically add the author as a member when they create a project
|
|
||||||
// if they're using the web interface.
|
|
||||||
|
|
||||||
$content_source = $this->getContentSource();
|
|
||||||
$source_web = PhabricatorContentSource::SOURCE_WEB;
|
|
||||||
$is_web = ($content_source->getSource() === $source_web);
|
|
||||||
|
|
||||||
if ($this->getIsNewObject() && $is_web) {
|
|
||||||
if ($actor_phid) {
|
|
||||||
$type_member = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST;
|
|
||||||
|
|
||||||
$results[] = id(new PhabricatorProjectTransaction())
|
|
||||||
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
|
|
||||||
->setMetadataValue('edge:type', $type_member)
|
|
||||||
->setNewValue(
|
|
||||||
array(
|
|
||||||
'+' => array($actor_phid => $actor_phid),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$is_milestone = $object->isMilestone();
|
$is_milestone = $object->isMilestone();
|
||||||
foreach ($xactions as $xaction) {
|
foreach ($xactions as $xaction) {
|
||||||
switch ($xaction->getTransactionType()) {
|
switch ($xaction->getTransactionType()) {
|
||||||
|
|
582
src/applications/project/engine/PhabricatorBoardLayoutEngine.php
Normal file
582
src/applications/project/engine/PhabricatorBoardLayoutEngine.php
Normal file
|
@ -0,0 +1,582 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorBoardLayoutEngine extends Phobject {
|
||||||
|
|
||||||
|
private $viewer;
|
||||||
|
private $boardPHIDs;
|
||||||
|
private $objectPHIDs;
|
||||||
|
private $boards;
|
||||||
|
private $columnMap = array();
|
||||||
|
private $objectColumnMap = array();
|
||||||
|
private $boardLayout = array();
|
||||||
|
|
||||||
|
private $remQueue = array();
|
||||||
|
private $addQueue = array();
|
||||||
|
|
||||||
|
public function setViewer(PhabricatorUser $viewer) {
|
||||||
|
$this->viewer = $viewer;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getViewer() {
|
||||||
|
return $this->viewer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBoardPHIDs(array $board_phids) {
|
||||||
|
$this->boardPHIDs = array_fuse($board_phids);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBoardPHIDs() {
|
||||||
|
return $this->boardPHIDs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setObjectPHIDs(array $object_phids) {
|
||||||
|
$this->objectPHIDs = array_fuse($object_phids);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getObjectPHIDs() {
|
||||||
|
return $this->objectPHIDs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function executeLayout() {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$boards = $this->loadBoards();
|
||||||
|
if (!$boards) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
$columns = $this->loadColumns($boards);
|
||||||
|
$positions = $this->loadPositions($boards);
|
||||||
|
|
||||||
|
foreach ($boards as $board_phid => $board) {
|
||||||
|
$board_columns = idx($columns, $board_phid);
|
||||||
|
|
||||||
|
// Don't layout boards with no columns. These boards need to be formally
|
||||||
|
// created first.
|
||||||
|
if (!$columns) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$board_positions = idx($positions, $board_phid, array());
|
||||||
|
|
||||||
|
$this->layoutBoard($board, $board_columns, $board_positions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getColumns($board_phid) {
|
||||||
|
$columns = idx($this->boardLayout, $board_phid, array());
|
||||||
|
return array_select_keys($this->columnMap, array_keys($columns));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getColumnObjectPHIDs($board_phid, $column_phid) {
|
||||||
|
$columns = idx($this->boardLayout, $board_phid, array());
|
||||||
|
$positions = idx($columns, $column_phid, array());
|
||||||
|
return mpull($positions, 'getObjectPHID');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getObjectColumns($board_phid, $object_phid) {
|
||||||
|
$board_map = idx($this->objectColumnMap, $board_phid, array());
|
||||||
|
|
||||||
|
$column_phids = idx($board_map, $object_phid);
|
||||||
|
if (!$column_phids) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_select_keys($this->columnMap, $column_phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function queueRemovePosition(
|
||||||
|
$board_phid,
|
||||||
|
$column_phid,
|
||||||
|
$object_phid) {
|
||||||
|
|
||||||
|
$board_layout = idx($this->boardLayout, $board_phid, array());
|
||||||
|
$positions = idx($board_layout, $column_phid, array());
|
||||||
|
$position = idx($positions, $object_phid);
|
||||||
|
|
||||||
|
if ($position) {
|
||||||
|
$this->remQueue[] = $position;
|
||||||
|
|
||||||
|
// If this position hasn't been saved yet, get it out of the add queue.
|
||||||
|
if (!$position->getID()) {
|
||||||
|
foreach ($this->addQueue as $key => $add_position) {
|
||||||
|
if ($add_position === $position) {
|
||||||
|
unset($this->addQueue[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($this->boardLayout[$board_phid][$column_phid][$object_phid]);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function queueAddPositionBefore(
|
||||||
|
$board_phid,
|
||||||
|
$column_phid,
|
||||||
|
$object_phid,
|
||||||
|
$before_phid) {
|
||||||
|
|
||||||
|
return $this->queueAddPositionRelative(
|
||||||
|
$board_phid,
|
||||||
|
$column_phid,
|
||||||
|
$object_phid,
|
||||||
|
$before_phid,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function queueAddPositionAfter(
|
||||||
|
$board_phid,
|
||||||
|
$column_phid,
|
||||||
|
$object_phid,
|
||||||
|
$after_phid) {
|
||||||
|
|
||||||
|
return $this->queueAddPositionRelative(
|
||||||
|
$board_phid,
|
||||||
|
$column_phid,
|
||||||
|
$object_phid,
|
||||||
|
$after_phid,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function queueAddPosition(
|
||||||
|
$board_phid,
|
||||||
|
$column_phid,
|
||||||
|
$object_phid) {
|
||||||
|
return $this->queueAddPositionRelative(
|
||||||
|
$board_phid,
|
||||||
|
$column_phid,
|
||||||
|
$object_phid,
|
||||||
|
null,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function queueAddPositionRelative(
|
||||||
|
$board_phid,
|
||||||
|
$column_phid,
|
||||||
|
$object_phid,
|
||||||
|
$relative_phid,
|
||||||
|
$is_before) {
|
||||||
|
|
||||||
|
$board_layout = idx($this->boardLayout, $board_phid, array());
|
||||||
|
$positions = idx($board_layout, $column_phid, array());
|
||||||
|
|
||||||
|
// Check if the object is already in the column, and remove it if it is.
|
||||||
|
$object_position = idx($positions, $object_phid);
|
||||||
|
unset($positions[$object_phid]);
|
||||||
|
|
||||||
|
if (!$object_position) {
|
||||||
|
$object_position = id(new PhabricatorProjectColumnPosition())
|
||||||
|
->setBoardPHID($board_phid)
|
||||||
|
->setColumnPHID($column_phid)
|
||||||
|
->setObjectPHID($object_phid);
|
||||||
|
}
|
||||||
|
|
||||||
|
$found = false;
|
||||||
|
if (!$positions) {
|
||||||
|
$object_position->setSequence(0);
|
||||||
|
} else {
|
||||||
|
foreach ($positions as $position) {
|
||||||
|
if (!$found) {
|
||||||
|
if ($relative_phid === null) {
|
||||||
|
$is_match = true;
|
||||||
|
} else {
|
||||||
|
$position_phid = $position->getObjectPHID();
|
||||||
|
$is_match = ($relative_phid == $position_phid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($is_match) {
|
||||||
|
$found = true;
|
||||||
|
|
||||||
|
$sequence = $position->getSequence();
|
||||||
|
|
||||||
|
if (!$is_before) {
|
||||||
|
$sequence++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$object_position->setSequence($sequence++);
|
||||||
|
|
||||||
|
if (!$is_before) {
|
||||||
|
// If we're inserting after this position, continue the loop so
|
||||||
|
// we don't update it.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($found) {
|
||||||
|
$position->setSequence($sequence++);
|
||||||
|
$this->addQueue[] = $position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($relative_phid && !$found) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Unable to find object "%s" in column "%s" on board "%s".',
|
||||||
|
$relative_phid,
|
||||||
|
$column_phid,
|
||||||
|
$board_phid));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->addQueue[] = $object_position;
|
||||||
|
|
||||||
|
$positions[$object_phid] = $object_position;
|
||||||
|
$positions = msort($positions, 'getOrderingKey');
|
||||||
|
|
||||||
|
$this->boardLayout[$board_phid][$column_phid] = $positions;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyPositionUpdates() {
|
||||||
|
foreach ($this->remQueue as $position) {
|
||||||
|
if ($position->getID()) {
|
||||||
|
$position->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->remQueue = array();
|
||||||
|
|
||||||
|
$adds = array();
|
||||||
|
$updates = array();
|
||||||
|
|
||||||
|
foreach ($this->addQueue as $position) {
|
||||||
|
$id = $position->getID();
|
||||||
|
if ($id) {
|
||||||
|
$updates[$id] = $position;
|
||||||
|
} else {
|
||||||
|
$adds[] = $position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->addQueue = array();
|
||||||
|
|
||||||
|
$table = new PhabricatorProjectColumnPosition();
|
||||||
|
$conn_w = $table->establishConnection('w');
|
||||||
|
|
||||||
|
$pairs = array();
|
||||||
|
foreach ($updates as $id => $position) {
|
||||||
|
// This is ugly because MySQL gets upset with us if it is configured
|
||||||
|
// strictly and we attempt inserts which can't work. We'll never actually
|
||||||
|
// do these inserts since they'll always collide (triggering the ON
|
||||||
|
// DUPLICATE KEY logic), so we just provide dummy values in order to get
|
||||||
|
// there.
|
||||||
|
|
||||||
|
$pairs[] = qsprintf(
|
||||||
|
$conn_w,
|
||||||
|
'(%d, %d, "", "", "")',
|
||||||
|
$id,
|
||||||
|
$position->getSequence());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pairs) {
|
||||||
|
queryfx(
|
||||||
|
$conn_w,
|
||||||
|
'INSERT INTO %T (id, sequence, boardPHID, columnPHID, objectPHID)
|
||||||
|
VALUES %Q ON DUPLICATE KEY UPDATE sequence = VALUES(sequence)',
|
||||||
|
$table->getTableName(),
|
||||||
|
implode(', ', $pairs));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($adds as $position) {
|
||||||
|
$position->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadBoards() {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$board_phids = $this->getBoardPHIDs();
|
||||||
|
|
||||||
|
$boards = id(new PhabricatorObjectQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs($board_phids)
|
||||||
|
->execute();
|
||||||
|
$boards = mpull($boards, null, 'getPHID');
|
||||||
|
|
||||||
|
foreach ($boards as $key => $board) {
|
||||||
|
if (!$board->getHasWorkboard()) {
|
||||||
|
unset($boards[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $boards;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadColumns(array $boards) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$columns = id(new PhabricatorProjectColumnQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withProjectPHIDs(array_keys($boards))
|
||||||
|
->execute();
|
||||||
|
$columns = msort($columns, 'getOrderingKey');
|
||||||
|
$columns = mpull($columns, null, 'getPHID');
|
||||||
|
|
||||||
|
$need_children = array();
|
||||||
|
foreach ($boards as $phid => $board) {
|
||||||
|
if ($board->getHasMilestones() || $board->getHasSubprojects()) {
|
||||||
|
$need_children[] = $phid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($need_children) {
|
||||||
|
$children = id(new PhabricatorProjectQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withParentProjectPHIDs($need_children)
|
||||||
|
->execute();
|
||||||
|
$children = mpull($children, null, 'getPHID');
|
||||||
|
$children = mgroup($children, 'getParentProjectPHID');
|
||||||
|
} else {
|
||||||
|
$children = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$columns = mgroup($columns, 'getProjectPHID');
|
||||||
|
foreach ($boards as $board_phid => $board) {
|
||||||
|
$board_columns = idx($columns, $board_phid, array());
|
||||||
|
|
||||||
|
// If the project has milestones, create any missing columns.
|
||||||
|
if ($board->getHasMilestones() || $board->getHasSubprojects()) {
|
||||||
|
$child_projects = idx($children, $board_phid, array());
|
||||||
|
|
||||||
|
$next_sequence = last($board_columns)->getSequence() + 1;
|
||||||
|
$proxy_columns = mpull($board_columns, null, 'getProxyPHID');
|
||||||
|
foreach ($child_projects as $child_phid => $child) {
|
||||||
|
if (isset($proxy_columns[$child_phid])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$new_column = PhabricatorProjectColumn::initializeNewColumn($viewer)
|
||||||
|
->attachProject($board)
|
||||||
|
->attachProxy($child)
|
||||||
|
->setSequence($next_sequence++)
|
||||||
|
->setProjectPHID($board_phid)
|
||||||
|
->setProxyPHID($child_phid);
|
||||||
|
|
||||||
|
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||||
|
$new_column->save();
|
||||||
|
unset($unguarded);
|
||||||
|
|
||||||
|
$board_columns[$new_column->getPHID()] = $new_column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$board_columns = msort($board_columns, 'getOrderingKey');
|
||||||
|
|
||||||
|
$columns[$board_phid] = $board_columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($columns as $board_phid => $board_columns) {
|
||||||
|
foreach ($board_columns as $board_column) {
|
||||||
|
$column_phid = $board_column->getPHID();
|
||||||
|
$this->columnMap[$column_phid] = $board_column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadPositions(array $boards) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$object_phids = $this->getObjectPHIDs();
|
||||||
|
if (!$object_phids) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$positions = id(new PhabricatorProjectColumnPositionQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withBoardPHIDs(array_keys($boards))
|
||||||
|
->withObjectPHIDs($object_phids)
|
||||||
|
->execute();
|
||||||
|
$positions = msort($positions, 'getOrderingKey');
|
||||||
|
$positions = mgroup($positions, 'getBoardPHID');
|
||||||
|
|
||||||
|
return $positions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function layoutBoard(
|
||||||
|
$board,
|
||||||
|
array $columns,
|
||||||
|
array $positions) {
|
||||||
|
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$board_phid = $board->getPHID();
|
||||||
|
$position_groups = mgroup($positions, 'getObjectPHID');
|
||||||
|
|
||||||
|
$layout = array();
|
||||||
|
foreach ($columns as $column) {
|
||||||
|
$column_phid = $column->getPHID();
|
||||||
|
$layout[$column_phid] = array();
|
||||||
|
|
||||||
|
if ($column->isDefaultColumn()) {
|
||||||
|
$default_phid = $column_phid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all the columns which are proxies for other objects.
|
||||||
|
$proxy_map = array();
|
||||||
|
foreach ($columns as $column) {
|
||||||
|
$proxy_phid = $column->getProxyPHID();
|
||||||
|
if ($proxy_phid) {
|
||||||
|
$proxy_map[$proxy_phid] = $column->getPHID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$object_phids = $this->getObjectPHIDs();
|
||||||
|
|
||||||
|
// If we have proxies, we need to force cards into the correct proxy
|
||||||
|
// columns.
|
||||||
|
if ($proxy_map) {
|
||||||
|
$edge_query = id(new PhabricatorEdgeQuery())
|
||||||
|
->withSourcePHIDs($object_phids)
|
||||||
|
->withEdgeTypes(
|
||||||
|
array(
|
||||||
|
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
|
||||||
|
));
|
||||||
|
$edge_query->execute();
|
||||||
|
|
||||||
|
$project_phids = $edge_query->getDestinationPHIDs();
|
||||||
|
$project_phids = array_fuse($project_phids);
|
||||||
|
} else {
|
||||||
|
$project_phids = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($project_phids) {
|
||||||
|
$projects = id(new PhabricatorProjectQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs($project_phids)
|
||||||
|
->execute();
|
||||||
|
$projects = mpull($projects, null, 'getPHID');
|
||||||
|
} else {
|
||||||
|
$projects = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a map from every project that any task is tagged with to the
|
||||||
|
// ancestor project which has a column on this board, if one exists.
|
||||||
|
$ancestor_map = array();
|
||||||
|
foreach ($projects as $phid => $project) {
|
||||||
|
if (isset($proxy_map[$phid])) {
|
||||||
|
$ancestor_map[$phid] = $proxy_map[$phid];
|
||||||
|
} else {
|
||||||
|
$seen = array($phid);
|
||||||
|
foreach ($project->getAncestorProjects() as $ancestor) {
|
||||||
|
$ancestor_phid = $ancestor->getPHID();
|
||||||
|
$seen[] = $ancestor_phid;
|
||||||
|
if (isset($proxy_map[$ancestor_phid])) {
|
||||||
|
foreach ($seen as $project_phid) {
|
||||||
|
$ancestor_map[$project_phid] = $proxy_map[$ancestor_phid];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($object_phids as $object_phid) {
|
||||||
|
$positions = idx($position_groups, $object_phid, array());
|
||||||
|
|
||||||
|
// First, check for objects that have corresponding proxy columns. We're
|
||||||
|
// going to overwrite normal column positions if a tag belongs to a proxy
|
||||||
|
// column, since you can't be in normal columns if you're in proxy
|
||||||
|
// columns.
|
||||||
|
$proxy_hits = array();
|
||||||
|
if ($proxy_map) {
|
||||||
|
$object_project_phids = $edge_query->getDestinationPHIDs(
|
||||||
|
array(
|
||||||
|
$object_phid,
|
||||||
|
));
|
||||||
|
|
||||||
|
foreach ($object_project_phids as $project_phid) {
|
||||||
|
if (isset($ancestor_map[$project_phid])) {
|
||||||
|
$proxy_hits[] = $ancestor_map[$project_phid];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($proxy_hits) {
|
||||||
|
// TODO: For now, only one column hit is permissible.
|
||||||
|
$proxy_hits = array_slice($proxy_hits, 0, 1);
|
||||||
|
|
||||||
|
$proxy_hits = array_fuse($proxy_hits);
|
||||||
|
|
||||||
|
// Check the object positions: we hope to find a position in each
|
||||||
|
// column the object should be part of. We're going to drop any
|
||||||
|
// invalid positions and create new positions where positions are
|
||||||
|
// missing.
|
||||||
|
foreach ($positions as $key => $position) {
|
||||||
|
$column_phid = $position->getColumnPHID();
|
||||||
|
if (isset($proxy_hits[$column_phid])) {
|
||||||
|
// Valid column, mark the position as found.
|
||||||
|
unset($proxy_hits[$column_phid]);
|
||||||
|
} else {
|
||||||
|
// Invalid column, ignore the position.
|
||||||
|
unset($positions[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new positions for anything we haven't found.
|
||||||
|
foreach ($proxy_hits as $proxy_hit) {
|
||||||
|
$new_position = id(new PhabricatorProjectColumnPosition())
|
||||||
|
->setBoardPHID($board_phid)
|
||||||
|
->setColumnPHID($proxy_hit)
|
||||||
|
->setObjectPHID($object_phid)
|
||||||
|
->setSequence(0);
|
||||||
|
|
||||||
|
$this->addQueue[] = $new_position;
|
||||||
|
|
||||||
|
$positions[] = $new_position;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Ignore any positions in columns which no longer exist. We don't
|
||||||
|
// actively destory them because the rest of the code ignores them and
|
||||||
|
// there's no real need to destroy the data.
|
||||||
|
foreach ($positions as $key => $position) {
|
||||||
|
$column_phid = $position->getColumnPHID();
|
||||||
|
if (empty($columns[$column_phid])) {
|
||||||
|
unset($positions[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the object has no position, put it on the default column.
|
||||||
|
if (!$positions) {
|
||||||
|
$new_position = id(new PhabricatorProjectColumnPosition())
|
||||||
|
->setBoardPHID($board_phid)
|
||||||
|
->setColumnPHID($default_phid)
|
||||||
|
->setObjectPHID($object_phid)
|
||||||
|
->setSequence(0);
|
||||||
|
|
||||||
|
$this->addQueue[] = $new_position;
|
||||||
|
|
||||||
|
$positions = array(
|
||||||
|
$new_position,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($positions as $position) {
|
||||||
|
$column_phid = $position->getColumnPHID();
|
||||||
|
$layout[$column_phid][$object_phid] = $position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($layout as $column_phid => $map) {
|
||||||
|
$map = msort($map, 'getOrderingKey');
|
||||||
|
$layout[$column_phid] = $map;
|
||||||
|
|
||||||
|
foreach ($map as $object_phid => $position) {
|
||||||
|
$this->objectColumnMap[$board_phid][$object_phid][] = $column_phid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->boardLayout[$board_phid] = $layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -176,7 +176,7 @@ final class PhabricatorProjectEditEngine
|
||||||
$milestone_phid = null;
|
$milestone_phid = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return array(
|
$fields = array(
|
||||||
id(new PhabricatorHandlesEditField())
|
id(new PhabricatorHandlesEditField())
|
||||||
->setKey('parent')
|
->setKey('parent')
|
||||||
->setLabel(pht('Parent'))
|
->setLabel(pht('Parent'))
|
||||||
|
@ -243,6 +243,49 @@ final class PhabricatorProjectEditEngine
|
||||||
->setConduitTypeDescription(pht('New list of slugs.'))
|
->setConduitTypeDescription(pht('New list of slugs.'))
|
||||||
->setValue($slugs),
|
->setValue($slugs),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$can_edit_members = (!$milestone) &&
|
||||||
|
(!$object->isMilestone()) &&
|
||||||
|
(!$object->getHasSubprojects());
|
||||||
|
|
||||||
|
if ($can_edit_members) {
|
||||||
|
|
||||||
|
// Show this on the web UI when creating a project, but not when editing
|
||||||
|
// one. It is always available via Conduit.
|
||||||
|
$conduit_only = !$this->getIsCreate();
|
||||||
|
|
||||||
|
$members_field = id(new PhabricatorUsersEditField())
|
||||||
|
->setKey('members')
|
||||||
|
->setAliases(array('memberPHIDs'))
|
||||||
|
->setLabel(pht('Initial Members'))
|
||||||
|
->setIsConduitOnly($conduit_only)
|
||||||
|
->setUseEdgeTransactions(true)
|
||||||
|
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
|
||||||
|
->setMetadataValue(
|
||||||
|
'edge:type',
|
||||||
|
PhabricatorProjectProjectHasMemberEdgeType::EDGECONST)
|
||||||
|
->setDescription(pht('Initial project members.'))
|
||||||
|
->setConduitDescription(pht('Set project members.'))
|
||||||
|
->setConduitTypeDescription(pht('New list of members.'))
|
||||||
|
->setValue(array());
|
||||||
|
|
||||||
|
$members_field->setViewer($this->getViewer());
|
||||||
|
|
||||||
|
$edit_add = $members_field->getConduitEditType('members.add')
|
||||||
|
->setConduitDescription(pht('Add members.'));
|
||||||
|
|
||||||
|
$edit_set = $members_field->getConduitEditType('members.set')
|
||||||
|
->setConduitDescription(
|
||||||
|
pht('Set members, overwriting the current value.'));
|
||||||
|
|
||||||
|
$edit_rem = $members_field->getConduitEditType('members.remove')
|
||||||
|
->setConduitDescription(pht('Remove members.'));
|
||||||
|
|
||||||
|
$fields[] = $members_field;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fields;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,10 @@ final class PhabricatorProjectProfilePanelEngine
|
||||||
->setBuiltinKey(PhabricatorProject::PANEL_MEMBERS)
|
->setBuiltinKey(PhabricatorProject::PANEL_MEMBERS)
|
||||||
->setPanelKey(PhabricatorProjectMembersProfilePanel::PANELKEY);
|
->setPanelKey(PhabricatorProjectMembersProfilePanel::PANELKEY);
|
||||||
|
|
||||||
|
$panels[] = $this->newPanel()
|
||||||
|
->setBuiltinKey(PhabricatorProject::PANEL_SUBPROJECTS)
|
||||||
|
->setPanelKey(PhabricatorProjectSubprojectsProfilePanel::PANELKEY);
|
||||||
|
|
||||||
$panels[] = $this->newPanel()
|
$panels[] = $this->newPanel()
|
||||||
->setBuiltinKey(PhabricatorProject::PANEL_MANAGE)
|
->setBuiltinKey(PhabricatorProject::PANEL_MANAGE)
|
||||||
->setPanelKey(PhabricatorProjectManageProfilePanel::PANELKEY);
|
->setPanelKey(PhabricatorProjectManageProfilePanel::PANELKEY);
|
||||||
|
|
|
@ -61,6 +61,15 @@ final class PhabricatorProjectsMembershipIndexEngineExtension
|
||||||
|
|
||||||
$conn_w = $project->establishConnection('w');
|
$conn_w = $project->establishConnection('w');
|
||||||
|
|
||||||
|
$any_milestone = queryfx_one(
|
||||||
|
$conn_w,
|
||||||
|
'SELECT id FROM %T
|
||||||
|
WHERE parentProjectPHID = %s AND milestoneNumber IS NOT NULL
|
||||||
|
LIMIT 1',
|
||||||
|
$project->getTableName(),
|
||||||
|
$project_phid);
|
||||||
|
$has_milestones = (bool)$any_milestone;
|
||||||
|
|
||||||
$project->openTransaction();
|
$project->openTransaction();
|
||||||
|
|
||||||
// Delete any existing materialized member edges.
|
// Delete any existing materialized member edges.
|
||||||
|
@ -92,6 +101,14 @@ final class PhabricatorProjectsMembershipIndexEngineExtension
|
||||||
(int)$has_subprojects,
|
(int)$has_subprojects,
|
||||||
$project->getID());
|
$project->getID());
|
||||||
|
|
||||||
|
// Update the hasMilestones flag.
|
||||||
|
queryfx(
|
||||||
|
$conn_w,
|
||||||
|
'UPDATE %T SET hasMilestones = %d WHERE id = %d',
|
||||||
|
$project->getTableName(),
|
||||||
|
(int)$has_milestones,
|
||||||
|
$project->getID());
|
||||||
|
|
||||||
$project->saveTransaction();
|
$project->saveTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,49 +49,42 @@ final class PhabricatorProjectUIEventListener
|
||||||
|
|
||||||
$annotations = array();
|
$annotations = array();
|
||||||
if ($handles && $can_appear_on_boards) {
|
if ($handles && $can_appear_on_boards) {
|
||||||
|
$engine = id(new PhabricatorBoardLayoutEngine())
|
||||||
|
->setViewer($user)
|
||||||
|
->setBoardPHIDs($project_phids)
|
||||||
|
->setObjectPHIDs(array($object->getPHID()))
|
||||||
|
->executeLayout();
|
||||||
|
|
||||||
// TDOO: Generalize this UI and move it out of Maniphest.
|
// TDOO: Generalize this UI and move it out of Maniphest.
|
||||||
|
|
||||||
require_celerity_resource('maniphest-task-summary-css');
|
require_celerity_resource('maniphest-task-summary-css');
|
||||||
|
|
||||||
$positions_query = id(new PhabricatorProjectColumnPositionQuery())
|
|
||||||
->setViewer($user)
|
|
||||||
->withBoardPHIDs($project_phids)
|
|
||||||
->withObjectPHIDs(array($object->getPHID()))
|
|
||||||
->needColumns(true);
|
|
||||||
|
|
||||||
// This is important because positions will be created "on demand"
|
|
||||||
// based on the set of columns. If we don't specify it, positions
|
|
||||||
// won't be created.
|
|
||||||
$columns = id(new PhabricatorProjectColumnQuery())
|
|
||||||
->setViewer($user)
|
|
||||||
->withProjectPHIDs($project_phids)
|
|
||||||
->execute();
|
|
||||||
if ($columns) {
|
|
||||||
$positions_query->withColumns($columns);
|
|
||||||
}
|
|
||||||
$positions = $positions_query->execute();
|
|
||||||
$positions = mpull($positions, null, 'getBoardPHID');
|
|
||||||
|
|
||||||
foreach ($project_phids as $project_phid) {
|
foreach ($project_phids as $project_phid) {
|
||||||
$handle = $handles[$project_phid];
|
$handle = $handles[$project_phid];
|
||||||
|
|
||||||
$position = idx($positions, $project_phid);
|
$columns = $engine->getObjectColumns(
|
||||||
if ($position) {
|
$project_phid,
|
||||||
$column = $position->getColumn();
|
$object->getPHID());
|
||||||
|
|
||||||
|
$annotation = array();
|
||||||
|
foreach ($columns as $column) {
|
||||||
|
$project_id = $column->getProject()->getID();
|
||||||
|
|
||||||
$column_name = pht('(%s)', $column->getDisplayName());
|
$column_name = pht('(%s)', $column->getDisplayName());
|
||||||
$column_link = phutil_tag(
|
$column_link = phutil_tag(
|
||||||
'a',
|
'a',
|
||||||
array(
|
array(
|
||||||
'href' => $handle->getURI().'board/',
|
'href' => "/project/board/{$project_id}/",
|
||||||
'class' => 'maniphest-board-link',
|
'class' => 'maniphest-board-link',
|
||||||
),
|
),
|
||||||
$column_name);
|
$column_name);
|
||||||
|
|
||||||
|
$annotation[] = $column_link;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($annotation) {
|
||||||
$annotations[$project_phid] = array(
|
$annotations[$project_phid] = array(
|
||||||
' ',
|
' ',
|
||||||
$column_link,
|
phutil_implode_html(', ', $annotation),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ProjectHovercardEngineExtension
|
||||||
|
extends PhabricatorHovercardEngineExtension {
|
||||||
|
|
||||||
|
const EXTENSIONKEY = 'project.card';
|
||||||
|
|
||||||
|
public function isExtensionEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExtensionName() {
|
||||||
|
return pht('Project Card');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canRenderObjectHovercard($object) {
|
||||||
|
return ($object instanceof PhabricatorProject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function willRenderHovercards(array $objects) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$phids = mpull($objects, 'getPHID');
|
||||||
|
|
||||||
|
$projects = id(new PhabricatorProjectQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs($phids)
|
||||||
|
->needImages(true)
|
||||||
|
->execute();
|
||||||
|
$projects = mpull($projects, null, 'getPHID');
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'projects' => $projects,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderHovercard(
|
||||||
|
PHUIHovercardView $hovercard,
|
||||||
|
PhabricatorObjectHandle $handle,
|
||||||
|
$object,
|
||||||
|
$data) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$project = idx($data['projects'], $object->getPHID());
|
||||||
|
if (!$project) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$project_card = id(new PhabricatorProjectCardView())
|
||||||
|
->setProject($project)
|
||||||
|
->setViewer($viewer);
|
||||||
|
|
||||||
|
$hovercard->appendChild($project_card);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
interface PhabricatorColumnProxyInterface {
|
||||||
|
|
||||||
|
public function getProxyColumnName();
|
||||||
|
|
||||||
|
}
|
|
@ -37,7 +37,7 @@ final class PhabricatorProjectProjectPHIDType extends PhabricatorPHIDType {
|
||||||
foreach ($handles as $phid => $handle) {
|
foreach ($handles as $phid => $handle) {
|
||||||
$project = $objects[$phid];
|
$project = $objects[$phid];
|
||||||
|
|
||||||
$name = $project->getName();
|
$name = $project->getDisplayName();
|
||||||
$id = $project->getID();
|
$id = $project->getID();
|
||||||
$slug = $project->getPrimarySlug();
|
$slug = $project->getPrimarySlug();
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,13 @@ final class PhabricatorProjectsPolicyRule
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getValueControlTemplate() {
|
public function getValueControlTemplate() {
|
||||||
return $this->getDatasourceTemplate(new PhabricatorProjectDatasource());
|
$datasource = id(new PhabricatorProjectDatasource())
|
||||||
|
->setParameters(
|
||||||
|
array(
|
||||||
|
'policy' => 1,
|
||||||
|
));
|
||||||
|
|
||||||
|
return $this->getDatasourceTemplate($datasource);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRuleOrder() {
|
public function getRuleOrder() {
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorProjectSubprojectsProfilePanel
|
||||||
|
extends PhabricatorProfilePanel {
|
||||||
|
|
||||||
|
const PANELKEY = 'project.subprojects';
|
||||||
|
|
||||||
|
public function getPanelTypeName() {
|
||||||
|
return pht('Project Subprojects');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDefaultName() {
|
||||||
|
return pht('Subprojects');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplayName(
|
||||||
|
PhabricatorProfilePanelConfiguration $config) {
|
||||||
|
$name = $config->getPanelProperty('name');
|
||||||
|
|
||||||
|
if (strlen($name)) {
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->getDefaultName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildEditEngineFields(
|
||||||
|
PhabricatorProfilePanelConfiguration $config) {
|
||||||
|
return array(
|
||||||
|
id(new PhabricatorTextEditField())
|
||||||
|
->setKey('name')
|
||||||
|
->setLabel(pht('Name'))
|
||||||
|
->setPlaceholder($this->getDefaultName())
|
||||||
|
->setValue($config->getPanelProperty('name')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newNavigationMenuItems(
|
||||||
|
PhabricatorProfilePanelConfiguration $config) {
|
||||||
|
|
||||||
|
$project = $config->getProfileObject();
|
||||||
|
|
||||||
|
$has_children = ($project->getHasSubprojects()) ||
|
||||||
|
($project->getHasMilestones());
|
||||||
|
|
||||||
|
$id = $project->getID();
|
||||||
|
|
||||||
|
$name = $this->getDisplayName($config);
|
||||||
|
$icon = 'fa-sitemap';
|
||||||
|
$href = "/project/subprojects/{$id}/";
|
||||||
|
|
||||||
|
$item = $this->newItem()
|
||||||
|
->setHref($href)
|
||||||
|
->setName($name)
|
||||||
|
->setDisabled(!$has_children)
|
||||||
|
->setIcon($icon);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
$item,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,10 +6,7 @@ final class PhabricatorProjectColumnPositionQuery
|
||||||
private $ids;
|
private $ids;
|
||||||
private $boardPHIDs;
|
private $boardPHIDs;
|
||||||
private $objectPHIDs;
|
private $objectPHIDs;
|
||||||
private $columns;
|
private $columnPHIDs;
|
||||||
|
|
||||||
private $needColumns;
|
|
||||||
private $skipImplicitCreate;
|
|
||||||
|
|
||||||
public function withIDs(array $ids) {
|
public function withIDs(array $ids) {
|
||||||
$this->ids = $ids;
|
$this->ids = $ids;
|
||||||
|
@ -26,277 +23,51 @@ final class PhabricatorProjectColumnPositionQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function withColumnPHIDs(array $column_phids) {
|
||||||
* Find objects in specific columns.
|
$this->columnPHIDs = $column_phids;
|
||||||
*
|
|
||||||
* NOTE: Using this method activates logic which constructs virtual
|
|
||||||
* column positions for objects not in any column, if you pass a default
|
|
||||||
* column. Normally these results are not returned.
|
|
||||||
*
|
|
||||||
* @param list<PhabricatorProjectColumn> Columns to look for objects in.
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public function withColumns(array $columns) {
|
|
||||||
assert_instances_of($columns, 'PhabricatorProjectColumn');
|
|
||||||
$this->columns = $columns;
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function needColumns($need_columns) {
|
public function newResultObject() {
|
||||||
$this->needColumns = true;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Skip implicit creation of column positions which are implied but do not
|
|
||||||
* yet exist.
|
|
||||||
*
|
|
||||||
* This is primarily useful internally.
|
|
||||||
*
|
|
||||||
* @param bool True to skip implicit creation of column positions.
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public function setSkipImplicitCreate($skip) {
|
|
||||||
$this->skipImplicitCreate = $skip;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: For now, boards are always attached to projects. However, they might
|
|
||||||
// not be in the future. This generalization just anticipates a future where
|
|
||||||
// we let other types of objects (like users) have boards, or let boards
|
|
||||||
// contain other types of objects.
|
|
||||||
|
|
||||||
private function newPositionObject() {
|
|
||||||
return new PhabricatorProjectColumnPosition();
|
return new PhabricatorProjectColumnPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function newColumnQuery() {
|
|
||||||
return new PhabricatorProjectColumnQuery();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getBoardMembershipEdgeTypes() {
|
|
||||||
return array(
|
|
||||||
PhabricatorProjectProjectHasObjectEdgeType::EDGECONST,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getBoardMembershipPHIDTypes() {
|
|
||||||
return array(
|
|
||||||
ManiphestTaskPHIDType::TYPECONST,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function loadPage() {
|
protected function loadPage() {
|
||||||
$table = $this->newPositionObject();
|
return $this->loadStandardPage($this->newResultObject());
|
||||||
$conn_r = $table->establishConnection('r');
|
|
||||||
|
|
||||||
// We're going to find results by combining two queries: one query finds
|
|
||||||
// objects on a board column, while the other query finds objects not on
|
|
||||||
// any board column and virtually puts them on the default column.
|
|
||||||
|
|
||||||
$unions = array();
|
|
||||||
|
|
||||||
// First, find all the stuff that's actually on a column.
|
|
||||||
|
|
||||||
$unions[] = qsprintf(
|
|
||||||
$conn_r,
|
|
||||||
'SELECT * FROM %T %Q',
|
|
||||||
$table->getTableName(),
|
|
||||||
$this->buildWhereClause($conn_r));
|
|
||||||
|
|
||||||
// If we have a default column, find all the stuff that's not in any
|
|
||||||
// column and put it in the default column.
|
|
||||||
|
|
||||||
$must_type_filter = false;
|
|
||||||
if ($this->columns && !$this->skipImplicitCreate) {
|
|
||||||
$default_map = array();
|
|
||||||
foreach ($this->columns as $column) {
|
|
||||||
if ($column->isDefaultColumn()) {
|
|
||||||
$default_map[$column->getProjectPHID()] = $column->getPHID();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($default_map) {
|
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||||
$where = array();
|
|
||||||
|
|
||||||
// Find the edges attached to the boards we have default columns for.
|
|
||||||
|
|
||||||
$where[] = qsprintf(
|
|
||||||
$conn_r,
|
|
||||||
'e.src IN (%Ls)',
|
|
||||||
array_keys($default_map));
|
|
||||||
|
|
||||||
// Find only edges which describe a board relationship.
|
|
||||||
|
|
||||||
$where[] = qsprintf(
|
|
||||||
$conn_r,
|
|
||||||
'e.type IN (%Ld)',
|
|
||||||
$this->getBoardMembershipEdgeTypes());
|
|
||||||
|
|
||||||
if ($this->boardPHIDs !== null) {
|
|
||||||
// This should normally be redundant, but construct it anyway if
|
|
||||||
// the caller has told us to.
|
|
||||||
$where[] = qsprintf(
|
|
||||||
$conn_r,
|
|
||||||
'e.src IN (%Ls)',
|
|
||||||
$this->boardPHIDs);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->objectPHIDs !== null) {
|
|
||||||
$where[] = qsprintf(
|
|
||||||
$conn_r,
|
|
||||||
'e.dst IN (%Ls)',
|
|
||||||
$this->objectPHIDs);
|
|
||||||
}
|
|
||||||
|
|
||||||
$where[] = qsprintf(
|
|
||||||
$conn_r,
|
|
||||||
'p.id IS NULL');
|
|
||||||
|
|
||||||
$where = $this->formatWhereClause($where);
|
|
||||||
|
|
||||||
$unions[] = qsprintf(
|
|
||||||
$conn_r,
|
|
||||||
'SELECT NULL id, e.src boardPHID, NULL columnPHID, e.dst objectPHID,
|
|
||||||
0 sequence
|
|
||||||
FROM %T e LEFT JOIN %T p
|
|
||||||
ON e.src = p.boardPHID AND e.dst = p.objectPHID
|
|
||||||
%Q',
|
|
||||||
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
|
|
||||||
$table->getTableName(),
|
|
||||||
$where);
|
|
||||||
|
|
||||||
$must_type_filter = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = queryfx_all(
|
|
||||||
$conn_r,
|
|
||||||
'%Q %Q %Q',
|
|
||||||
implode(' UNION ALL ', $unions),
|
|
||||||
$this->buildOrderClause($conn_r),
|
|
||||||
$this->buildLimitClause($conn_r));
|
|
||||||
|
|
||||||
// If we've picked up objects not in any column, we need to filter out any
|
|
||||||
// matched objects which have the wrong edge type.
|
|
||||||
if ($must_type_filter) {
|
|
||||||
$allowed_types = array_fuse($this->getBoardMembershipPHIDTypes());
|
|
||||||
foreach ($data as $id => $row) {
|
|
||||||
if ($row['columnPHID'] === null) {
|
|
||||||
$object_phid = $row['objectPHID'];
|
|
||||||
if (empty($allowed_types[phid_get_type($object_phid)])) {
|
|
||||||
unset($data[$id]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$positions = $table->loadAllFromArray($data);
|
|
||||||
|
|
||||||
// Find the implied positions which don't exist yet. If there are any,
|
|
||||||
// we're going to create them.
|
|
||||||
$create = array();
|
|
||||||
foreach ($positions as $position) {
|
|
||||||
if ($position->getColumnPHID() === null) {
|
|
||||||
$column_phid = idx($default_map, $position->getBoardPHID());
|
|
||||||
$position->setColumnPHID($column_phid);
|
|
||||||
|
|
||||||
$create[] = $position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($create) {
|
|
||||||
// If we're adding several objects to a column, insert the column
|
|
||||||
// position objects in object ID order. This means that newly added
|
|
||||||
// objects float to the top, and when a group of newly added objects
|
|
||||||
// float up at the same time, the most recently created ones end up
|
|
||||||
// highest in the list.
|
|
||||||
|
|
||||||
$objects = id(new PhabricatorObjectQuery())
|
|
||||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
|
||||||
->withPHIDs(mpull($create, 'getObjectPHID'))
|
|
||||||
->execute();
|
|
||||||
$objects = mpull($objects, null, 'getPHID');
|
|
||||||
$objects = msort($objects, 'getID');
|
|
||||||
|
|
||||||
$create = mgroup($create, 'getObjectPHID');
|
|
||||||
$create = array_select_keys($create, array_keys($objects)) + $create;
|
|
||||||
|
|
||||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
|
||||||
|
|
||||||
foreach ($create as $object_phid => $create_positions) {
|
|
||||||
foreach ($create_positions as $create_position) {
|
|
||||||
$create_position->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unset($unguarded);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $positions;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function willFilterPage(array $page) {
|
|
||||||
|
|
||||||
if ($this->needColumns) {
|
|
||||||
$column_phids = mpull($page, 'getColumnPHID');
|
|
||||||
$columns = $this->newColumnQuery()
|
|
||||||
->setParentQuery($this)
|
|
||||||
->setViewer($this->getViewer())
|
|
||||||
->withPHIDs($column_phids)
|
|
||||||
->execute();
|
|
||||||
$columns = mpull($columns, null, 'getPHID');
|
|
||||||
|
|
||||||
foreach ($page as $key => $position) {
|
|
||||||
$column = idx($columns, $position->getColumnPHID());
|
|
||||||
if (!$column) {
|
|
||||||
unset($page[$key]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$position->attachColumn($column);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $page;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
|
||||||
$where = array();
|
$where = array();
|
||||||
|
|
||||||
if ($this->ids !== null) {
|
if ($this->ids !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'id IN (%Ld)',
|
'id IN (%Ld)',
|
||||||
$this->ids);
|
$this->ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->boardPHIDs !== null) {
|
if ($this->boardPHIDs !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'boardPHID IN (%Ls)',
|
'boardPHID IN (%Ls)',
|
||||||
$this->boardPHIDs);
|
$this->boardPHIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->objectPHIDs !== null) {
|
if ($this->objectPHIDs !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'objectPHID IN (%Ls)',
|
'objectPHID IN (%Ls)',
|
||||||
$this->objectPHIDs);
|
$this->objectPHIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->columns !== null) {
|
if ($this->columnPHIDs !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn,
|
||||||
'columnPHID IN (%Ls)',
|
'columnPHID IN (%Ls)',
|
||||||
mpull($this->columns, 'getPHID'));
|
$this->columnPHIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Explicitly not building the paging clause here, since it won't
|
return $where;
|
||||||
// work with the UNION.
|
|
||||||
|
|
||||||
return $this->formatWhereClause($where);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getQueryApplicationClass() {
|
public function getQueryApplicationClass() {
|
||||||
|
|
|
@ -6,6 +6,7 @@ final class PhabricatorProjectColumnQuery
|
||||||
private $ids;
|
private $ids;
|
||||||
private $phids;
|
private $phids;
|
||||||
private $projectPHIDs;
|
private $projectPHIDs;
|
||||||
|
private $proxyPHIDs;
|
||||||
private $statuses;
|
private $statuses;
|
||||||
|
|
||||||
public function withIDs(array $ids) {
|
public function withIDs(array $ids) {
|
||||||
|
@ -23,6 +24,11 @@ final class PhabricatorProjectColumnQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withProxyPHIDs(array $proxy_phids) {
|
||||||
|
$this->proxyPHIDs = $proxy_phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function withStatuses(array $status) {
|
public function withStatuses(array $status) {
|
||||||
$this->statuses = $status;
|
$this->statuses = $status;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -60,6 +66,55 @@ final class PhabricatorProjectColumnQuery
|
||||||
$column->attachProject($project);
|
$column->attachProject($project);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$proxy_phids = array_filter(mpull($page, 'getProjectPHID'));
|
||||||
|
|
||||||
|
return $page;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function didFilterPage(array $page) {
|
||||||
|
$proxy_phids = array();
|
||||||
|
foreach ($page as $column) {
|
||||||
|
$proxy_phid = $column->getProxyPHID();
|
||||||
|
if ($proxy_phid !== null) {
|
||||||
|
$proxy_phids[$proxy_phid] = $proxy_phid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($proxy_phids) {
|
||||||
|
$proxies = id(new PhabricatorObjectQuery())
|
||||||
|
->setParentQuery($this)
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->withPHIDs($proxy_phids)
|
||||||
|
->execute();
|
||||||
|
$proxies = mpull($proxies, null, 'getPHID');
|
||||||
|
} else {
|
||||||
|
$proxies = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($page as $key => $column) {
|
||||||
|
$proxy_phid = $column->getProxyPHID();
|
||||||
|
|
||||||
|
if ($proxy_phid !== null) {
|
||||||
|
$proxy = idx($proxies, $proxy_phid);
|
||||||
|
|
||||||
|
// Only attach valid proxies, so we don't end up getting surprsied if
|
||||||
|
// an install somehow gets junk into their database.
|
||||||
|
if (!($proxy instanceof PhabricatorColumnProxyInterface)) {
|
||||||
|
$proxy = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$proxy) {
|
||||||
|
$this->didRejectResult($column);
|
||||||
|
unset($page[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$proxy = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$column->attachProxy($proxy);
|
||||||
|
}
|
||||||
|
|
||||||
return $page;
|
return $page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +142,13 @@ final class PhabricatorProjectColumnQuery
|
||||||
$this->projectPHIDs);
|
$this->projectPHIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->proxyPHIDs !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'proxyPHID IN (%Ls)',
|
||||||
|
$this->proxyPHIDs);
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->statuses !== null) {
|
if ($this->statuses !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn,
|
$conn,
|
||||||
|
|
|
@ -16,7 +16,9 @@ final class ProjectRemarkupRule extends PhabricatorObjectRemarkupRule {
|
||||||
return '#'.$id;
|
return '#'.$id;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $handle->renderTag();
|
$tag = $handle->renderTag();
|
||||||
|
$tag->setPHID($handle->getPHID());
|
||||||
|
return $tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getObjectIDPattern() {
|
protected function getObjectIDPattern() {
|
||||||
|
|
|
@ -9,7 +9,8 @@ final class PhabricatorProject extends PhabricatorProjectDAO
|
||||||
PhabricatorCustomFieldInterface,
|
PhabricatorCustomFieldInterface,
|
||||||
PhabricatorDestructibleInterface,
|
PhabricatorDestructibleInterface,
|
||||||
PhabricatorFulltextInterface,
|
PhabricatorFulltextInterface,
|
||||||
PhabricatorConduitResultInterface {
|
PhabricatorConduitResultInterface,
|
||||||
|
PhabricatorColumnProxyInterface {
|
||||||
|
|
||||||
protected $name;
|
protected $name;
|
||||||
protected $status = PhabricatorProjectStatus::STATUS_ACTIVE;
|
protected $status = PhabricatorProjectStatus::STATUS_ACTIVE;
|
||||||
|
@ -378,7 +379,7 @@ final class PhabricatorProject extends PhabricatorProjectDAO
|
||||||
$this->getPHID());
|
$this->getPHID());
|
||||||
|
|
||||||
$all_strings = ipull($slugs, 'slug');
|
$all_strings = ipull($slugs, 'slug');
|
||||||
$all_strings[] = $this->getName();
|
$all_strings[] = $this->getDisplayName();
|
||||||
$all_strings = implode(' ', $all_strings);
|
$all_strings = implode(' ', $all_strings);
|
||||||
|
|
||||||
$tokens = PhabricatorTypeaheadDatasource::tokenizeString($all_strings);
|
$tokens = PhabricatorTypeaheadDatasource::tokenizeString($all_strings);
|
||||||
|
@ -489,6 +490,20 @@ final class PhabricatorProject extends PhabricatorProjectDAO
|
||||||
return $number;
|
return $number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDisplayName() {
|
||||||
|
$name = $this->getName();
|
||||||
|
|
||||||
|
// If this is a milestone, show it as "Parent > Sprint 99".
|
||||||
|
if ($this->isMilestone()) {
|
||||||
|
$name = pht(
|
||||||
|
'%s (%s)',
|
||||||
|
$this->getParentProject()->getName(),
|
||||||
|
$name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
public function getDisplayIconKey() {
|
public function getDisplayIconKey() {
|
||||||
if ($this->isMilestone()) {
|
if ($this->isMilestone()) {
|
||||||
$key = PhabricatorProjectIconSet::getMilestoneIconKey();
|
$key = PhabricatorProjectIconSet::getMilestoneIconKey();
|
||||||
|
@ -663,4 +678,25 @@ final class PhabricatorProject extends PhabricatorProjectDAO
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( PhabricatorColumnProxyInterface )------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
public function getProxyColumnName() {
|
||||||
|
return $this->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProxyColumnIcon() {
|
||||||
|
return $this->getDisplayIconIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProxyColumnClass() {
|
||||||
|
if ($this->isMilestone()) {
|
||||||
|
return 'phui-workboard-column-milestone';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,15 +17,18 @@ final class PhabricatorProjectColumn
|
||||||
protected $name;
|
protected $name;
|
||||||
protected $status;
|
protected $status;
|
||||||
protected $projectPHID;
|
protected $projectPHID;
|
||||||
|
protected $proxyPHID;
|
||||||
protected $sequence;
|
protected $sequence;
|
||||||
protected $properties = array();
|
protected $properties = array();
|
||||||
|
|
||||||
private $project = self::ATTACHABLE;
|
private $project = self::ATTACHABLE;
|
||||||
|
private $proxy = self::ATTACHABLE;
|
||||||
|
|
||||||
public static function initializeNewColumn(PhabricatorUser $user) {
|
public static function initializeNewColumn(PhabricatorUser $user) {
|
||||||
return id(new PhabricatorProjectColumn())
|
return id(new PhabricatorProjectColumn())
|
||||||
->setName('')
|
->setName('')
|
||||||
->setStatus(self::STATUS_ACTIVE);
|
->setStatus(self::STATUS_ACTIVE)
|
||||||
|
->attachProxy(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getConfiguration() {
|
protected function getConfiguration() {
|
||||||
|
@ -38,6 +41,7 @@ final class PhabricatorProjectColumn
|
||||||
'name' => 'text255',
|
'name' => 'text255',
|
||||||
'status' => 'uint32',
|
'status' => 'uint32',
|
||||||
'sequence' => 'uint32',
|
'sequence' => 'uint32',
|
||||||
|
'proxyPHID' => 'phid?',
|
||||||
),
|
),
|
||||||
self::CONFIG_KEY_SCHEMA => array(
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
'key_status' => array(
|
'key_status' => array(
|
||||||
|
@ -46,6 +50,10 @@ final class PhabricatorProjectColumn
|
||||||
'key_sequence' => array(
|
'key_sequence' => array(
|
||||||
'columns' => array('projectPHID', 'sequence'),
|
'columns' => array('projectPHID', 'sequence'),
|
||||||
),
|
),
|
||||||
|
'key_proxy' => array(
|
||||||
|
'columns' => array('projectPHID', 'proxyPHID'),
|
||||||
|
'unique' => true,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
) + parent::getConfiguration();
|
) + parent::getConfiguration();
|
||||||
}
|
}
|
||||||
|
@ -64,6 +72,15 @@ final class PhabricatorProjectColumn
|
||||||
return $this->assertAttached($this->project);
|
return $this->assertAttached($this->project);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function attachProxy($proxy) {
|
||||||
|
$this->proxy = $proxy;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProxy() {
|
||||||
|
return $this->assertAttached($this->proxy);
|
||||||
|
}
|
||||||
|
|
||||||
public function isDefaultColumn() {
|
public function isDefaultColumn() {
|
||||||
return (bool)$this->getProperty('isDefault');
|
return (bool)$this->getProperty('isDefault');
|
||||||
}
|
}
|
||||||
|
@ -73,6 +90,11 @@ final class PhabricatorProjectColumn
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDisplayName() {
|
public function getDisplayName() {
|
||||||
|
$proxy = $this->getProxy();
|
||||||
|
if ($proxy) {
|
||||||
|
return $proxy->getProxyColumnName();
|
||||||
|
}
|
||||||
|
|
||||||
$name = $this->getName();
|
$name = $this->getName();
|
||||||
if (strlen($name)) {
|
if (strlen($name)) {
|
||||||
return $name;
|
return $name;
|
||||||
|
@ -96,11 +118,23 @@ final class PhabricatorProjectColumn
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDisplayClass() {
|
||||||
|
$proxy = $this->getProxy();
|
||||||
|
if ($proxy) {
|
||||||
|
return $proxy->getProxyColumnClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public function getHeaderIcon() {
|
public function getHeaderIcon() {
|
||||||
$icon = null;
|
$proxy = $this->getProxy();
|
||||||
|
if ($proxy) {
|
||||||
|
return $proxy->getProxyColumnIcon();
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->isHidden()) {
|
if ($this->isHidden()) {
|
||||||
$icon = 'fa-eye-slash';
|
return 'fa-eye-slash';
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -124,6 +158,25 @@ final class PhabricatorProjectColumn
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getOrderingKey() {
|
||||||
|
$proxy = $this->getProxy();
|
||||||
|
|
||||||
|
// Normal columns and subproject columns go first, in a user-controlled
|
||||||
|
// order.
|
||||||
|
|
||||||
|
// All the milestone columns go last, in their sequential order.
|
||||||
|
|
||||||
|
if (!$proxy || !$proxy->isMilestone()) {
|
||||||
|
$group = 'A';
|
||||||
|
$sequence = $this->getSequence();
|
||||||
|
} else {
|
||||||
|
$group = 'B';
|
||||||
|
$sequence = $proxy->getMilestoneNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf('%s%012d', $group, $sequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,10 @@ final class PhabricatorProjectColumnPosition extends PhabricatorProjectDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getOrderingKey() {
|
public function getOrderingKey() {
|
||||||
|
if (!$this->getID() && !$this->getSequence()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Low sequence numbers go above high sequence numbers.
|
// Low sequence numbers go above high sequence numbers.
|
||||||
// High position IDs go above low position IDs.
|
// High position IDs go above low position IDs.
|
||||||
// Broadly, this makes newly added stuff float to the top.
|
// Broadly, this makes newly added stuff float to the top.
|
||||||
|
|
|
@ -32,6 +32,12 @@ final class PhabricatorProjectDatasource
|
||||||
$query->withNameTokens($tokens);
|
$query->withNameTokens($tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is for policy selection, prevent users from using milestones.
|
||||||
|
$for_policy = $this->getParameter('policy');
|
||||||
|
if ($for_policy) {
|
||||||
|
$query->withIsMilestone(false);
|
||||||
|
}
|
||||||
|
|
||||||
$projs = $this->executeQuery($query);
|
$projs = $this->executeQuery($query);
|
||||||
|
|
||||||
$projs = mpull($projs, null, 'getPHID');
|
$projs = mpull($projs, null, 'getPHID');
|
||||||
|
@ -58,13 +64,13 @@ final class PhabricatorProjectDatasource
|
||||||
}
|
}
|
||||||
|
|
||||||
$all_strings = mpull($proj->getSlugs(), 'getSlug');
|
$all_strings = mpull($proj->getSlugs(), 'getSlug');
|
||||||
$all_strings[] = $proj->getName();
|
$all_strings[] = $proj->getDisplayName();
|
||||||
$all_strings = implode(' ', $all_strings);
|
$all_strings = implode(' ', $all_strings);
|
||||||
|
|
||||||
$proj_result = id(new PhabricatorTypeaheadResult())
|
$proj_result = id(new PhabricatorTypeaheadResult())
|
||||||
->setName($all_strings)
|
->setName($all_strings)
|
||||||
->setDisplayName($proj->getName())
|
->setDisplayName($proj->getDisplayName())
|
||||||
->setDisplayType(pht('Project'))
|
->setDisplayType($proj->getDisplayIconName())
|
||||||
->setURI($proj->getURI())
|
->setURI($proj->getURI())
|
||||||
->setPHID($proj->getPHID())
|
->setPHID($proj->getPHID())
|
||||||
->setIcon($proj->getDisplayIconIcon())
|
->setIcon($proj->getDisplayIconIcon())
|
||||||
|
|
84
src/applications/project/view/PhabricatorProjectCardView.php
Normal file
84
src/applications/project/view/PhabricatorProjectCardView.php
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorProjectCardView extends AphrontTagView {
|
||||||
|
|
||||||
|
private $project;
|
||||||
|
private $viewer;
|
||||||
|
private $tag;
|
||||||
|
|
||||||
|
public function setProject(PhabricatorProject $project) {
|
||||||
|
$this->project = $project;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setViewer(PhabricatorUser $viewer) {
|
||||||
|
$this->viewer = $viewer;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTag($tag) {
|
||||||
|
$this->tag = $tag;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTagName() {
|
||||||
|
if ($this->tag) {
|
||||||
|
return $this->tag;
|
||||||
|
}
|
||||||
|
return 'div';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTagAttributes() {
|
||||||
|
$classes = array();
|
||||||
|
$classes[] = 'project-card-view';
|
||||||
|
|
||||||
|
$color = $this->project->getColor();
|
||||||
|
$classes[] = 'project-card-'.$color;
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'class' => implode($classes, ' '),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTagContent() {
|
||||||
|
|
||||||
|
$project = $this->project;
|
||||||
|
$viewer = $this->viewer;
|
||||||
|
require_celerity_resource('project-card-view-css');
|
||||||
|
|
||||||
|
$icon = $project->getDisplayIconIcon();
|
||||||
|
$icon_name = $project->getDisplayIconName();
|
||||||
|
$tag = id(new PHUITagView())
|
||||||
|
->setIcon($icon)
|
||||||
|
->setName($icon_name)
|
||||||
|
->addClass('project-view-header-tag')
|
||||||
|
->setType(PHUITagView::TYPE_SHADE);
|
||||||
|
|
||||||
|
$header = id(new PHUIHeaderView())
|
||||||
|
->setHeader(array($project->getDisplayName(), $tag))
|
||||||
|
->setUser($viewer)
|
||||||
|
->setPolicyObject($project)
|
||||||
|
->setImage($project->getProfileImageURI());
|
||||||
|
|
||||||
|
if ($project->getStatus() == PhabricatorProjectStatus::STATUS_ACTIVE) {
|
||||||
|
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
|
||||||
|
} else {
|
||||||
|
$header->setStatus('fa-ban', 'red', pht('Archived'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$description = null;
|
||||||
|
|
||||||
|
$card = phutil_tag(
|
||||||
|
'div',
|
||||||
|
array(
|
||||||
|
'class' => 'project-card-inner',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
$header,
|
||||||
|
$description,
|
||||||
|
));
|
||||||
|
|
||||||
|
return $card;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -86,7 +86,7 @@ abstract class PhabricatorProjectUserListView extends AphrontView {
|
||||||
->setImageURI($handle->getImageURI());
|
->setImageURI($handle->getImageURI());
|
||||||
|
|
||||||
$icon = id(new PHUIIconView())
|
$icon = id(new PHUIIconView())
|
||||||
->setIcon($handle->getIcon().' lightbluetext');
|
->setIcon($handle->getIcon());
|
||||||
|
|
||||||
$subtitle = $handle->getSubtitle();
|
$subtitle = $handle->getSubtitle();
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
final class ProjectBoardTaskCard extends Phobject {
|
final class ProjectBoardTaskCard extends Phobject {
|
||||||
|
|
||||||
private $viewer;
|
private $viewer;
|
||||||
|
private $projectHandles;
|
||||||
private $task;
|
private $task;
|
||||||
private $owner;
|
private $owner;
|
||||||
private $canEdit;
|
private $canEdit;
|
||||||
|
@ -15,6 +16,15 @@ final class ProjectBoardTaskCard extends Phobject {
|
||||||
return $this->viewer;
|
return $this->viewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setProjectHandles(array $handles) {
|
||||||
|
$this->projectHandles = $handles;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProjectHandles() {
|
||||||
|
return $this->projectHandles;
|
||||||
|
}
|
||||||
|
|
||||||
public function setTask(ManiphestTask $task) {
|
public function setTask(ManiphestTask $task) {
|
||||||
$this->task = $task;
|
$this->task = $task;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -44,13 +54,14 @@ final class ProjectBoardTaskCard extends Phobject {
|
||||||
$task = $this->getTask();
|
$task = $this->getTask();
|
||||||
$owner = $this->getOwner();
|
$owner = $this->getOwner();
|
||||||
$can_edit = $this->getCanEdit();
|
$can_edit = $this->getCanEdit();
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
$color_map = ManiphestTaskPriority::getColorMap();
|
$color_map = ManiphestTaskPriority::getColorMap();
|
||||||
$bar_color = idx($color_map, $task->getPriority(), 'grey');
|
$bar_color = idx($color_map, $task->getPriority(), 'grey');
|
||||||
|
|
||||||
$card = id(new PHUIObjectItemView())
|
$card = id(new PHUIObjectItemView())
|
||||||
->setObject($task)
|
->setObject($task)
|
||||||
->setUser($this->getViewer())
|
->setUser($viewer)
|
||||||
->setObjectName('T'.$task->getID())
|
->setObjectName('T'.$task->getID())
|
||||||
->setHeader($task->getTitle())
|
->setHeader($task->getTitle())
|
||||||
->setGrippable($can_edit)
|
->setGrippable($can_edit)
|
||||||
|
@ -70,7 +81,23 @@ final class ProjectBoardTaskCard extends Phobject {
|
||||||
->setBarColor($bar_color);
|
->setBarColor($bar_color);
|
||||||
|
|
||||||
if ($owner) {
|
if ($owner) {
|
||||||
$card->addAttribute($owner->renderLink());
|
$card->addHandleIcon($owner, $owner->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($task->isClosed()) {
|
||||||
|
$icon = ManiphestTaskStatus::getStatusIcon($task->getStatus());
|
||||||
|
$icon = id(new PHUIIconView())
|
||||||
|
->setIcon($icon.' grey');
|
||||||
|
$card->addAttribute($icon);
|
||||||
|
$card->setBarColor('grey');
|
||||||
|
}
|
||||||
|
|
||||||
|
$project_handles = $this->getProjectHandles();
|
||||||
|
if ($project_handles) {
|
||||||
|
$tag_list = id(new PHUIHandleTagListView())
|
||||||
|
->setSlim(true)
|
||||||
|
->setHandles($project_handles);
|
||||||
|
$card->addAttribute($tag_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $card;
|
return $card;
|
||||||
|
|
|
@ -30,7 +30,7 @@ final class PhabricatorRepositoryPullEvent
|
||||||
self::CONFIG_COLUMN_SCHEMA => array(
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
'repositoryPHID' => 'phid?',
|
'repositoryPHID' => 'phid?',
|
||||||
'pullerPHID' => 'phid?',
|
'pullerPHID' => 'phid?',
|
||||||
'remoteAddress' => 'uint32?',
|
'remoteAddress' => 'ipaddress?',
|
||||||
'remoteProtocol' => 'text32?',
|
'remoteProtocol' => 'text32?',
|
||||||
'resultType' => 'text32',
|
'resultType' => 'text32',
|
||||||
'resultCode' => 'uint32',
|
'resultCode' => 'uint32',
|
||||||
|
|
|
@ -29,7 +29,7 @@ final class PhabricatorRepositoryPushEvent
|
||||||
self::CONFIG_AUX_PHID => true,
|
self::CONFIG_AUX_PHID => true,
|
||||||
self::CONFIG_TIMESTAMPS => false,
|
self::CONFIG_TIMESTAMPS => false,
|
||||||
self::CONFIG_COLUMN_SCHEMA => array(
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
'remoteAddress' => 'uint32?',
|
'remoteAddress' => 'ipaddress?',
|
||||||
'remoteProtocol' => 'text32?',
|
'remoteProtocol' => 'text32?',
|
||||||
'rejectCode' => 'uint32',
|
'rejectCode' => 'uint32',
|
||||||
'rejectDetails' => 'text64?',
|
'rejectDetails' => 'text64?',
|
||||||
|
|
|
@ -56,7 +56,7 @@ final class PhabricatorSearchHovercardController
|
||||||
$handle = $handles[$phid];
|
$handle = $handles[$phid];
|
||||||
$object = idx($objects, $phid);
|
$object = idx($objects, $phid);
|
||||||
|
|
||||||
$hovercard = id(new PhabricatorHovercardView())
|
$hovercard = id(new PHUIHovercardView())
|
||||||
->setUser($viewer)
|
->setUser($viewer)
|
||||||
->setObjectHandle($handle);
|
->setObjectHandle($handle);
|
||||||
|
|
||||||
|
|
|
@ -663,6 +663,7 @@ abstract class PhabricatorProfilePanelEngine extends Phobject {
|
||||||
|
|
||||||
$header = id(new PHUIHeaderView())
|
$header = id(new PHUIHeaderView())
|
||||||
->setHeader(pht('Profile Menu Items'))
|
->setHeader(pht('Profile Menu Items'))
|
||||||
|
->setSubHeader(pht('Drag tabs to reorder menu'))
|
||||||
->addActionLink($action_button);
|
->addActionLink($action_button);
|
||||||
|
|
||||||
$box = id(new PHUIObjectBoxView())
|
$box = id(new PHUIObjectBoxView())
|
||||||
|
@ -907,7 +908,7 @@ abstract class PhabricatorProfilePanelEngine extends Phobject {
|
||||||
|
|
||||||
$is_target =
|
$is_target =
|
||||||
(($builtin_key !== null) && ($builtin_key === $key)) ||
|
(($builtin_key !== null) && ($builtin_key === $key)) ||
|
||||||
(($id !== null) && ($id === (int)$key));
|
(($id !== null) && ((int)$id === (int)$key));
|
||||||
|
|
||||||
if ($is_target) {
|
if ($is_target) {
|
||||||
if (!$panel->isDefault()) {
|
if (!$panel->isDefault()) {
|
||||||
|
|
|
@ -32,7 +32,7 @@ abstract class PhabricatorHovercardEngineExtension extends Phobject {
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract public function renderHovercard(
|
abstract public function renderHovercard(
|
||||||
PhabricatorHovercardView $hovercard,
|
PHUIHovercardView $hovercard,
|
||||||
PhabricatorObjectHandle $handle,
|
PhabricatorObjectHandle $handle,
|
||||||
$object,
|
$object,
|
||||||
$data);
|
$data);
|
||||||
|
|
|
@ -131,6 +131,15 @@ final class PhabricatorMotivatorProfilePanel
|
||||||
pht(
|
pht(
|
||||||
'The Japanese word for cat is "kome", which is also the word for '.
|
'The Japanese word for cat is "kome", which is also the word for '.
|
||||||
'rice. Japanese cats love to eat rice, so the two are synonymous.'),
|
'rice. Japanese cats love to eat rice, so the two are synonymous.'),
|
||||||
|
pht('Cats have five pointy ends.'),
|
||||||
|
pht('cat -A can find mice hiding in files.'),
|
||||||
|
pht('A cat\'s visual, olfactory, and auditory senses, '.
|
||||||
|
'Contribute to their hunting skills and natural defenses.'),
|
||||||
|
pht(
|
||||||
|
'Cats with high self-esteem seek out high perches '.
|
||||||
|
'to launch their attacks. Watch out!'),
|
||||||
|
pht('Cats prefer vanilla ice cream.'),
|
||||||
|
pht('Taco cat spelled backwards is taco cat.'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ final class PhabricatorUserPreferences extends PhabricatorUserDAO {
|
||||||
const PREFERENCE_DESKTOP_NOTIFICATIONS = 'desktop-notifications';
|
const PREFERENCE_DESKTOP_NOTIFICATIONS = 'desktop-notifications';
|
||||||
|
|
||||||
const PREFERENCE_PROFILE_MENU_COLLAPSED = 'profile-menu.collapsed';
|
const PREFERENCE_PROFILE_MENU_COLLAPSED = 'profile-menu.collapsed';
|
||||||
|
const PREFERENCE_FAVORITE_POLICIES = 'policy.favorites';
|
||||||
|
|
||||||
// These are in an unusual order for historic reasons.
|
// These are in an unusual order for historic reasons.
|
||||||
const MAILTAG_PREFERENCE_NOTIFY = 0;
|
const MAILTAG_PREFERENCE_NOTIFY = 0;
|
||||||
|
|
|
@ -400,7 +400,15 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
return $space_phid;
|
return $space_phid;
|
||||||
}
|
}
|
||||||
case PhabricatorTransactions::TYPE_EDGE:
|
case PhabricatorTransactions::TYPE_EDGE:
|
||||||
return $this->getEdgeTransactionNewValue($xaction);
|
$new_value = $this->getEdgeTransactionNewValue($xaction);
|
||||||
|
|
||||||
|
$edge_type = $xaction->getMetadataValue('edge:type');
|
||||||
|
$type_project = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
|
||||||
|
if ($edge_type == $type_project) {
|
||||||
|
$new_value = $this->applyProjectConflictRules($new_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $new_value;
|
||||||
case PhabricatorTransactions::TYPE_CUSTOMFIELD:
|
case PhabricatorTransactions::TYPE_CUSTOMFIELD:
|
||||||
$field = $this->getCustomFieldForTransaction($object, $xaction);
|
$field = $this->getCustomFieldForTransaction($object, $xaction);
|
||||||
return $field->getNewValueFromApplicationTransactions($xaction);
|
return $field->getNewValueFromApplicationTransactions($xaction);
|
||||||
|
@ -3346,4 +3354,127 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
return $state;
|
return $state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove conflicts from a list of projects.
|
||||||
|
*
|
||||||
|
* Objects aren't allowed to be tagged with multiple milestones in the same
|
||||||
|
* group, nor projects such that one tag is the ancestor of any other tag.
|
||||||
|
* If the list of PHIDs include mutually exclusive projects, remove the
|
||||||
|
* conflicting projects.
|
||||||
|
*
|
||||||
|
* @param list<phid> List of project PHIDs.
|
||||||
|
* @return list<phid> List with conflicts removed.
|
||||||
|
*/
|
||||||
|
private function applyProjectConflictRules(array $phids) {
|
||||||
|
if (!$phids) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overall, the last project in the list wins in cases of conflict (so when
|
||||||
|
// you add something, the thing you just added sticks and removes older
|
||||||
|
// values).
|
||||||
|
|
||||||
|
// Beyond that, there are two basic cases:
|
||||||
|
|
||||||
|
// Milestones: An object can't be in "A > Sprint 3" and "A > Sprint 4".
|
||||||
|
// If multiple projects are milestones of the same parent, we only keep the
|
||||||
|
// last one.
|
||||||
|
|
||||||
|
// Ancestor: You can't be in "A" and "A > B". If "A > B" comes later
|
||||||
|
// in the list, we remove "A" and keep "A > B". If "A" comes later, we
|
||||||
|
// remove "A > B" and keep "A".
|
||||||
|
|
||||||
|
// Note that it's OK to be in "A > B" and "A > C". There's only a conflict
|
||||||
|
// if one project is an ancestor of another. It's OK to have something
|
||||||
|
// tagged with multiple projects which share a common ancestor, so long as
|
||||||
|
// they are not mutual ancestors.
|
||||||
|
|
||||||
|
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||||
|
|
||||||
|
$projects = id(new PhabricatorProjectQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs(array_keys($phids))
|
||||||
|
->execute();
|
||||||
|
$projects = mpull($projects, null, 'getPHID');
|
||||||
|
|
||||||
|
// We're going to build a map from each project with milestones to the last
|
||||||
|
// milestone in the list. This last milestone is the milestone we'll keep.
|
||||||
|
$milestone_map = array();
|
||||||
|
|
||||||
|
// We're going to build a set of the projects which have no descendants
|
||||||
|
// later in the list. This allows us to apply both ancestor rules.
|
||||||
|
$ancestor_map = array();
|
||||||
|
|
||||||
|
foreach ($phids as $phid => $ignored) {
|
||||||
|
$project = idx($projects, $phid);
|
||||||
|
if (!$project) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the last milestone we've seen, so set it as the selection for
|
||||||
|
// the project's parent. This might be setting a new value or overwriting
|
||||||
|
// an earlier value.
|
||||||
|
if ($project->isMilestone()) {
|
||||||
|
$parent_phid = $project->getParentProjectPHID();
|
||||||
|
$milestone_map[$parent_phid] = $phid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since this is the last item in the list we've examined so far, add it
|
||||||
|
// to the set of projects with no later descendants.
|
||||||
|
$ancestor_map[$phid] = $phid;
|
||||||
|
|
||||||
|
// Remove any ancestors from the set, since this is a later descendant.
|
||||||
|
foreach ($project->getAncestorProjects() as $ancestor) {
|
||||||
|
$ancestor_phid = $ancestor->getPHID();
|
||||||
|
unset($ancestor_map[$ancestor_phid]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we've built the maps, we can throw away all the projects which
|
||||||
|
// have conflicts.
|
||||||
|
foreach ($phids as $phid => $ignored) {
|
||||||
|
$project = idx($projects, $phid);
|
||||||
|
|
||||||
|
if (!$project) {
|
||||||
|
// If a PHID is invalid, we just leave it as-is. We could clean it up,
|
||||||
|
// but leaving it untouched is less likely to cause collateral damage.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this was a milestone, check if it was the last milestone from its
|
||||||
|
// group in the list. If not, remove it from the list.
|
||||||
|
if ($project->isMilestone()) {
|
||||||
|
$parent_phid = $project->getParentProjectPHID();
|
||||||
|
if ($milestone_map[$parent_phid] !== $phid) {
|
||||||
|
unset($phids[$phid]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a later project in the list is a subproject of this one, it will
|
||||||
|
// have removed ancestors from the map. If this project does not point
|
||||||
|
// at itself in the ancestor map, it should be discarded in favor of a
|
||||||
|
// subproject that comes later.
|
||||||
|
if (idx($ancestor_map, $phid) !== $phid) {
|
||||||
|
unset($phids[$phid]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a later project in the list is an ancestor of this one, it will
|
||||||
|
// have added itself to the map. If any ancestor of this project points
|
||||||
|
// at itself in the map, this project should be dicarded in favor of
|
||||||
|
// that later ancestor.
|
||||||
|
foreach ($project->getAncestorProjects() as $ancestor) {
|
||||||
|
$ancestor_phid = $ancestor->getPHID();
|
||||||
|
if (isset($ancestor_map[$ancestor_phid])) {
|
||||||
|
unset($phids[$phid]);
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $phids;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ final class PhabricatorTypeaheadTokenView
|
||||||
$token = id(new PhabricatorTypeaheadTokenView())
|
$token = id(new PhabricatorTypeaheadTokenView())
|
||||||
->setKey($handle->getPHID())
|
->setKey($handle->getPHID())
|
||||||
->setValue($handle->getFullName())
|
->setValue($handle->getFullName())
|
||||||
->setIcon($handle->getIcon());
|
->setIcon($handle->getTokenIcon());
|
||||||
|
|
||||||
if ($handle->isDisabled() ||
|
if ($handle->isDisabled() ||
|
||||||
$handle->getStatus() == PhabricatorObjectHandle::STATUS_CLOSED) {
|
$handle->getStatus() == PhabricatorObjectHandle::STATUS_CLOSED) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class PhabricatorHovercardUIExample extends PhabricatorUIExample {
|
final class PHUIHovercardUIExample extends PhabricatorUIExample {
|
||||||
|
|
||||||
public function getName() {
|
public function getName() {
|
||||||
return pht('Hovercard');
|
return pht('Hovercard');
|
||||||
|
@ -8,8 +8,8 @@ final class PhabricatorHovercardUIExample extends PhabricatorUIExample {
|
||||||
|
|
||||||
public function getDescription() {
|
public function getDescription() {
|
||||||
return pht(
|
return pht(
|
||||||
"Use %s to render hovercards. Aren't I genius?",
|
'Use %s to render hovercards.',
|
||||||
phutil_tag('tt', array(), 'PhabricatorHovercardView'));
|
phutil_tag('tt', array(), 'PHUIHovercardView'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderExample() {
|
public function renderExample() {
|
||||||
|
@ -24,7 +24,7 @@ final class PhabricatorHovercardUIExample extends PhabricatorUIExample {
|
||||||
pht('Introduce cooler Differential Revisions'));
|
pht('Introduce cooler Differential Revisions'));
|
||||||
|
|
||||||
$panel = $this->createPanel(pht('Differential Hovercard'));
|
$panel = $this->createPanel(pht('Differential Hovercard'));
|
||||||
$panel->appendChild(id(new PhabricatorHovercardView())
|
$panel->appendChild(id(new PHUIHovercardView())
|
||||||
->setObjectHandle($diff_handle)
|
->setObjectHandle($diff_handle)
|
||||||
->addField(pht('Author'), $user->getUsername())
|
->addField(pht('Author'), $user->getUsername())
|
||||||
->addField(pht('Updated'), phabricator_datetime(time(), $user))
|
->addField(pht('Updated'), phabricator_datetime(time(), $user))
|
||||||
|
@ -41,7 +41,7 @@ final class PhabricatorHovercardUIExample extends PhabricatorUIExample {
|
||||||
->setType(PHUITagView::TYPE_STATE)
|
->setType(PHUITagView::TYPE_STATE)
|
||||||
->setName(pht('Closed, Resolved'));
|
->setName(pht('Closed, Resolved'));
|
||||||
$panel = $this->createPanel(pht('Maniphest Hovercard'));
|
$panel = $this->createPanel(pht('Maniphest Hovercard'));
|
||||||
$panel->appendChild(id(new PhabricatorHovercardView())
|
$panel->appendChild(id(new PHUIHovercardView())
|
||||||
->setObjectHandle($task_handle)
|
->setObjectHandle($task_handle)
|
||||||
->setUser($user)
|
->setUser($user)
|
||||||
->addField(pht('Assigned to'), $user->getUsername())
|
->addField(pht('Assigned to'), $user->getUsername())
|
||||||
|
@ -66,7 +66,7 @@ final class PhabricatorHovercardUIExample extends PhabricatorUIExample {
|
||||||
$user_handle->setImageURI(
|
$user_handle->setImageURI(
|
||||||
celerity_get_resource_uri('/rsrc/image/people/washington.png'));
|
celerity_get_resource_uri('/rsrc/image/people/washington.png'));
|
||||||
$panel = $this->createPanel(pht('Whatevery Hovercard'));
|
$panel = $this->createPanel(pht('Whatevery Hovercard'));
|
||||||
$panel->appendChild(id(new PhabricatorHovercardView())
|
$panel->appendChild(id(new PHUIHovercardView())
|
||||||
->setObjectHandle($user_handle)
|
->setObjectHandle($user_handle)
|
||||||
->addField(pht('Status'), pht('Available'))
|
->addField(pht('Status'), pht('Available'))
|
||||||
->addField(pht('Member since'), '30. February 1750')
|
->addField(pht('Member since'), '30. February 1750')
|
|
@ -127,8 +127,13 @@ If you plan to use authenticated HTTP, you need to set
|
||||||
use only anonymous HTTP, you can leave this setting disabled.
|
use only anonymous HTTP, you can leave this setting disabled.
|
||||||
|
|
||||||
If you plan to use authenticated HTTP, you'll also need to configure a VCS
|
If you plan to use authenticated HTTP, you'll also need to configure a VCS
|
||||||
password in {nav Settings > VCS Password}. This is a different password than
|
password in {nav Settings > VCS Password}.
|
||||||
your main Phabricator password primarily for security reasons.
|
|
||||||
|
Your VCS password must be a different password than your main Phabricator
|
||||||
|
password because VCS passwords are very easy to accidentally disclose. They are
|
||||||
|
often stored in plaintext in world-readable files, observable in `ps` output,
|
||||||
|
and present in command output and logs. We strongly encourage you to use SSH
|
||||||
|
instead of HTTP to authenticate access to repositories.
|
||||||
|
|
||||||
Otherwise, if you've configured system accounts above, you're all set. No
|
Otherwise, if you've configured system accounts above, you're all set. No
|
||||||
additional server configuration is required to make HTTP work.
|
additional server configuration is required to make HTTP work.
|
||||||
|
|
|
@ -135,6 +135,142 @@ members or won't have a workboard, you can hide these items to streamline the
|
||||||
menu.
|
menu.
|
||||||
|
|
||||||
|
|
||||||
|
Subprojects and Milestones
|
||||||
|
==========================
|
||||||
|
|
||||||
|
IMPORTANT: This feature is only partially implemented.
|
||||||
|
|
||||||
|
After creating a project, you can use the
|
||||||
|
{nav icon="sitemap", name="Subprojects"} menu item to add subprojects or
|
||||||
|
milestones.
|
||||||
|
|
||||||
|
**Subprojects** are projects that are contained inside the main project. You
|
||||||
|
can use them to break large or complex groups, tags, lists, or undertakings
|
||||||
|
apart into smaller pieces.
|
||||||
|
|
||||||
|
**Milestones** are a special kind of subproject for organizing tasks into
|
||||||
|
blocks of work. You can use them to implement sprints, iterations, milestones,
|
||||||
|
versions, etc.
|
||||||
|
|
||||||
|
Subprojects and milestones have some additional special behaviors and rules,
|
||||||
|
particularly around policies and membership. See below for details.
|
||||||
|
|
||||||
|
This is a brief summary of the major differences between normal projects,
|
||||||
|
subprojects, parent projects, and milestones.
|
||||||
|
|
||||||
|
| | Normal | Parent | Subproject | Milestone |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| //Members// | Yes | Union of Subprojects | Yes | Same as Parent |
|
||||||
|
| //Policies// | Yes | Yes | Affected by Parent | Same as Parent |
|
||||||
|
| //Hashtags// | Yes | Yes | Yes | Special |
|
||||||
|
|
||||||
|
|
||||||
|
Subprojects
|
||||||
|
===========
|
||||||
|
|
||||||
|
Subprojects are full-power projects that are contained inside some parent
|
||||||
|
project. You can use them to divide a large or complex project into smaller
|
||||||
|
parts.
|
||||||
|
|
||||||
|
Subprojects have normal members and normal policies, but note that the policies
|
||||||
|
of the parent project affect the policies of the subproject (see "Parent
|
||||||
|
Projects", below).
|
||||||
|
|
||||||
|
Subprojects can have their own subprojects, milestones, or both. If a
|
||||||
|
subproject has its own subprojects, it is both a subproject and a parent
|
||||||
|
project. Thus, the parent project rules apply to it, and are stronger than the
|
||||||
|
subproject rules.
|
||||||
|
|
||||||
|
Subprojects can have normal workboards.
|
||||||
|
|
||||||
|
The maximum subproject depth is 16. This limit is intended to grossly exceed
|
||||||
|
the depth necessary in normal usage.
|
||||||
|
|
||||||
|
Objects may not be tagged with multiple projects that are ancestors or
|
||||||
|
descendants of one another. For example, a task may not be tagged with both
|
||||||
|
{nav Stonework} and {nav Stonework > Masonry}.
|
||||||
|
|
||||||
|
When a project tag is added that is the ancestor or descendant of one or more
|
||||||
|
existing tags, the old tags are replaced. For example, adding
|
||||||
|
{nav Stonework > Masonry} to a task tagged with {nav Stonework} will replace
|
||||||
|
{nav Stonework} with the newer, more specific tag.
|
||||||
|
|
||||||
|
This restriction does not apply to projects which share some common ancestor
|
||||||
|
but are not themselves mutual ancestors. For example, a task may be tagged
|
||||||
|
with both {nav Stonework > Masonry} and {nav Stonework > Sculpting}.
|
||||||
|
|
||||||
|
This restriction //does// apply when the descendant is a milestone. For
|
||||||
|
example, a task may not be tagged with both {nav Stonework} and
|
||||||
|
{nav Stonework > Iteration II}.
|
||||||
|
|
||||||
|
|
||||||
|
Milestones
|
||||||
|
==========
|
||||||
|
|
||||||
|
Milestones are simple subprojects for tracking sprints, iterations, versions,
|
||||||
|
or other similar blocks of work. Milestones make it easier to create and manage
|
||||||
|
a large number of similar subprojects (for example: {nav Sprint 1},
|
||||||
|
{nav Sprint 2}, {nav Sprint 3}, etc).
|
||||||
|
|
||||||
|
Milestones can not have direct members or policies. Instead, the membership
|
||||||
|
and policies of a milestones are always the same as the milestone's parent
|
||||||
|
project. This makes large numbers of milestones more manageable when changes
|
||||||
|
occur.
|
||||||
|
|
||||||
|
Milestones can not have subprojects, and can not have their own milestones.
|
||||||
|
|
||||||
|
By default, Milestones do not have their own hashtags.
|
||||||
|
|
||||||
|
Milestones can have normal workboards.
|
||||||
|
|
||||||
|
Objects may not be tagged with two different milestones of the same parent
|
||||||
|
project. For example, a task may not be tagged with both {nav Stonework >
|
||||||
|
Iteration III} and {nav Stonework > Iteration V}.
|
||||||
|
|
||||||
|
When a milestone tag is added to an object which already has a tag from the
|
||||||
|
same series of milestones, the old tag is removed. For example, adding the
|
||||||
|
{nav Stonework > Iteration V} tag to a task which already has the
|
||||||
|
{nav Stonework > Iteration III} tag will remove the {nav Iteration III} tag.
|
||||||
|
|
||||||
|
This restriction does not apply to milestones which are not part of the same
|
||||||
|
series. For example, a task may be tagged with both
|
||||||
|
{nav Stonework > Iteration V} and {nav Heraldry > Iteration IX}.
|
||||||
|
|
||||||
|
|
||||||
|
Parent Projects
|
||||||
|
===============
|
||||||
|
|
||||||
|
When you add the first subproject to an existing project, it is converted into
|
||||||
|
a **parent project**. Parent projects have some special rules.
|
||||||
|
|
||||||
|
**No Direct Members**: Parent projects can not have members of their own.
|
||||||
|
Instead, all of the users who are members of any subproject count as members
|
||||||
|
of the parent project. By joining (or leaving) a subproject, a user is
|
||||||
|
implicitly added to (or removed from) all ancestors of that project.
|
||||||
|
|
||||||
|
Consequently, when you add the first subproject to an existing project, all of
|
||||||
|
the project's current members are moved to become members of the subproject
|
||||||
|
instead. Implicitly, they will remain members of the parent project because the
|
||||||
|
parent project is an ancestor of the new subproject.
|
||||||
|
|
||||||
|
You can edit the project afterward to change or remove members if you want to
|
||||||
|
split membership apart in a more granular way across multiple new subprojects.
|
||||||
|
|
||||||
|
**Searching**: When you search for a parent project, results for any subproject
|
||||||
|
are returned. For example, if you search for {nav Engineering}, your query will
|
||||||
|
match results in {nav Engineering} itself, but also subprojects like
|
||||||
|
{nav Engineering > Warp Drive} and {nav Engineering > Shield Batteries}.
|
||||||
|
|
||||||
|
**Policy Effects**: To view a subproject or milestone, you must be able to
|
||||||
|
view the parent project. As a result, the parent project's view policy now
|
||||||
|
affects child projects. If you restrict the visibility of the parent, you also
|
||||||
|
restrict the visibility of the children.
|
||||||
|
|
||||||
|
In contrast, permission to edit a parent project grants permission to edit
|
||||||
|
any subproject. If a user can {nav Root Project}, they can also edit
|
||||||
|
{nav Root Project > Child} and {nav Root Project > Child > Sprint 3}.
|
||||||
|
|
||||||
|
|
||||||
Policies In Depth
|
Policies In Depth
|
||||||
=================
|
=================
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,24 @@ final class AphrontFormPolicyControl extends AphrontFormControl {
|
||||||
protected function getOptions() {
|
protected function getOptions() {
|
||||||
$capability = $this->capability;
|
$capability = $this->capability;
|
||||||
$policies = $this->policies;
|
$policies = $this->policies;
|
||||||
|
$viewer = $this->getUser();
|
||||||
|
|
||||||
|
// Check if we're missing the policy for the current control value. This
|
||||||
|
// is unusual, but can occur if the user is submitting a form and selected
|
||||||
|
// an unusual project as a policy but the change has not been saved yet.
|
||||||
|
$policy_map = mpull($policies, null, 'getPHID');
|
||||||
|
$value = $this->getValue();
|
||||||
|
if ($value && empty($policy_map[$value])) {
|
||||||
|
$handle = id(new PhabricatorHandleQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs(array($value))
|
||||||
|
->executeOne();
|
||||||
|
if ($handle->isComplete()) {
|
||||||
|
$policies[] = PhabricatorPolicy::newFromPolicyAndHandle(
|
||||||
|
$value,
|
||||||
|
$handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Exclude object policies which don't make sense here. This primarily
|
// Exclude object policies which don't make sense here. This primarily
|
||||||
// filters object policies associated from template capabilities (like
|
// filters object policies associated from template capabilities (like
|
||||||
|
@ -143,12 +161,32 @@ final class AphrontFormPolicyControl extends AphrontFormControl {
|
||||||
'name' => $policy_short_name,
|
'name' => $policy_short_name,
|
||||||
'full' => $policy->getName(),
|
'full' => $policy->getName(),
|
||||||
'icon' => $policy->getIcon(),
|
'icon' => $policy->getIcon(),
|
||||||
|
'sort' => phutil_utf8_strtolower($policy->getName()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$type_project = PhabricatorPolicyType::TYPE_PROJECT;
|
||||||
|
|
||||||
|
// Make sure we have a "Projects" group before we adjust it.
|
||||||
|
if (empty($options[$type_project])) {
|
||||||
|
$options[$type_project] = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$options[$type_project] = isort($options[$type_project], 'sort');
|
||||||
|
|
||||||
|
$placeholder = id(new PhabricatorPolicy())
|
||||||
|
->setName(pht('Other Project...'))
|
||||||
|
->setIcon('fa-search');
|
||||||
|
|
||||||
|
$options[$type_project][$this->getSelectProjectKey()] = array(
|
||||||
|
'name' => $placeholder->getName(),
|
||||||
|
'full' => $placeholder->getName(),
|
||||||
|
'icon' => $placeholder->getIcon(),
|
||||||
|
);
|
||||||
|
|
||||||
// If we were passed several custom policy options, throw away the ones
|
// If we were passed several custom policy options, throw away the ones
|
||||||
// which aren't the value for this capability. For example, an object might
|
// which aren't the value for this capability. For example, an object might
|
||||||
// have a custom view pollicy and a custom edit policy. When we render
|
// have a custom view policy and a custom edit policy. When we render
|
||||||
// the selector for "Can View", we don't want to show the "Can Edit"
|
// the selector for "Can View", we don't want to show the "Can Edit"
|
||||||
// custom policy -- if we did, the menu would look like this:
|
// custom policy -- if we did, the menu would look like this:
|
||||||
//
|
//
|
||||||
|
@ -172,7 +210,7 @@ final class AphrontFormPolicyControl extends AphrontFormControl {
|
||||||
if (empty($options[$type_custom])) {
|
if (empty($options[$type_custom])) {
|
||||||
$placeholder = new PhabricatorPolicy();
|
$placeholder = new PhabricatorPolicy();
|
||||||
$placeholder->setName(pht('Custom Policy...'));
|
$placeholder->setName(pht('Custom Policy...'));
|
||||||
$options[$type_custom][$this->getCustomPolicyPlaceholder()] = array(
|
$options[$type_custom][$this->getSelectCustomKey()] = array(
|
||||||
'name' => $placeholder->getName(),
|
'name' => $placeholder->getName(),
|
||||||
'full' => $placeholder->getName(),
|
'full' => $placeholder->getName(),
|
||||||
'icon' => $placeholder->getIcon(),
|
'icon' => $placeholder->getIcon(),
|
||||||
|
@ -266,12 +304,12 @@ final class AphrontFormPolicyControl extends AphrontFormControl {
|
||||||
'options' => $flat_options,
|
'options' => $flat_options,
|
||||||
'groups' => array_keys($options),
|
'groups' => array_keys($options),
|
||||||
'order' => $order,
|
'order' => $order,
|
||||||
'icons' => $icons,
|
|
||||||
'labels' => $labels,
|
'labels' => $labels,
|
||||||
'value' => $this->getValue(),
|
'value' => $this->getValue(),
|
||||||
'capability' => $this->capability,
|
'capability' => $this->capability,
|
||||||
'editURI' => '/policy/edit/'.$context_path,
|
'editURI' => '/policy/edit/'.$context_path,
|
||||||
'customPlaceholder' => $this->getCustomPolicyPlaceholder(),
|
'customKey' => $this->getSelectCustomKey(),
|
||||||
|
'projectKey' => $this->getSelectProjectKey(),
|
||||||
'disabled' => $this->getDisabled(),
|
'disabled' => $this->getDisabled(),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -322,8 +360,12 @@ final class AphrontFormPolicyControl extends AphrontFormControl {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getCustomPolicyPlaceholder() {
|
public static function getSelectCustomKey() {
|
||||||
return 'custom:placeholder';
|
return 'select:custom';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getSelectProjectKey() {
|
||||||
|
return 'select:project';
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildSpacesControl() {
|
private function buildSpacesControl() {
|
||||||
|
|
|
@ -157,7 +157,7 @@ final class PHUIFeedStoryView extends AphrontView {
|
||||||
public function render() {
|
public function render() {
|
||||||
|
|
||||||
require_celerity_resource('phui-feed-story-css');
|
require_celerity_resource('phui-feed-story-css');
|
||||||
Javelin::initBehavior('phabricator-hovercards');
|
Javelin::initBehavior('phui-hovercards');
|
||||||
|
|
||||||
$body = null;
|
$body = null;
|
||||||
$foot = null;
|
$foot = null;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* The default one-for-all hovercard. We may derive from this one to create
|
* The default one-for-all hovercard. We may derive from this one to create
|
||||||
* more specialized ones.
|
* more specialized ones.
|
||||||
*/
|
*/
|
||||||
final class PhabricatorHovercardView extends AphrontView {
|
final class PHUIHovercardView extends AphrontTagView {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var PhabricatorObjectHandle
|
* @var PhabricatorObjectHandle
|
||||||
|
@ -70,7 +70,16 @@ final class PhabricatorHovercardView extends AphrontView {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render() {
|
protected function getTagAttributes() {
|
||||||
|
$classes = array();
|
||||||
|
$classes[] = 'phui-hovercard-wrapper';
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'class' => implode(' ', $classes),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTagContent() {
|
||||||
if (!$this->handle) {
|
if (!$this->handle) {
|
||||||
throw new PhutilInvalidStateException('setObjectHandle');
|
throw new PhutilInvalidStateException('setObjectHandle');
|
||||||
}
|
}
|
||||||
|
@ -78,16 +87,19 @@ final class PhabricatorHovercardView extends AphrontView {
|
||||||
$viewer = $this->getUser();
|
$viewer = $this->getUser();
|
||||||
$handle = $this->handle;
|
$handle = $this->handle;
|
||||||
|
|
||||||
require_celerity_resource('phabricator-hovercard-view-css');
|
require_celerity_resource('phui-hovercard-view-css');
|
||||||
|
|
||||||
|
// If we're a fully custom Hovercard, skip the common UI
|
||||||
|
$children = $this->renderChildren();
|
||||||
|
if ($children) {
|
||||||
|
return $children;
|
||||||
|
}
|
||||||
|
|
||||||
$title = array(
|
$title = array(
|
||||||
id(new PHUISpacesNamespaceContextView())
|
id(new PHUISpacesNamespaceContextView())
|
||||||
->setUser($viewer)
|
->setUser($viewer)
|
||||||
->setObject($this->getObject()),
|
->setObject($this->getObject()),
|
||||||
pht(
|
$this->title ? $this->title : $handle->getName(),
|
||||||
'%s: %s',
|
|
||||||
$handle->getTypeName(),
|
|
||||||
$this->title ? $this->title : $handle->getName()),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
$header = new PHUIHeaderView();
|
$header = new PHUIHeaderView();
|
||||||
|
@ -107,7 +119,7 @@ final class PhabricatorHovercardView extends AphrontView {
|
||||||
$body_title = $handle->getFullName();
|
$body_title = $handle->getFullName();
|
||||||
}
|
}
|
||||||
|
|
||||||
$body[] = phutil_tag_div('phabricator-hovercard-body-header', $body_title);
|
$body[] = phutil_tag_div('phui-hovercard-body-header', $body_title);
|
||||||
|
|
||||||
foreach ($this->fields as $field) {
|
foreach ($this->fields as $field) {
|
||||||
$item = array(
|
$item = array(
|
||||||
|
@ -115,7 +127,7 @@ final class PhabricatorHovercardView extends AphrontView {
|
||||||
': ',
|
': ',
|
||||||
phutil_tag('span', array(), $field['value']),
|
phutil_tag('span', array(), $field['value']),
|
||||||
);
|
);
|
||||||
$body[] = phutil_tag_div('phabricator-hovercard-body-item', $item);
|
$body[] = phutil_tag_div('phui-hovercard-body-item', $item);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->badges) {
|
if ($this->badges) {
|
||||||
|
@ -125,7 +137,7 @@ final class PhabricatorHovercardView extends AphrontView {
|
||||||
$body[] = phutil_tag(
|
$body[] = phutil_tag(
|
||||||
'div',
|
'div',
|
||||||
array(
|
array(
|
||||||
'class' => 'phabricator-hovercard-body-item hovercard-badges',
|
'class' => 'phui-hovercard-body-item hovercard-badges',
|
||||||
),
|
),
|
||||||
$badges);
|
$badges);
|
||||||
}
|
}
|
||||||
|
@ -136,7 +148,7 @@ final class PhabricatorHovercardView extends AphrontView {
|
||||||
$body = phutil_tag(
|
$body = phutil_tag(
|
||||||
'div',
|
'div',
|
||||||
array(
|
array(
|
||||||
'class' => 'phabricator-hovercard-body-image',
|
'class' => 'phui-hovercard-body-image',
|
||||||
),
|
),
|
||||||
phutil_tag(
|
phutil_tag(
|
||||||
'div',
|
'div',
|
||||||
|
@ -149,7 +161,7 @@ final class PhabricatorHovercardView extends AphrontView {
|
||||||
phutil_tag(
|
phutil_tag(
|
||||||
'div',
|
'div',
|
||||||
array(
|
array(
|
||||||
'class' => 'phabricator-hovercard-body-details',
|
'class' => 'phui-hovercard-body-details',
|
||||||
),
|
),
|
||||||
$body));
|
$body));
|
||||||
}
|
}
|
||||||
|
@ -178,18 +190,18 @@ final class PhabricatorHovercardView extends AphrontView {
|
||||||
|
|
||||||
$tail = null;
|
$tail = null;
|
||||||
if ($buttons) {
|
if ($buttons) {
|
||||||
$tail = phutil_tag_div('phabricator-hovercard-tail', $buttons);
|
$tail = phutil_tag_div('phui-hovercard-tail', $buttons);
|
||||||
}
|
}
|
||||||
|
|
||||||
$hovercard = phutil_tag_div(
|
$hovercard = phutil_tag_div(
|
||||||
'phabricator-hovercard-container',
|
'phui-hovercard-container grouped',
|
||||||
array(
|
array(
|
||||||
phutil_tag_div('phabricator-hovercard-head', $header),
|
phutil_tag_div('phui-hovercard-head', $header),
|
||||||
phutil_tag_div('phabricator-hovercard-body grouped', $body),
|
phutil_tag_div('phui-hovercard-body grouped', $body),
|
||||||
$tail,
|
$tail,
|
||||||
));
|
));
|
||||||
|
|
||||||
return phutil_tag_div('phabricator-hovercard-wrapper', $hovercard);
|
return $hovercard;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -9,6 +9,7 @@ final class PHUIObjectItemListView extends AphrontTagView {
|
||||||
private $flush;
|
private $flush;
|
||||||
private $allowEmptyList;
|
private $allowEmptyList;
|
||||||
private $states;
|
private $states;
|
||||||
|
private $itemClass = 'phui-object-item-standard';
|
||||||
|
|
||||||
public function setAllowEmptyList($allow_empty_list) {
|
public function setAllowEmptyList($allow_empty_list) {
|
||||||
$this->allowEmptyList = $allow_empty_list;
|
$this->allowEmptyList = $allow_empty_list;
|
||||||
|
@ -49,6 +50,11 @@ final class PHUIObjectItemListView extends AphrontTagView {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setItemClass($item_class) {
|
||||||
|
$this->itemClass = $item_class;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
protected function getTagName() {
|
protected function getTagName() {
|
||||||
return 'ul';
|
return 'ul';
|
||||||
}
|
}
|
||||||
|
@ -89,6 +95,11 @@ final class PHUIObjectItemListView extends AphrontTagView {
|
||||||
$item->setUser($viewer);
|
$item->setUser($viewer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($this->items as $item) {
|
||||||
|
$item->addClass($this->itemClass);
|
||||||
|
}
|
||||||
|
|
||||||
$items = $this->items;
|
$items = $this->items;
|
||||||
} else if ($this->allowEmptyList) {
|
} else if ($this->allowEmptyList) {
|
||||||
$items = null;
|
$items = null;
|
||||||
|
|
|
@ -383,17 +383,24 @@ final class PHUIObjectItemView extends AphrontTagView {
|
||||||
),
|
),
|
||||||
$this->header);
|
$this->header);
|
||||||
|
|
||||||
$header = javelin_tag(
|
// Wrap the header content in a <span> with the "slippery" sigil. This
|
||||||
|
// prevents us from beginning a drag if you click the text (like "T123"),
|
||||||
|
// but not if you click the white space after the header.
|
||||||
|
$header = phutil_tag(
|
||||||
'div',
|
'div',
|
||||||
array(
|
array(
|
||||||
'class' => 'phui-object-item-name',
|
'class' => 'phui-object-item-name',
|
||||||
|
),
|
||||||
|
javelin_tag(
|
||||||
|
'span',
|
||||||
|
array(
|
||||||
'sigil' => 'slippery',
|
'sigil' => 'slippery',
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
$this->headIcons,
|
$this->headIcons,
|
||||||
$header_name,
|
$header_name,
|
||||||
$header_link,
|
$header_link,
|
||||||
));
|
)));
|
||||||
|
|
||||||
$icons = array();
|
$icons = array();
|
||||||
if ($this->icons) {
|
if ($this->icons) {
|
||||||
|
@ -452,14 +459,15 @@ final class PHUIObjectItemView extends AphrontTagView {
|
||||||
$icon_list);
|
$icon_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$handle_bar = null;
|
||||||
if ($this->handleIcons) {
|
if ($this->handleIcons) {
|
||||||
$handle_bar = array();
|
$handle_bar = array();
|
||||||
foreach ($this->handleIcons as $handleicon) {
|
foreach ($this->handleIcons as $handleicon) {
|
||||||
$handle_bar[] =
|
$handle_bar[] =
|
||||||
$this->renderHandleIcon($handleicon['icon'], $handleicon['label']);
|
$this->renderHandleIcon($handleicon['icon'], $handleicon['label']);
|
||||||
}
|
}
|
||||||
$icons[] = phutil_tag(
|
$handle_bar = phutil_tag(
|
||||||
'div',
|
'li',
|
||||||
array(
|
array(
|
||||||
'class' => 'phui-object-item-handle-icons',
|
'class' => 'phui-object-item-handle-icons',
|
||||||
),
|
),
|
||||||
|
@ -504,7 +512,7 @@ final class PHUIObjectItemView extends AphrontTagView {
|
||||||
}
|
}
|
||||||
|
|
||||||
$attrs = null;
|
$attrs = null;
|
||||||
if ($this->attributes) {
|
if ($this->attributes || $handle_bar) {
|
||||||
$attrs = array();
|
$attrs = array();
|
||||||
$spacer = phutil_tag(
|
$spacer = phutil_tag(
|
||||||
'span',
|
'span',
|
||||||
|
@ -531,7 +539,10 @@ final class PHUIObjectItemView extends AphrontTagView {
|
||||||
array(
|
array(
|
||||||
'class' => 'phui-object-item-attributes',
|
'class' => 'phui-object-item-attributes',
|
||||||
),
|
),
|
||||||
$attrs);
|
array(
|
||||||
|
$handle_bar,
|
||||||
|
$attrs,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
$status = null;
|
$status = null;
|
||||||
|
@ -750,7 +761,7 @@ final class PHUIObjectItemView extends AphrontTagView {
|
||||||
|
|
||||||
if (strlen($label)) {
|
if (strlen($label)) {
|
||||||
$options['sigil'] = 'has-tooltip';
|
$options['sigil'] = 'has-tooltip';
|
||||||
$options['meta'] = array('tip' => $label);
|
$options['meta'] = array('tip' => $label, 'align' => 'E');
|
||||||
}
|
}
|
||||||
|
|
||||||
return javelin_tag('span', $options, '');
|
return javelin_tag('span', $options, '');
|
||||||
|
|
|
@ -115,14 +115,14 @@ final class PHUIPropertyListView extends AphrontView {
|
||||||
$this->invokedWillRenderEvent = true;
|
$this->invokedWillRenderEvent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isEmpty() {
|
public function hasAnyProperties() {
|
||||||
$this->invokeWillRenderEvent();
|
$this->invokeWillRenderEvent();
|
||||||
|
|
||||||
if ($this->parts) {
|
if ($this->parts) {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render() {
|
public function render() {
|
||||||
|
|
|
@ -122,7 +122,7 @@ final class PHUITagView extends AphrontTagView {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->phid) {
|
if ($this->phid) {
|
||||||
Javelin::initBehavior('phabricator-hovercards');
|
Javelin::initBehavior('phui-hovercards');
|
||||||
|
|
||||||
$attributes = array(
|
$attributes = array(
|
||||||
'href' => $this->href,
|
'href' => $this->href,
|
||||||
|
|
|
@ -10,8 +10,8 @@ final class PHUIWorkpanelView extends AphrontTagView {
|
||||||
private $headerTag;
|
private $headerTag;
|
||||||
private $headerIcon;
|
private $headerIcon;
|
||||||
|
|
||||||
public function setHeaderIcon(PHUIIconView $header_icon) {
|
public function setHeaderIcon($icon) {
|
||||||
$this->headerIcon = $header_icon;
|
$this->headerIcon = $icon;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,9 @@
|
||||||
|
|
||||||
"globals": {
|
"globals": {
|
||||||
"JX": false,
|
"JX": false,
|
||||||
"Raphael": false,
|
"d3": false,
|
||||||
"__DEV__": false
|
"__DEV__": false
|
||||||
},
|
},
|
||||||
|
|
||||||
"browser": true
|
"browser": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,14 +19,14 @@
|
||||||
|
|
||||||
.phabricator-standard-page-footer {
|
.phabricator-standard-page-footer {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
margin: 32px 16px 16px;
|
margin: 44px 16px 16px;
|
||||||
padding: 12px 0;
|
padding: 12px 0;
|
||||||
border-top: 1px solid rgba(71, 87, 120, 0.20);
|
border-top: 1px solid rgba(55,55,55,.1);
|
||||||
color: {$greytext};
|
color: {$greytext};
|
||||||
}
|
}
|
||||||
|
|
||||||
.device .phabricator-standard-page-footer {
|
.device .phabricator-standard-page-footer {
|
||||||
margin: 4px 8px;
|
margin: 24px 8px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
!print .phabricator-standard-page-footer {
|
!print .phabricator-standard-page-footer {
|
||||||
|
|
|
@ -43,24 +43,34 @@
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device .phame-home-view .phui-side-column {
|
.phame-home-view {
|
||||||
background-color: transparent;
|
background-color: #fff;
|
||||||
|
border-bottom: 1px solid rgba(55,55,55,.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.phame-home-view .phame-home-container {
|
||||||
|
max-width: 980px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phame-home-view .phui-document-container {
|
||||||
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.phame-blog-list {
|
.phame-blog-list {
|
||||||
margin: 24px 16px 16px 16px;
|
margin: 96px 16px 16px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phame-blog-list + .phame-blog-list {
|
||||||
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device .phame-blog-list {
|
.device .phame-blog-list {
|
||||||
padding: 0;
|
margin: 16px;
|
||||||
background-color: {$bluebackground};
|
|
||||||
margin: 0;
|
|
||||||
border-radius: 0;
|
|
||||||
border-bottom: 1px solid {$thinblueborder};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.phame-blog-list-item:last-child {
|
.device-phone .phame-blog-list {
|
||||||
margin-bottom: 0;
|
margin: 16px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.phame-blog-list-header {
|
.phame-blog-list-header {
|
||||||
|
|
150
webroot/rsrc/css/application/project/project-card-view.css
Normal file
150
webroot/rsrc/css/application/project/project-card-view.css
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
/**
|
||||||
|
* @provides project-card-view-css
|
||||||
|
*/
|
||||||
|
|
||||||
|
.project-card-view {
|
||||||
|
margin: 0 12px 16px 0;
|
||||||
|
text-align: left;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid {$lightblueborder};
|
||||||
|
border-radius: 3px;
|
||||||
|
box-shadow: {$dropshadow};
|
||||||
|
width: 380px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view .phui-header-shell {
|
||||||
|
margin: 0;
|
||||||
|
padding: 12px 12px 16px 12px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view .phui-header-shell .phui-header-image {
|
||||||
|
border: 3px solid #fff;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view .phui-header-shell .phui-header-header {
|
||||||
|
font-size: 18px;
|
||||||
|
font-family: 'Aleo', {$fontfamily};
|
||||||
|
width: 290px;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view .phui-header-shell .phui-header-col1 {
|
||||||
|
vertical-align: top;
|
||||||
|
width: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view .phui-header-subheader {
|
||||||
|
font-size: {$normalfontsize};
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view .phui-header-header .phui-tag-view {
|
||||||
|
display: block;
|
||||||
|
font-weight: normal;
|
||||||
|
color: {$bluetext};
|
||||||
|
font-size: {$normalfontsize};
|
||||||
|
font-family: {$fontfamily};
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view .phui-header-header .phui-tag-view .phui-tag-core {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view .phui-header-header .phui-tag-view .phui-icon-view {
|
||||||
|
margin-left: 0;
|
||||||
|
color: {$bluetext};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Colors */
|
||||||
|
|
||||||
|
.project-card-view.project-card-red {
|
||||||
|
border-color: {$sh-redborder};
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view.project-card-red .phui-header-shell {
|
||||||
|
background: linear-gradient(to bottom,
|
||||||
|
{$sh-redbackground} 42px, #fff 42px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view.project-card-orange {
|
||||||
|
border-color: {$sh-orangeborder};
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view.project-card-orange .phui-header-shell {
|
||||||
|
background: linear-gradient(to bottom,
|
||||||
|
{$sh-orangebackground} 42px, #fff 42px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view.project-card-yellow {
|
||||||
|
border-color: {$sh-yellowborder};
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view.project-card-yellow .phui-header-shell {
|
||||||
|
background: linear-gradient(to bottom,
|
||||||
|
{$sh-yellowbackground} 42px, #fff 42px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view.project-card-green {
|
||||||
|
border-color: {$sh-greenborder};
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view.project-card-green .phui-header-shell {
|
||||||
|
background: linear-gradient(to bottom,
|
||||||
|
{$sh-greenbackground} 42px, #fff 42px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view.project-card-blue {
|
||||||
|
border-color: {$sh-blueborder};
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view.project-card-blue .phui-header-shell {
|
||||||
|
background: linear-gradient(to bottom,
|
||||||
|
{$sh-bluebackground} 42px, #fff 42px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view.project-card-indigo {
|
||||||
|
border-color: {$sh-indigoborder};
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view.project-card-indigo .phui-header-shell {
|
||||||
|
background: linear-gradient(to bottom,
|
||||||
|
{$sh-indigobackground} 42px, #fff 42px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view.project-card-violet {
|
||||||
|
border-color: {$sh-violetborder};
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view.project-card-violet .phui-header-shell {
|
||||||
|
background: linear-gradient(to bottom,
|
||||||
|
{$sh-violetbackground} 42px, #fff 42px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view.project-card-pink {
|
||||||
|
border-color: {$sh-pinkborder};
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view.project-card-pink .phui-header-shell {
|
||||||
|
background: linear-gradient(to bottom,
|
||||||
|
{$sh-pinkbackground} 42px, #fff 42px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view.project-card-grey,
|
||||||
|
.project-card-view.project-card-checkered {
|
||||||
|
border-color: {$sh-greyborder};
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card-view.project-card-grey .phui-header-shell,
|
||||||
|
.project-card-view.project-card-checkered .phui-header-shell {
|
||||||
|
background: linear-gradient(to bottom,
|
||||||
|
{$sh-greybackground} 42px, #fff 42px);
|
||||||
|
}
|
|
@ -7,6 +7,23 @@
|
||||||
padding-bottom: 64px;
|
padding-bottom: 64px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.project-view-header-tag {
|
||||||
|
margin-left: 8px;
|
||||||
|
font-size: {$normalfontsize};
|
||||||
|
color: {$bluetext};
|
||||||
|
font-family: {$fontfamily};
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-phone .project-view-header-tag {
|
||||||
|
display: block;
|
||||||
|
margin-left: -4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-view-header-tag .phui-icon-view {
|
||||||
|
color: {$bluetext};
|
||||||
|
}
|
||||||
|
|
||||||
.phui-box.phui-box-grey.project-view-properties {
|
.phui-box.phui-box-grey.project-view-properties {
|
||||||
margin: 0 16px 0 16px;
|
margin: 0 16px 0 16px;
|
||||||
padding: 4px 12px;
|
padding: 4px 12px;
|
||||||
|
@ -31,13 +48,18 @@
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-view-feed .phui-header-shell {
|
.project-view-feed .phui-object-box .phui-header-shell {
|
||||||
padding: 8px 4px;
|
padding: 8px 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-view-feed .phui-header-header {
|
.project-view-feed .phui-header-header {
|
||||||
font-size: {$biggerfontsize};
|
font-size: {$biggerfontsize};
|
||||||
margin-left: 4px;
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-desktop .project-view-feed .phui-feed-story,
|
||||||
|
.device-tablet .project-view-feed .phui-feed-story {
|
||||||
|
padding-left: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-view-home .phui-box-grey {
|
.project-view-home .phui-box-grey {
|
||||||
|
@ -60,6 +82,10 @@
|
||||||
width: 364px;
|
width: 364px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.project-view-home .phui-box-grey .phui-object-item-attribute .phui-icon-view {
|
||||||
|
color: {$lightgreytext};
|
||||||
|
}
|
||||||
|
|
||||||
.profile-no-badges {
|
.profile-no-badges {
|
||||||
padding: 24px 0;
|
padding: 24px 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,10 +81,6 @@ div.phui-calendar-day-event {
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.drag-dragging {
|
|
||||||
z-index: 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.phui-calendar-date-number {
|
.phui-calendar-date-number {
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
}
|
}
|
||||||
|
@ -114,6 +110,10 @@ div.phui-calendar-day-event {
|
||||||
z-index: 9;
|
z-index: 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.drag-frame {
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
.jx-mask {
|
.jx-mask {
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
54
webroot/rsrc/css/phui/phui-chart.css
Normal file
54
webroot/rsrc/css/phui/phui-chart.css
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/**
|
||||||
|
* @provides phui-chart-css
|
||||||
|
*/
|
||||||
|
|
||||||
|
.chart .axis line,
|
||||||
|
.chart .axis path {
|
||||||
|
fill: none;
|
||||||
|
stroke: {$blueborder};
|
||||||
|
shape-rendering: crispEdges;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart .axis text {
|
||||||
|
fill: {$darkgreytext};
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart .outer,
|
||||||
|
.chart .inner {
|
||||||
|
shape-rendering: crispEdges;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart .outer {
|
||||||
|
fill: none;
|
||||||
|
stroke: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart .inner {
|
||||||
|
fill: {$lightbluebackground};
|
||||||
|
stroke: {$lightblueborder};
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart .line {
|
||||||
|
fill: none;
|
||||||
|
stroke: {$blue};
|
||||||
|
stroke-width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart .point {
|
||||||
|
fill: {$lightblue};
|
||||||
|
stroke: {$blue};
|
||||||
|
stroke-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-tooltip {
|
||||||
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
width: 120px;
|
||||||
|
height: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 2px;
|
||||||
|
background: {$lightbluebackground};
|
||||||
|
border: 1px solid {$blueborder};
|
||||||
|
border-radius: 8px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue