mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-27 09:12:41 +01:00
(stable) Promote 2016 Week 47
This commit is contained in:
commit
10c4dedd18
126 changed files with 3139 additions and 1185 deletions
|
@ -9,8 +9,8 @@ return array(
|
||||||
'names' => array(
|
'names' => array(
|
||||||
'conpherence.pkg.css' => '0b64e988',
|
'conpherence.pkg.css' => '0b64e988',
|
||||||
'conpherence.pkg.js' => '6249a1cf',
|
'conpherence.pkg.js' => '6249a1cf',
|
||||||
'core.pkg.css' => 'a729d20e',
|
'core.pkg.css' => '347113ea',
|
||||||
'core.pkg.js' => '1a77dddf',
|
'core.pkg.js' => '40e98735',
|
||||||
'darkconsole.pkg.js' => 'e7393ebb',
|
'darkconsole.pkg.js' => 'e7393ebb',
|
||||||
'differential.pkg.css' => 'a4ba74b5',
|
'differential.pkg.css' => 'a4ba74b5',
|
||||||
'differential.pkg.js' => '634399e9',
|
'differential.pkg.js' => '634399e9',
|
||||||
|
@ -21,8 +21,7 @@ return array(
|
||||||
'maniphest.pkg.js' => '949a7498',
|
'maniphest.pkg.js' => '949a7498',
|
||||||
'rsrc/css/aphront/aphront-bars.css' => '231ac33c',
|
'rsrc/css/aphront/aphront-bars.css' => '231ac33c',
|
||||||
'rsrc/css/aphront/dark-console.css' => 'f54bf286',
|
'rsrc/css/aphront/dark-console.css' => 'f54bf286',
|
||||||
'rsrc/css/aphront/dialog-view.css' => 'ea3745f5',
|
'rsrc/css/aphront/dialog-view.css' => '49b2a8a3',
|
||||||
'rsrc/css/aphront/lightbox-attachment.css' => '7acac05d',
|
|
||||||
'rsrc/css/aphront/list-filter-view.css' => '5d6f0526',
|
'rsrc/css/aphront/list-filter-view.css' => '5d6f0526',
|
||||||
'rsrc/css/aphront/multi-column.css' => '84cc6640',
|
'rsrc/css/aphront/multi-column.css' => '84cc6640',
|
||||||
'rsrc/css/aphront/notification.css' => '3f6c89c9',
|
'rsrc/css/aphront/notification.css' => '3f6c89c9',
|
||||||
|
@ -84,7 +83,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' => '1898e534',
|
'rsrc/css/application/paste/paste.css' => '1898e534',
|
||||||
'rsrc/css/application/people/people-profile.css' => '2473d929',
|
'rsrc/css/application/people/people-profile.css' => '2473d929',
|
||||||
'rsrc/css/application/phame/phame.css' => '8efb0729',
|
'rsrc/css/application/phame/phame.css' => '654dd9ef',
|
||||||
'rsrc/css/application/pholio/pholio-edit.css' => '07676f51',
|
'rsrc/css/application/pholio/pholio-edit.css' => '07676f51',
|
||||||
'rsrc/css/application/pholio/pholio-inline-comments.css' => '8e545e49',
|
'rsrc/css/application/pholio/pholio-inline-comments.css' => '8e545e49',
|
||||||
'rsrc/css/application/pholio/pholio.css' => 'ca89d380',
|
'rsrc/css/application/pholio/pholio.css' => 'ca89d380',
|
||||||
|
@ -133,14 +132,15 @@ return array(
|
||||||
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
|
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
|
||||||
'rsrc/css/phui/phui-cms.css' => 'be43c8a8',
|
'rsrc/css/phui/phui-cms.css' => 'be43c8a8',
|
||||||
'rsrc/css/phui/phui-comment-form.css' => '4ecc56ef',
|
'rsrc/css/phui/phui-comment-form.css' => '4ecc56ef',
|
||||||
|
'rsrc/css/phui/phui-comment-panel.css' => '85113e6a',
|
||||||
'rsrc/css/phui/phui-crumbs-view.css' => '195ac419',
|
'rsrc/css/phui/phui-crumbs-view.css' => '195ac419',
|
||||||
'rsrc/css/phui/phui-curtain-view.css' => '947bf1a4',
|
'rsrc/css/phui/phui-curtain-view.css' => '947bf1a4',
|
||||||
'rsrc/css/phui/phui-document-pro.css' => 'ca1fed81',
|
'rsrc/css/phui/phui-document-pro.css' => 'c354e312',
|
||||||
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
|
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
|
||||||
'rsrc/css/phui/phui-document.css' => 'c32e8dec',
|
'rsrc/css/phui/phui-document.css' => 'c32e8dec',
|
||||||
'rsrc/css/phui/phui-feed-story.css' => '44a9c8e9',
|
'rsrc/css/phui/phui-feed-story.css' => '44a9c8e9',
|
||||||
'rsrc/css/phui/phui-fontkit.css' => '9cda225e',
|
'rsrc/css/phui/phui-fontkit.css' => '9cda225e',
|
||||||
'rsrc/css/phui/phui-form-view.css' => '91adabe4',
|
'rsrc/css/phui/phui-form-view.css' => '3fadd537',
|
||||||
'rsrc/css/phui/phui-form.css' => 'b8fb087a',
|
'rsrc/css/phui/phui-form.css' => 'b8fb087a',
|
||||||
'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f',
|
'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f',
|
||||||
'rsrc/css/phui/phui-header-view.css' => '6ec8f155',
|
'rsrc/css/phui/phui-header-view.css' => '6ec8f155',
|
||||||
|
@ -149,8 +149,9 @@ return array(
|
||||||
'rsrc/css/phui/phui-icon.css' => '417f80fb',
|
'rsrc/css/phui/phui-icon.css' => '417f80fb',
|
||||||
'rsrc/css/phui/phui-image-mask.css' => 'a8498f9c',
|
'rsrc/css/phui/phui-image-mask.css' => 'a8498f9c',
|
||||||
'rsrc/css/phui/phui-info-panel.css' => '27ea50a1',
|
'rsrc/css/phui/phui-info-panel.css' => '27ea50a1',
|
||||||
'rsrc/css/phui/phui-info-view.css' => '28efab79',
|
'rsrc/css/phui/phui-info-view.css' => 'ec92802a',
|
||||||
'rsrc/css/phui/phui-invisible-character-view.css' => '6993d9f0',
|
'rsrc/css/phui/phui-invisible-character-view.css' => '6993d9f0',
|
||||||
|
'rsrc/css/phui/phui-lightbox.css' => 'e17ce2bd',
|
||||||
'rsrc/css/phui/phui-list.css' => '9da2aa00',
|
'rsrc/css/phui/phui-list.css' => '9da2aa00',
|
||||||
'rsrc/css/phui/phui-object-box.css' => '6b487c57',
|
'rsrc/css/phui/phui-object-box.css' => '6b487c57',
|
||||||
'rsrc/css/phui/phui-object-item-list-view.css' => '87278fa0',
|
'rsrc/css/phui/phui-object-item-list-view.css' => '87278fa0',
|
||||||
|
@ -165,8 +166,8 @@ return array(
|
||||||
'rsrc/css/phui/phui-tag-view.css' => '6bbd83e2',
|
'rsrc/css/phui/phui-tag-view.css' => '6bbd83e2',
|
||||||
'rsrc/css/phui/phui-timeline-view.css' => 'bc523970',
|
'rsrc/css/phui/phui-timeline-view.css' => 'bc523970',
|
||||||
'rsrc/css/phui/phui-two-column-view.css' => 'bbe32c23',
|
'rsrc/css/phui/phui-two-column-view.css' => 'bbe32c23',
|
||||||
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7',
|
'rsrc/css/phui/workboards/phui-workboard-color.css' => '207828dd',
|
||||||
'rsrc/css/phui/workboards/phui-workboard.css' => 'e09eb53a',
|
'rsrc/css/phui/workboards/phui-workboard.css' => '60d09514',
|
||||||
'rsrc/css/phui/workboards/phui-workcard.css' => '0c62d7c5',
|
'rsrc/css/phui/workboards/phui-workcard.css' => '0c62d7c5',
|
||||||
'rsrc/css/phui/workboards/phui-workpanel.css' => '92197373',
|
'rsrc/css/phui/workboards/phui-workpanel.css' => '92197373',
|
||||||
'rsrc/css/sprite-login.css' => '6dbbbd97',
|
'rsrc/css/sprite-login.css' => '6dbbbd97',
|
||||||
|
@ -261,7 +262,7 @@ return array(
|
||||||
'rsrc/externals/javelin/lib/behavior.js' => '61cbc29a',
|
'rsrc/externals/javelin/lib/behavior.js' => '61cbc29a',
|
||||||
'rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js' => '8d3bc1b2',
|
'rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js' => '8d3bc1b2',
|
||||||
'rsrc/externals/javelin/lib/control/typeahead/Typeahead.js' => '70baed2f',
|
'rsrc/externals/javelin/lib/control/typeahead/Typeahead.js' => '70baed2f',
|
||||||
'rsrc/externals/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js' => 'e6e25838',
|
'rsrc/externals/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js' => '185bbd53',
|
||||||
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js' => '503e17fd',
|
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js' => '503e17fd',
|
||||||
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js' => '013ffff9',
|
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js' => '013ffff9',
|
||||||
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js' => '54f314a0',
|
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js' => '54f314a0',
|
||||||
|
@ -323,12 +324,6 @@ return array(
|
||||||
'rsrc/image/icon/fatcow/source/mobile.png' => 'f1321264',
|
'rsrc/image/icon/fatcow/source/mobile.png' => 'f1321264',
|
||||||
'rsrc/image/icon/fatcow/source/tablet.png' => '49396799',
|
'rsrc/image/icon/fatcow/source/tablet.png' => '49396799',
|
||||||
'rsrc/image/icon/fatcow/source/web.png' => '136ccb5d',
|
'rsrc/image/icon/fatcow/source/web.png' => '136ccb5d',
|
||||||
'rsrc/image/icon/lightbox/close-2.png' => 'cc40e7c8',
|
|
||||||
'rsrc/image/icon/lightbox/close-hover-2.png' => 'fb5d6d9e',
|
|
||||||
'rsrc/image/icon/lightbox/left-arrow-2.png' => '8426133b',
|
|
||||||
'rsrc/image/icon/lightbox/left-arrow-hover-2.png' => '701e5ee3',
|
|
||||||
'rsrc/image/icon/lightbox/right-arrow-2.png' => '6d5519a0',
|
|
||||||
'rsrc/image/icon/lightbox/right-arrow-hover-2.png' => '3a04aa21',
|
|
||||||
'rsrc/image/icon/subscribe.png' => 'd03ed5a5',
|
'rsrc/image/icon/subscribe.png' => 'd03ed5a5',
|
||||||
'rsrc/image/icon/tango/attachment.png' => 'ecc8022e',
|
'rsrc/image/icon/tango/attachment.png' => 'ecc8022e',
|
||||||
'rsrc/image/icon/tango/edit.png' => '929a1363',
|
'rsrc/image/icon/tango/edit.png' => '929a1363',
|
||||||
|
@ -477,7 +472,7 @@ 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' => '58dea2fa',
|
'rsrc/js/core/DragAndDropFileUpload.js' => '58dea2fa',
|
||||||
'rsrc/js/core/DraggableList.js' => '5a13c79f',
|
'rsrc/js/core/DraggableList.js' => 'bea6e7f4',
|
||||||
'rsrc/js/core/Favicon.js' => '1fe2510c',
|
'rsrc/js/core/Favicon.js' => '1fe2510c',
|
||||||
'rsrc/js/core/FileUpload.js' => '680ea2c8',
|
'rsrc/js/core/FileUpload.js' => '680ea2c8',
|
||||||
'rsrc/js/core/Hovercard.js' => '1bd28176',
|
'rsrc/js/core/Hovercard.js' => '1bd28176',
|
||||||
|
@ -500,7 +495,7 @@ return array(
|
||||||
'rsrc/js/core/behavior-device.js' => 'bb1dd507',
|
'rsrc/js/core/behavior-device.js' => 'bb1dd507',
|
||||||
'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '484a6e22',
|
'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '484a6e22',
|
||||||
'rsrc/js/core/behavior-error-log.js' => '6882e80a',
|
'rsrc/js/core/behavior-error-log.js' => '6882e80a',
|
||||||
'rsrc/js/core/behavior-fancy-datepicker.js' => '568931f3',
|
'rsrc/js/core/behavior-fancy-datepicker.js' => 'a9210d03',
|
||||||
'rsrc/js/core/behavior-file-tree.js' => '88236f00',
|
'rsrc/js/core/behavior-file-tree.js' => '88236f00',
|
||||||
'rsrc/js/core/behavior-form.js' => '5c54cbf3',
|
'rsrc/js/core/behavior-form.js' => '5c54cbf3',
|
||||||
'rsrc/js/core/behavior-gesture.js' => '3ab51e2c',
|
'rsrc/js/core/behavior-gesture.js' => '3ab51e2c',
|
||||||
|
@ -510,7 +505,7 @@ return array(
|
||||||
'rsrc/js/core/behavior-hovercard.js' => 'bcaccd64',
|
'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' => '01fca1f0',
|
'rsrc/js/core/behavior-keyboard-shortcuts.js' => '01fca1f0',
|
||||||
'rsrc/js/core/behavior-lightbox-attachments.js' => 'f8ba29d7',
|
'rsrc/js/core/behavior-lightbox-attachments.js' => 'ec949017',
|
||||||
'rsrc/js/core/behavior-line-linker.js' => '1499a8cb',
|
'rsrc/js/core/behavior-line-linker.js' => '1499a8cb',
|
||||||
'rsrc/js/core/behavior-more.js' => 'a80d0378',
|
'rsrc/js/core/behavior-more.js' => 'a80d0378',
|
||||||
'rsrc/js/core/behavior-object-selector.js' => 'e0ec7f2f',
|
'rsrc/js/core/behavior-object-selector.js' => 'e0ec7f2f',
|
||||||
|
@ -550,7 +545,7 @@ return array(
|
||||||
'almanac-css' => 'dbb9b3af',
|
'almanac-css' => 'dbb9b3af',
|
||||||
'aphront-bars' => '231ac33c',
|
'aphront-bars' => '231ac33c',
|
||||||
'aphront-dark-console-css' => 'f54bf286',
|
'aphront-dark-console-css' => 'f54bf286',
|
||||||
'aphront-dialog-view-css' => 'ea3745f5',
|
'aphront-dialog-view-css' => '49b2a8a3',
|
||||||
'aphront-list-filter-view-css' => '5d6f0526',
|
'aphront-list-filter-view-css' => '5d6f0526',
|
||||||
'aphront-multi-column-view-css' => '84cc6640',
|
'aphront-multi-column-view-css' => '84cc6640',
|
||||||
'aphront-panel-view-css' => '8427b78d',
|
'aphront-panel-view-css' => '8427b78d',
|
||||||
|
@ -649,14 +644,14 @@ return array(
|
||||||
'javelin-behavior-editengine-reorder-fields' => 'b59e1e96',
|
'javelin-behavior-editengine-reorder-fields' => 'b59e1e96',
|
||||||
'javelin-behavior-error-log' => '6882e80a',
|
'javelin-behavior-error-log' => '6882e80a',
|
||||||
'javelin-behavior-event-all-day' => 'b41537c9',
|
'javelin-behavior-event-all-day' => 'b41537c9',
|
||||||
'javelin-behavior-fancy-datepicker' => '568931f3',
|
'javelin-behavior-fancy-datepicker' => 'a9210d03',
|
||||||
'javelin-behavior-global-drag-and-drop' => '960f6a39',
|
'javelin-behavior-global-drag-and-drop' => '960f6a39',
|
||||||
'javelin-behavior-herald-rule-editor' => '7ebaeed3',
|
'javelin-behavior-herald-rule-editor' => '7ebaeed3',
|
||||||
'javelin-behavior-high-security-warning' => 'a464fe03',
|
'javelin-behavior-high-security-warning' => 'a464fe03',
|
||||||
'javelin-behavior-history-install' => '7ee2b591',
|
'javelin-behavior-history-install' => '7ee2b591',
|
||||||
'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' => 'ec949017',
|
||||||
'javelin-behavior-line-chart' => 'e4232876',
|
'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',
|
||||||
|
@ -753,7 +748,7 @@ return array(
|
||||||
'javelin-tokenizer' => '8d3bc1b2',
|
'javelin-tokenizer' => '8d3bc1b2',
|
||||||
'javelin-typeahead' => '70baed2f',
|
'javelin-typeahead' => '70baed2f',
|
||||||
'javelin-typeahead-composite-source' => '503e17fd',
|
'javelin-typeahead-composite-source' => '503e17fd',
|
||||||
'javelin-typeahead-normalizer' => 'e6e25838',
|
'javelin-typeahead-normalizer' => '185bbd53',
|
||||||
'javelin-typeahead-ondemand-source' => '013ffff9',
|
'javelin-typeahead-ondemand-source' => '013ffff9',
|
||||||
'javelin-typeahead-preloaded-source' => '54f314a0',
|
'javelin-typeahead-preloaded-source' => '54f314a0',
|
||||||
'javelin-typeahead-source' => '0fcf201c',
|
'javelin-typeahead-source' => '0fcf201c',
|
||||||
|
@ -772,7 +767,6 @@ return array(
|
||||||
'javelin-workboard-column' => '21df4ff5',
|
'javelin-workboard-column' => '21df4ff5',
|
||||||
'javelin-workboard-controller' => '55baf5ed',
|
'javelin-workboard-controller' => '55baf5ed',
|
||||||
'javelin-workflow' => '1e911d0f',
|
'javelin-workflow' => '1e911d0f',
|
||||||
'lightbox-attachment-css' => '7acac05d',
|
|
||||||
'maniphest-batch-editor' => 'b0f0b6d5',
|
'maniphest-batch-editor' => 'b0f0b6d5',
|
||||||
'maniphest-report-css' => '9b9580b7',
|
'maniphest-report-css' => '9b9580b7',
|
||||||
'maniphest-task-edit-css' => 'fda62a9b',
|
'maniphest-task-edit-css' => 'fda62a9b',
|
||||||
|
@ -792,7 +786,7 @@ return array(
|
||||||
'phabricator-countdown-css' => '16c52f5c',
|
'phabricator-countdown-css' => '16c52f5c',
|
||||||
'phabricator-dashboard-css' => 'bc6f2127',
|
'phabricator-dashboard-css' => 'bc6f2127',
|
||||||
'phabricator-drag-and-drop-file-upload' => '58dea2fa',
|
'phabricator-drag-and-drop-file-upload' => '58dea2fa',
|
||||||
'phabricator-draggable-list' => '5a13c79f',
|
'phabricator-draggable-list' => 'bea6e7f4',
|
||||||
'phabricator-fatal-config-template-css' => '8f18fa41',
|
'phabricator-fatal-config-template-css' => '8f18fa41',
|
||||||
'phabricator-favicon' => '1fe2510c',
|
'phabricator-favicon' => '1fe2510c',
|
||||||
'phabricator-feed-css' => 'ecd4ec57',
|
'phabricator-feed-css' => 'ecd4ec57',
|
||||||
|
@ -830,7 +824,7 @@ return array(
|
||||||
'phabricator-uiexample-reactor-sendclass' => '1def2711',
|
'phabricator-uiexample-reactor-sendclass' => '1def2711',
|
||||||
'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
|
'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
|
||||||
'phabricator-zindex-css' => 'd1270942',
|
'phabricator-zindex-css' => 'd1270942',
|
||||||
'phame-css' => '8efb0729',
|
'phame-css' => '654dd9ef',
|
||||||
'pholio-css' => 'ca89d380',
|
'pholio-css' => 'ca89d380',
|
||||||
'pholio-edit-css' => '07676f51',
|
'pholio-edit-css' => '07676f51',
|
||||||
'pholio-inline-comments-css' => '8e545e49',
|
'pholio-inline-comments-css' => '8e545e49',
|
||||||
|
@ -853,16 +847,17 @@ return array(
|
||||||
'phui-chart-css' => '6bf6f78e',
|
'phui-chart-css' => '6bf6f78e',
|
||||||
'phui-cms-css' => 'be43c8a8',
|
'phui-cms-css' => 'be43c8a8',
|
||||||
'phui-comment-form-css' => '4ecc56ef',
|
'phui-comment-form-css' => '4ecc56ef',
|
||||||
|
'phui-comment-panel-css' => '85113e6a',
|
||||||
'phui-crumbs-view-css' => '195ac419',
|
'phui-crumbs-view-css' => '195ac419',
|
||||||
'phui-curtain-view-css' => '947bf1a4',
|
'phui-curtain-view-css' => '947bf1a4',
|
||||||
'phui-document-summary-view-css' => '9ca48bdf',
|
'phui-document-summary-view-css' => '9ca48bdf',
|
||||||
'phui-document-view-css' => 'c32e8dec',
|
'phui-document-view-css' => 'c32e8dec',
|
||||||
'phui-document-view-pro-css' => 'ca1fed81',
|
'phui-document-view-pro-css' => 'c354e312',
|
||||||
'phui-feed-story-css' => '44a9c8e9',
|
'phui-feed-story-css' => '44a9c8e9',
|
||||||
'phui-font-icon-base-css' => '870a7360',
|
'phui-font-icon-base-css' => '870a7360',
|
||||||
'phui-fontkit-css' => '9cda225e',
|
'phui-fontkit-css' => '9cda225e',
|
||||||
'phui-form-css' => 'b8fb087a',
|
'phui-form-css' => 'b8fb087a',
|
||||||
'phui-form-view-css' => '91adabe4',
|
'phui-form-view-css' => '3fadd537',
|
||||||
'phui-head-thing-view-css' => 'fd311e5f',
|
'phui-head-thing-view-css' => 'fd311e5f',
|
||||||
'phui-header-view-css' => '6ec8f155',
|
'phui-header-view-css' => '6ec8f155',
|
||||||
'phui-hovercard' => '1bd28176',
|
'phui-hovercard' => '1bd28176',
|
||||||
|
@ -871,9 +866,10 @@ return array(
|
||||||
'phui-icon-view-css' => '417f80fb',
|
'phui-icon-view-css' => '417f80fb',
|
||||||
'phui-image-mask-css' => 'a8498f9c',
|
'phui-image-mask-css' => 'a8498f9c',
|
||||||
'phui-info-panel-css' => '27ea50a1',
|
'phui-info-panel-css' => '27ea50a1',
|
||||||
'phui-info-view-css' => '28efab79',
|
'phui-info-view-css' => 'ec92802a',
|
||||||
'phui-inline-comment-view-css' => '5953c28e',
|
'phui-inline-comment-view-css' => '5953c28e',
|
||||||
'phui-invisible-character-view-css' => '6993d9f0',
|
'phui-invisible-character-view-css' => '6993d9f0',
|
||||||
|
'phui-lightbox-css' => 'e17ce2bd',
|
||||||
'phui-list-view-css' => '9da2aa00',
|
'phui-list-view-css' => '9da2aa00',
|
||||||
'phui-object-box-css' => '6b487c57',
|
'phui-object-box-css' => '6b487c57',
|
||||||
'phui-object-item-list-view-css' => '87278fa0',
|
'phui-object-item-list-view-css' => '87278fa0',
|
||||||
|
@ -889,8 +885,8 @@ return array(
|
||||||
'phui-theme-css' => '798c69b8',
|
'phui-theme-css' => '798c69b8',
|
||||||
'phui-timeline-view-css' => 'bc523970',
|
'phui-timeline-view-css' => 'bc523970',
|
||||||
'phui-two-column-view-css' => 'bbe32c23',
|
'phui-two-column-view-css' => 'bbe32c23',
|
||||||
'phui-workboard-color-css' => 'ac6fe6a7',
|
'phui-workboard-color-css' => '207828dd',
|
||||||
'phui-workboard-view-css' => 'e09eb53a',
|
'phui-workboard-view-css' => '60d09514',
|
||||||
'phui-workcard-view-css' => '0c62d7c5',
|
'phui-workcard-view-css' => '0c62d7c5',
|
||||||
'phui-workpanel-view-css' => '92197373',
|
'phui-workpanel-view-css' => '92197373',
|
||||||
'phuix-action-list-view' => 'b5c256b8',
|
'phuix-action-list-view' => 'b5c256b8',
|
||||||
|
@ -1043,6 +1039,9 @@ return array(
|
||||||
'javelin-workflow',
|
'javelin-workflow',
|
||||||
'javelin-workboard-controller',
|
'javelin-workboard-controller',
|
||||||
),
|
),
|
||||||
|
'185bbd53' => array(
|
||||||
|
'javelin-install',
|
||||||
|
),
|
||||||
'1aa4c968' => array(
|
'1aa4c968' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
|
@ -1353,13 +1352,6 @@ return array(
|
||||||
'phabricator-drag-and-drop-file-upload',
|
'phabricator-drag-and-drop-file-upload',
|
||||||
'javelin-workboard-board',
|
'javelin-workboard-board',
|
||||||
),
|
),
|
||||||
'568931f3' => array(
|
|
||||||
'javelin-behavior',
|
|
||||||
'javelin-util',
|
|
||||||
'javelin-dom',
|
|
||||||
'javelin-stratcom',
|
|
||||||
'javelin-vector',
|
|
||||||
),
|
|
||||||
'58dea2fa' => array(
|
'58dea2fa' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
|
@ -1379,14 +1371,6 @@ return array(
|
||||||
'javelin-vector',
|
'javelin-vector',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
),
|
),
|
||||||
'5a13c79f' => array(
|
|
||||||
'javelin-install',
|
|
||||||
'javelin-dom',
|
|
||||||
'javelin-stratcom',
|
|
||||||
'javelin-util',
|
|
||||||
'javelin-vector',
|
|
||||||
'javelin-magical-init',
|
|
||||||
),
|
|
||||||
'5c54cbf3' => array(
|
'5c54cbf3' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
|
@ -1597,6 +1581,9 @@ return array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
),
|
),
|
||||||
|
'85113e6a' => array(
|
||||||
|
'phui-timeline-view-css',
|
||||||
|
),
|
||||||
'85ee8ce6' => array(
|
'85ee8ce6' => array(
|
||||||
'aphront-dialog-view-css',
|
'aphront-dialog-view-css',
|
||||||
),
|
),
|
||||||
|
@ -1808,6 +1795,13 @@ return array(
|
||||||
'javelin-uri',
|
'javelin-uri',
|
||||||
'phabricator-keyboard-shortcut',
|
'phabricator-keyboard-shortcut',
|
||||||
),
|
),
|
||||||
|
'a9210d03' => array(
|
||||||
|
'javelin-behavior',
|
||||||
|
'javelin-util',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-stratcom',
|
||||||
|
'javelin-vector',
|
||||||
|
),
|
||||||
'a9f88de2' => array(
|
'a9f88de2' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
@ -1939,6 +1933,14 @@ return array(
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
'javelin-request',
|
'javelin-request',
|
||||||
),
|
),
|
||||||
|
'bea6e7f4' => array(
|
||||||
|
'javelin-install',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-stratcom',
|
||||||
|
'javelin-util',
|
||||||
|
'javelin-vector',
|
||||||
|
'javelin-magical-init',
|
||||||
|
),
|
||||||
'bee502c8' => array(
|
'bee502c8' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
|
@ -2118,9 +2120,6 @@ return array(
|
||||||
'javelin-workflow',
|
'javelin-workflow',
|
||||||
'javelin-magical-init',
|
'javelin-magical-init',
|
||||||
),
|
),
|
||||||
'e6e25838' => array(
|
|
||||||
'javelin-install',
|
|
||||||
),
|
|
||||||
'e9581f08' => array(
|
'e9581f08' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
|
@ -2128,6 +2127,15 @@ return array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'phabricator-draggable-list',
|
'phabricator-draggable-list',
|
||||||
),
|
),
|
||||||
|
'ec949017' => array(
|
||||||
|
'javelin-behavior',
|
||||||
|
'javelin-stratcom',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-mask',
|
||||||
|
'javelin-util',
|
||||||
|
'phuix-icon-view',
|
||||||
|
'phabricator-busy',
|
||||||
|
),
|
||||||
'edd1ba66' => array(
|
'edd1ba66' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
|
@ -2188,14 +2196,6 @@ return array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
),
|
),
|
||||||
'f8ba29d7' => array(
|
|
||||||
'javelin-behavior',
|
|
||||||
'javelin-stratcom',
|
|
||||||
'javelin-dom',
|
|
||||||
'javelin-mask',
|
|
||||||
'javelin-util',
|
|
||||||
'phabricator-busy',
|
|
||||||
),
|
|
||||||
'fb20ac8d' => array(
|
'fb20ac8d' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-aphlict',
|
'javelin-aphlict',
|
||||||
|
@ -2288,7 +2288,8 @@ return array(
|
||||||
'phabricator-main-menu-view',
|
'phabricator-main-menu-view',
|
||||||
'phabricator-notification-css',
|
'phabricator-notification-css',
|
||||||
'phabricator-notification-menu-css',
|
'phabricator-notification-menu-css',
|
||||||
'lightbox-attachment-css',
|
'phui-lightbox-css',
|
||||||
|
'phui-comment-panel-css',
|
||||||
'phui-header-view-css',
|
'phui-header-view-css',
|
||||||
'phabricator-nav-view-css',
|
'phabricator-nav-view-css',
|
||||||
'phui-basic-nav-view-css',
|
'phui-basic-nav-view-css',
|
||||||
|
|
|
@ -111,7 +111,8 @@ return array(
|
||||||
'phabricator-main-menu-view',
|
'phabricator-main-menu-view',
|
||||||
'phabricator-notification-css',
|
'phabricator-notification-css',
|
||||||
'phabricator-notification-menu-css',
|
'phabricator-notification-menu-css',
|
||||||
'lightbox-attachment-css',
|
'phui-lightbox-css',
|
||||||
|
'phui-comment-panel-css',
|
||||||
'phui-header-view-css',
|
'phui-header-view-css',
|
||||||
'phabricator-nav-view-css',
|
'phabricator-nav-view-css',
|
||||||
'phui-basic-nav-view-css',
|
'phui-basic-nav-view-css',
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_phame.phame_post
|
||||||
|
ADD subtitle VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT};
|
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_phame.phame_post
|
||||||
|
ADD headerImagePHID VARBINARY(64);
|
|
@ -34,7 +34,13 @@ try {
|
||||||
'name' => 'host',
|
'name' => 'host',
|
||||||
'param' => 'hostname',
|
'param' => 'hostname',
|
||||||
'help' => pht(
|
'help' => pht(
|
||||||
'Connect to __host__ instead of the default host.'),
|
'Operate on the database server identified by __hostname__.'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'ref',
|
||||||
|
'param' => 'ref',
|
||||||
|
'help' => pht(
|
||||||
|
'Operate on the database identified by __ref__.'),
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'name' => 'user',
|
'name' => 'user',
|
||||||
|
@ -81,118 +87,147 @@ try {
|
||||||
// First, test that the Phabricator configuration is set up correctly. After
|
// First, test that the Phabricator configuration is set up correctly. After
|
||||||
// we know this works we'll test any administrative credentials specifically.
|
// we know this works we'll test any administrative credentials specifically.
|
||||||
|
|
||||||
|
$refs = PhabricatorDatabaseRef::getActiveDatabaseRefs();
|
||||||
|
if (!$refs) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht('No databases are configured.'));
|
||||||
|
}
|
||||||
|
|
||||||
$host = $args->getArg('host');
|
$host = $args->getArg('host');
|
||||||
if (strlen($host)) {
|
$ref_key = $args->getArg('ref');
|
||||||
$ref = null;
|
if (strlen($host) || strlen($ref_key)) {
|
||||||
|
if ($host && $ref_key) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Use "--host" or "--ref" to select a database, but not both.'));
|
||||||
|
}
|
||||||
|
|
||||||
$refs = PhabricatorDatabaseRef::getLiveRefs();
|
$refs = PhabricatorDatabaseRef::getActiveDatabaseRefs();
|
||||||
|
|
||||||
// Include the master in case the user is just specifying a redundant
|
|
||||||
// "--host" flag for no reason and does not actually have a database
|
|
||||||
// cluster configured.
|
|
||||||
$refs[] = PhabricatorDatabaseRef::getMasterDatabaseRef();
|
|
||||||
|
|
||||||
|
$possible_refs = array();
|
||||||
foreach ($refs as $possible_ref) {
|
foreach ($refs as $possible_ref) {
|
||||||
if ($possible_ref->getHost() == $host) {
|
if ($host && ($possible_ref->getHost() == $host)) {
|
||||||
$ref = $possible_ref;
|
$possible_refs[] = $possible_ref;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ($ref_key && ($possible_ref->getRefKey() == $ref_key)) {
|
||||||
|
$possible_refs[] = $possible_ref;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$ref) {
|
if (!$possible_refs) {
|
||||||
|
if ($host) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'There is no configured database on host "%s". This command can '.
|
||||||
|
'only interact with configured databases.',
|
||||||
|
$host));
|
||||||
|
} else {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'There is no configured database with ref "%s". This command can '.
|
||||||
|
'only interact with configured databases.',
|
||||||
|
$ref_key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($possible_refs) > 1) {
|
||||||
throw new PhutilArgumentUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
pht(
|
pht(
|
||||||
'There is no configured database on host "%s". This command can '.
|
'Host "%s" identifies more than one database. Use "--ref" to select '.
|
||||||
'only interact with configured databases.',
|
'a specific database.',
|
||||||
$host));
|
$host));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
$ref = PhabricatorDatabaseRef::getMasterDatabaseRef();
|
$refs = $possible_refs;
|
||||||
if (!$ref) {
|
}
|
||||||
throw new Exception(
|
|
||||||
pht('No database master is configured.'));
|
$apis = array();
|
||||||
|
foreach ($refs as $ref) {
|
||||||
|
$default_user = $ref->getUser();
|
||||||
|
$default_host = $ref->getHost();
|
||||||
|
$default_port = $ref->getPort();
|
||||||
|
|
||||||
|
$test_api = id(new PhabricatorStorageManagementAPI())
|
||||||
|
->setUser($default_user)
|
||||||
|
->setHost($default_host)
|
||||||
|
->setPort($default_port)
|
||||||
|
->setPassword($ref->getPass())
|
||||||
|
->setNamespace($args->getArg('namespace'));
|
||||||
|
|
||||||
|
try {
|
||||||
|
queryfx(
|
||||||
|
$test_api->getConn(null),
|
||||||
|
'SELECT 1');
|
||||||
|
} catch (AphrontQueryException $ex) {
|
||||||
|
$message = phutil_console_format(
|
||||||
|
"**%s**\n\n%s\n\n%s\n\n%s\n\n**%s**: %s\n",
|
||||||
|
pht('MySQL Credentials Not Configured'),
|
||||||
|
pht(
|
||||||
|
'Unable to connect to MySQL using the configured credentials. '.
|
||||||
|
'You must configure standard credentials before you can upgrade '.
|
||||||
|
'storage. Run these commands to set up credentials:'),
|
||||||
|
" phabricator/ $ ./bin/config set mysql.host __host__\n".
|
||||||
|
" phabricator/ $ ./bin/config set mysql.user __username__\n".
|
||||||
|
" phabricator/ $ ./bin/config set mysql.pass __password__",
|
||||||
|
pht(
|
||||||
|
'These standard credentials are separate from any administrative '.
|
||||||
|
'credentials provided to this command with __%s__ or '.
|
||||||
|
'__%s__, and must be configured correctly before you can proceed.',
|
||||||
|
'--user',
|
||||||
|
'--password'),
|
||||||
|
pht('Raw MySQL Error'),
|
||||||
|
$ex->getMessage());
|
||||||
|
echo phutil_console_wrap($message);
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$default_user = $ref->getUser();
|
if ($args->getArg('password') === null) {
|
||||||
$default_host = $ref->getHost();
|
// This is already a PhutilOpaqueEnvelope.
|
||||||
$default_port = $ref->getPort();
|
$password = $ref->getPass();
|
||||||
|
} else {
|
||||||
|
// Put this in a PhutilOpaqueEnvelope.
|
||||||
|
$password = new PhutilOpaqueEnvelope($args->getArg('password'));
|
||||||
|
PhabricatorEnv::overrideConfig('mysql.pass', $args->getArg('password'));
|
||||||
|
}
|
||||||
|
|
||||||
$test_api = id(new PhabricatorStorageManagementAPI())
|
$selected_user = $args->getArg('user');
|
||||||
->setUser($default_user)
|
if ($selected_user === null) {
|
||||||
->setHost($default_host)
|
$selected_user = $default_user;
|
||||||
->setPort($default_port)
|
}
|
||||||
->setPassword($ref->getPass())
|
|
||||||
->setNamespace($args->getArg('namespace'));
|
|
||||||
|
|
||||||
try {
|
$api = id(new PhabricatorStorageManagementAPI())
|
||||||
queryfx(
|
->setUser($selected_user)
|
||||||
$test_api->getConn(null),
|
->setHost($default_host)
|
||||||
'SELECT 1');
|
->setPort($default_port)
|
||||||
} catch (AphrontQueryException $ex) {
|
->setPassword($password)
|
||||||
$message = phutil_console_format(
|
->setNamespace($args->getArg('namespace'))
|
||||||
"**%s**\n\n%s\n\n%s\n\n%s\n\n**%s**: %s\n",
|
->setDisableUTF8MB4($args->getArg('disable-utf8mb4'));
|
||||||
pht('MySQL Credentials Not Configured'),
|
PhabricatorEnv::overrideConfig('mysql.user', $api->getUser());
|
||||||
pht(
|
|
||||||
'Unable to connect to MySQL using the configured credentials. '.
|
|
||||||
'You must configure standard credentials before you can upgrade '.
|
|
||||||
'storage. Run these commands to set up credentials:'),
|
|
||||||
" phabricator/ $ ./bin/config set mysql.host __host__\n".
|
|
||||||
" phabricator/ $ ./bin/config set mysql.user __username__\n".
|
|
||||||
" phabricator/ $ ./bin/config set mysql.pass __password__",
|
|
||||||
pht(
|
|
||||||
'These standard credentials are separate from any administrative '.
|
|
||||||
'credentials provided to this command with __%s__ or '.
|
|
||||||
'__%s__, and must be configured correctly before you can proceed.',
|
|
||||||
'--user',
|
|
||||||
'--password'),
|
|
||||||
pht('Raw MySQL Error'),
|
|
||||||
$ex->getMessage());
|
|
||||||
echo phutil_console_wrap($message);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($args->getArg('password') === null) {
|
try {
|
||||||
// This is already a PhutilOpaqueEnvelope.
|
queryfx(
|
||||||
$password = $ref->getPass();
|
$api->getConn(null),
|
||||||
} else {
|
'SELECT 1');
|
||||||
// Put this in a PhutilOpaqueEnvelope.
|
} catch (AphrontQueryException $ex) {
|
||||||
$password = new PhutilOpaqueEnvelope($args->getArg('password'));
|
$message = phutil_console_format(
|
||||||
PhabricatorEnv::overrideConfig('mysql.pass', $args->getArg('password'));
|
"**%s**\n\n%s\n\n**%s**: %s\n",
|
||||||
}
|
pht('Bad Administrative Credentials'),
|
||||||
|
pht(
|
||||||
|
'Unable to connect to MySQL using the administrative credentials '.
|
||||||
|
'provided with the __%s__ and __%s__ flags. Check that '.
|
||||||
|
'you have entered them correctly.',
|
||||||
|
'--user',
|
||||||
|
'--password'),
|
||||||
|
pht('Raw MySQL Error'),
|
||||||
|
$ex->getMessage());
|
||||||
|
echo phutil_console_wrap($message);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
$selected_user = $args->getArg('user');
|
$api->setRef($ref);
|
||||||
if ($selected_user === null) {
|
$apis[] = $api;
|
||||||
$selected_user = $default_user;
|
|
||||||
}
|
|
||||||
|
|
||||||
$api = id(new PhabricatorStorageManagementAPI())
|
|
||||||
->setUser($selected_user)
|
|
||||||
->setHost($default_host)
|
|
||||||
->setPort($default_port)
|
|
||||||
->setPassword($password)
|
|
||||||
->setNamespace($args->getArg('namespace'))
|
|
||||||
->setDisableUTF8MB4($args->getArg('disable-utf8mb4'));
|
|
||||||
PhabricatorEnv::overrideConfig('mysql.user', $api->getUser());
|
|
||||||
|
|
||||||
try {
|
|
||||||
queryfx(
|
|
||||||
$api->getConn(null),
|
|
||||||
'SELECT 1');
|
|
||||||
} catch (AphrontQueryException $ex) {
|
|
||||||
$message = phutil_console_format(
|
|
||||||
"**%s**\n\n%s\n\n**%s**: %s\n",
|
|
||||||
pht('Bad Administrative Credentials'),
|
|
||||||
pht(
|
|
||||||
'Unable to connect to MySQL using the administrative credentials '.
|
|
||||||
'provided with the __%s__ and __%s__ flags. Check that '.
|
|
||||||
'you have entered them correctly.',
|
|
||||||
'--user',
|
|
||||||
'--password'),
|
|
||||||
pht('Raw MySQL Error'),
|
|
||||||
$ex->getMessage());
|
|
||||||
echo phutil_console_wrap($message);
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$workflows = id(new PhutilClassMapQuery())
|
$workflows = id(new PhutilClassMapQuery())
|
||||||
|
@ -202,7 +237,7 @@ $workflows = id(new PhutilClassMapQuery())
|
||||||
$patches = PhabricatorSQLPatchList::buildAllPatches();
|
$patches = PhabricatorSQLPatchList::buildAllPatches();
|
||||||
|
|
||||||
foreach ($workflows as $workflow) {
|
foreach ($workflows as $workflow) {
|
||||||
$workflow->setAPI($api);
|
$workflow->setAPIs($apis);
|
||||||
$workflow->setPatches($patches);
|
$workflow->setPatches($patches);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1917,6 +1917,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorAuthProviderConfigQuery' => 'applications/auth/query/PhabricatorAuthProviderConfigQuery.php',
|
'PhabricatorAuthProviderConfigQuery' => 'applications/auth/query/PhabricatorAuthProviderConfigQuery.php',
|
||||||
'PhabricatorAuthProviderConfigTransaction' => 'applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php',
|
'PhabricatorAuthProviderConfigTransaction' => 'applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php',
|
||||||
'PhabricatorAuthProviderConfigTransactionQuery' => 'applications/auth/query/PhabricatorAuthProviderConfigTransactionQuery.php',
|
'PhabricatorAuthProviderConfigTransactionQuery' => 'applications/auth/query/PhabricatorAuthProviderConfigTransactionQuery.php',
|
||||||
|
'PhabricatorAuthProvidersGuidanceContext' => 'applications/auth/guidance/PhabricatorAuthProvidersGuidanceContext.php',
|
||||||
|
'PhabricatorAuthProvidersGuidanceEngineExtension' => 'applications/auth/guidance/PhabricatorAuthProvidersGuidanceEngineExtension.php',
|
||||||
'PhabricatorAuthQueryPublicKeysConduitAPIMethod' => 'applications/auth/conduit/PhabricatorAuthQueryPublicKeysConduitAPIMethod.php',
|
'PhabricatorAuthQueryPublicKeysConduitAPIMethod' => 'applications/auth/conduit/PhabricatorAuthQueryPublicKeysConduitAPIMethod.php',
|
||||||
'PhabricatorAuthRegisterController' => 'applications/auth/controller/PhabricatorAuthRegisterController.php',
|
'PhabricatorAuthRegisterController' => 'applications/auth/controller/PhabricatorAuthRegisterController.php',
|
||||||
'PhabricatorAuthRevokeTokenController' => 'applications/auth/controller/PhabricatorAuthRevokeTokenController.php',
|
'PhabricatorAuthRevokeTokenController' => 'applications/auth/controller/PhabricatorAuthRevokeTokenController.php',
|
||||||
|
@ -1941,6 +1943,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorAuthSessionEngineExtension' => 'applications/auth/engine/PhabricatorAuthSessionEngineExtension.php',
|
'PhabricatorAuthSessionEngineExtension' => 'applications/auth/engine/PhabricatorAuthSessionEngineExtension.php',
|
||||||
'PhabricatorAuthSessionEngineExtensionModule' => 'applications/auth/engine/PhabricatorAuthSessionEngineExtensionModule.php',
|
'PhabricatorAuthSessionEngineExtensionModule' => 'applications/auth/engine/PhabricatorAuthSessionEngineExtensionModule.php',
|
||||||
'PhabricatorAuthSessionGarbageCollector' => 'applications/auth/garbagecollector/PhabricatorAuthSessionGarbageCollector.php',
|
'PhabricatorAuthSessionGarbageCollector' => 'applications/auth/garbagecollector/PhabricatorAuthSessionGarbageCollector.php',
|
||||||
|
'PhabricatorAuthSessionInfo' => 'applications/auth/data/PhabricatorAuthSessionInfo.php',
|
||||||
'PhabricatorAuthSessionQuery' => 'applications/auth/query/PhabricatorAuthSessionQuery.php',
|
'PhabricatorAuthSessionQuery' => 'applications/auth/query/PhabricatorAuthSessionQuery.php',
|
||||||
'PhabricatorAuthSetupCheck' => 'applications/config/check/PhabricatorAuthSetupCheck.php',
|
'PhabricatorAuthSetupCheck' => 'applications/config/check/PhabricatorAuthSetupCheck.php',
|
||||||
'PhabricatorAuthStartController' => 'applications/auth/controller/PhabricatorAuthStartController.php',
|
'PhabricatorAuthStartController' => 'applications/auth/controller/PhabricatorAuthStartController.php',
|
||||||
|
@ -2158,6 +2161,9 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorCalendarImportTriggerLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportTriggerLogType.php',
|
'PhabricatorCalendarImportTriggerLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportTriggerLogType.php',
|
||||||
'PhabricatorCalendarImportUpdateLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportUpdateLogType.php',
|
'PhabricatorCalendarImportUpdateLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportUpdateLogType.php',
|
||||||
'PhabricatorCalendarImportViewController' => 'applications/calendar/controller/PhabricatorCalendarImportViewController.php',
|
'PhabricatorCalendarImportViewController' => 'applications/calendar/controller/PhabricatorCalendarImportViewController.php',
|
||||||
|
'PhabricatorCalendarInviteeDatasource' => 'applications/calendar/typeahead/PhabricatorCalendarInviteeDatasource.php',
|
||||||
|
'PhabricatorCalendarInviteeUserDatasource' => 'applications/calendar/typeahead/PhabricatorCalendarInviteeUserDatasource.php',
|
||||||
|
'PhabricatorCalendarInviteeViewerFunctionDatasource' => 'applications/calendar/typeahead/PhabricatorCalendarInviteeViewerFunctionDatasource.php',
|
||||||
'PhabricatorCalendarManagementNotifyWorkflow' => 'applications/calendar/management/PhabricatorCalendarManagementNotifyWorkflow.php',
|
'PhabricatorCalendarManagementNotifyWorkflow' => 'applications/calendar/management/PhabricatorCalendarManagementNotifyWorkflow.php',
|
||||||
'PhabricatorCalendarManagementReloadWorkflow' => 'applications/calendar/management/PhabricatorCalendarManagementReloadWorkflow.php',
|
'PhabricatorCalendarManagementReloadWorkflow' => 'applications/calendar/management/PhabricatorCalendarManagementReloadWorkflow.php',
|
||||||
'PhabricatorCalendarManagementWorkflow' => 'applications/calendar/management/PhabricatorCalendarManagementWorkflow.php',
|
'PhabricatorCalendarManagementWorkflow' => 'applications/calendar/management/PhabricatorCalendarManagementWorkflow.php',
|
||||||
|
@ -2647,6 +2653,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorFileImageProxyController' => 'applications/files/controller/PhabricatorFileImageProxyController.php',
|
'PhabricatorFileImageProxyController' => 'applications/files/controller/PhabricatorFileImageProxyController.php',
|
||||||
'PhabricatorFileImageTransform' => 'applications/files/transform/PhabricatorFileImageTransform.php',
|
'PhabricatorFileImageTransform' => 'applications/files/transform/PhabricatorFileImageTransform.php',
|
||||||
'PhabricatorFileInfoController' => 'applications/files/controller/PhabricatorFileInfoController.php',
|
'PhabricatorFileInfoController' => 'applications/files/controller/PhabricatorFileInfoController.php',
|
||||||
|
'PhabricatorFileLightboxController' => 'applications/files/controller/PhabricatorFileLightboxController.php',
|
||||||
'PhabricatorFileLinkView' => 'view/layout/PhabricatorFileLinkView.php',
|
'PhabricatorFileLinkView' => 'view/layout/PhabricatorFileLinkView.php',
|
||||||
'PhabricatorFileListController' => 'applications/files/controller/PhabricatorFileListController.php',
|
'PhabricatorFileListController' => 'applications/files/controller/PhabricatorFileListController.php',
|
||||||
'PhabricatorFileQuery' => 'applications/files/query/PhabricatorFileQuery.php',
|
'PhabricatorFileQuery' => 'applications/files/query/PhabricatorFileQuery.php',
|
||||||
|
@ -2728,6 +2735,10 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorGlobalLock' => 'infrastructure/util/PhabricatorGlobalLock.php',
|
'PhabricatorGlobalLock' => 'infrastructure/util/PhabricatorGlobalLock.php',
|
||||||
'PhabricatorGlobalUploadTargetView' => 'applications/files/view/PhabricatorGlobalUploadTargetView.php',
|
'PhabricatorGlobalUploadTargetView' => 'applications/files/view/PhabricatorGlobalUploadTargetView.php',
|
||||||
'PhabricatorGoogleAuthProvider' => 'applications/auth/provider/PhabricatorGoogleAuthProvider.php',
|
'PhabricatorGoogleAuthProvider' => 'applications/auth/provider/PhabricatorGoogleAuthProvider.php',
|
||||||
|
'PhabricatorGuidanceContext' => 'applications/guides/guidance/PhabricatorGuidanceContext.php',
|
||||||
|
'PhabricatorGuidanceEngine' => 'applications/guides/guidance/PhabricatorGuidanceEngine.php',
|
||||||
|
'PhabricatorGuidanceEngineExtension' => 'applications/guides/guidance/PhabricatorGuidanceEngineExtension.php',
|
||||||
|
'PhabricatorGuidanceMessage' => 'applications/guides/guidance/PhabricatorGuidanceMessage.php',
|
||||||
'PhabricatorGuideApplication' => 'applications/guides/application/PhabricatorGuideApplication.php',
|
'PhabricatorGuideApplication' => 'applications/guides/application/PhabricatorGuideApplication.php',
|
||||||
'PhabricatorGuideController' => 'applications/guides/controller/PhabricatorGuideController.php',
|
'PhabricatorGuideController' => 'applications/guides/controller/PhabricatorGuideController.php',
|
||||||
'PhabricatorGuideInstallModule' => 'applications/guides/module/PhabricatorGuideInstallModule.php',
|
'PhabricatorGuideInstallModule' => 'applications/guides/module/PhabricatorGuideInstallModule.php',
|
||||||
|
@ -2887,6 +2898,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorManiphestTaskTestDataGenerator' => 'applications/maniphest/lipsum/PhabricatorManiphestTaskTestDataGenerator.php',
|
'PhabricatorManiphestTaskTestDataGenerator' => 'applications/maniphest/lipsum/PhabricatorManiphestTaskTestDataGenerator.php',
|
||||||
'PhabricatorMarkupCache' => 'applications/cache/storage/PhabricatorMarkupCache.php',
|
'PhabricatorMarkupCache' => 'applications/cache/storage/PhabricatorMarkupCache.php',
|
||||||
'PhabricatorMarkupEngine' => 'infrastructure/markup/PhabricatorMarkupEngine.php',
|
'PhabricatorMarkupEngine' => 'infrastructure/markup/PhabricatorMarkupEngine.php',
|
||||||
|
'PhabricatorMarkupEngineTestCase' => 'infrastructure/markup/__tests__/PhabricatorMarkupEngineTestCase.php',
|
||||||
'PhabricatorMarkupInterface' => 'infrastructure/markup/PhabricatorMarkupInterface.php',
|
'PhabricatorMarkupInterface' => 'infrastructure/markup/PhabricatorMarkupInterface.php',
|
||||||
'PhabricatorMarkupOneOff' => 'infrastructure/markup/PhabricatorMarkupOneOff.php',
|
'PhabricatorMarkupOneOff' => 'infrastructure/markup/PhabricatorMarkupOneOff.php',
|
||||||
'PhabricatorMarkupPreviewController' => 'infrastructure/markup/PhabricatorMarkupPreviewController.php',
|
'PhabricatorMarkupPreviewController' => 'infrastructure/markup/PhabricatorMarkupPreviewController.php',
|
||||||
|
@ -3218,6 +3230,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPeopleApproveController' => 'applications/people/controller/PhabricatorPeopleApproveController.php',
|
'PhabricatorPeopleApproveController' => 'applications/people/controller/PhabricatorPeopleApproveController.php',
|
||||||
'PhabricatorPeopleController' => 'applications/people/controller/PhabricatorPeopleController.php',
|
'PhabricatorPeopleController' => 'applications/people/controller/PhabricatorPeopleController.php',
|
||||||
'PhabricatorPeopleCreateController' => 'applications/people/controller/PhabricatorPeopleCreateController.php',
|
'PhabricatorPeopleCreateController' => 'applications/people/controller/PhabricatorPeopleCreateController.php',
|
||||||
|
'PhabricatorPeopleCreateGuidanceContext' => 'applications/people/guidance/PhabricatorPeopleCreateGuidanceContext.php',
|
||||||
'PhabricatorPeopleDatasource' => 'applications/people/typeahead/PhabricatorPeopleDatasource.php',
|
'PhabricatorPeopleDatasource' => 'applications/people/typeahead/PhabricatorPeopleDatasource.php',
|
||||||
'PhabricatorPeopleDeleteController' => 'applications/people/controller/PhabricatorPeopleDeleteController.php',
|
'PhabricatorPeopleDeleteController' => 'applications/people/controller/PhabricatorPeopleDeleteController.php',
|
||||||
'PhabricatorPeopleDetailsProfilePanel' => 'applications/people/profilepanel/PhabricatorPeopleDetailsProfilePanel.php',
|
'PhabricatorPeopleDetailsProfilePanel' => 'applications/people/profilepanel/PhabricatorPeopleDetailsProfilePanel.php',
|
||||||
|
@ -4075,6 +4088,7 @@ phutil_register_library_map(array(
|
||||||
'PhamePostEditEngine' => 'applications/phame/editor/PhamePostEditEngine.php',
|
'PhamePostEditEngine' => 'applications/phame/editor/PhamePostEditEngine.php',
|
||||||
'PhamePostEditor' => 'applications/phame/editor/PhamePostEditor.php',
|
'PhamePostEditor' => 'applications/phame/editor/PhamePostEditor.php',
|
||||||
'PhamePostFulltextEngine' => 'applications/phame/search/PhamePostFulltextEngine.php',
|
'PhamePostFulltextEngine' => 'applications/phame/search/PhamePostFulltextEngine.php',
|
||||||
|
'PhamePostHeaderPictureController' => 'applications/phame/controller/post/PhamePostHeaderPictureController.php',
|
||||||
'PhamePostHistoryController' => 'applications/phame/controller/post/PhamePostHistoryController.php',
|
'PhamePostHistoryController' => 'applications/phame/controller/post/PhamePostHistoryController.php',
|
||||||
'PhamePostListController' => 'applications/phame/controller/post/PhamePostListController.php',
|
'PhamePostListController' => 'applications/phame/controller/post/PhamePostListController.php',
|
||||||
'PhamePostListView' => 'applications/phame/view/PhamePostListView.php',
|
'PhamePostListView' => 'applications/phame/view/PhamePostListView.php',
|
||||||
|
@ -6436,7 +6450,7 @@ phutil_register_library_map(array(
|
||||||
'PHUIInfoExample' => 'PhabricatorUIExample',
|
'PHUIInfoExample' => 'PhabricatorUIExample',
|
||||||
'PHUIInfoPanelExample' => 'PhabricatorUIExample',
|
'PHUIInfoPanelExample' => 'PhabricatorUIExample',
|
||||||
'PHUIInfoPanelView' => 'AphrontView',
|
'PHUIInfoPanelView' => 'AphrontView',
|
||||||
'PHUIInfoView' => 'AphrontView',
|
'PHUIInfoView' => 'AphrontTagView',
|
||||||
'PHUIInvisibleCharacterTestCase' => 'PhabricatorTestCase',
|
'PHUIInvisibleCharacterTestCase' => 'PhabricatorTestCase',
|
||||||
'PHUIInvisibleCharacterView' => 'AphrontView',
|
'PHUIInvisibleCharacterView' => 'AphrontView',
|
||||||
'PHUIListExample' => 'PhabricatorUIExample',
|
'PHUIListExample' => 'PhabricatorUIExample',
|
||||||
|
@ -6730,6 +6744,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorAuthProviderConfigQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'PhabricatorAuthProviderConfigQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
'PhabricatorAuthProviderConfigTransaction' => 'PhabricatorApplicationTransaction',
|
'PhabricatorAuthProviderConfigTransaction' => 'PhabricatorApplicationTransaction',
|
||||||
'PhabricatorAuthProviderConfigTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
'PhabricatorAuthProviderConfigTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||||
|
'PhabricatorAuthProvidersGuidanceContext' => 'PhabricatorGuidanceContext',
|
||||||
|
'PhabricatorAuthProvidersGuidanceEngineExtension' => 'PhabricatorGuidanceEngineExtension',
|
||||||
'PhabricatorAuthQueryPublicKeysConduitAPIMethod' => 'PhabricatorAuthConduitAPIMethod',
|
'PhabricatorAuthQueryPublicKeysConduitAPIMethod' => 'PhabricatorAuthConduitAPIMethod',
|
||||||
'PhabricatorAuthRegisterController' => 'PhabricatorAuthController',
|
'PhabricatorAuthRegisterController' => 'PhabricatorAuthController',
|
||||||
'PhabricatorAuthRevokeTokenController' => 'PhabricatorAuthController',
|
'PhabricatorAuthRevokeTokenController' => 'PhabricatorAuthController',
|
||||||
|
@ -6762,6 +6778,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorAuthSessionEngineExtension' => 'Phobject',
|
'PhabricatorAuthSessionEngineExtension' => 'Phobject',
|
||||||
'PhabricatorAuthSessionEngineExtensionModule' => 'PhabricatorConfigModule',
|
'PhabricatorAuthSessionEngineExtensionModule' => 'PhabricatorConfigModule',
|
||||||
'PhabricatorAuthSessionGarbageCollector' => 'PhabricatorGarbageCollector',
|
'PhabricatorAuthSessionGarbageCollector' => 'PhabricatorGarbageCollector',
|
||||||
|
'PhabricatorAuthSessionInfo' => 'Phobject',
|
||||||
'PhabricatorAuthSessionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'PhabricatorAuthSessionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
'PhabricatorAuthSetupCheck' => 'PhabricatorSetupCheck',
|
'PhabricatorAuthSetupCheck' => 'PhabricatorSetupCheck',
|
||||||
'PhabricatorAuthStartController' => 'PhabricatorAuthController',
|
'PhabricatorAuthStartController' => 'PhabricatorAuthController',
|
||||||
|
@ -7032,6 +7049,9 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorCalendarImportTriggerLogType' => 'PhabricatorCalendarImportLogType',
|
'PhabricatorCalendarImportTriggerLogType' => 'PhabricatorCalendarImportLogType',
|
||||||
'PhabricatorCalendarImportUpdateLogType' => 'PhabricatorCalendarImportLogType',
|
'PhabricatorCalendarImportUpdateLogType' => 'PhabricatorCalendarImportLogType',
|
||||||
'PhabricatorCalendarImportViewController' => 'PhabricatorCalendarController',
|
'PhabricatorCalendarImportViewController' => 'PhabricatorCalendarController',
|
||||||
|
'PhabricatorCalendarInviteeDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||||
|
'PhabricatorCalendarInviteeUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||||
|
'PhabricatorCalendarInviteeViewerFunctionDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||||
'PhabricatorCalendarManagementNotifyWorkflow' => 'PhabricatorCalendarManagementWorkflow',
|
'PhabricatorCalendarManagementNotifyWorkflow' => 'PhabricatorCalendarManagementWorkflow',
|
||||||
'PhabricatorCalendarManagementReloadWorkflow' => 'PhabricatorCalendarManagementWorkflow',
|
'PhabricatorCalendarManagementReloadWorkflow' => 'PhabricatorCalendarManagementWorkflow',
|
||||||
'PhabricatorCalendarManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
'PhabricatorCalendarManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||||
|
@ -7604,6 +7624,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorFileImageProxyController' => 'PhabricatorFileController',
|
'PhabricatorFileImageProxyController' => 'PhabricatorFileController',
|
||||||
'PhabricatorFileImageTransform' => 'PhabricatorFileTransform',
|
'PhabricatorFileImageTransform' => 'PhabricatorFileTransform',
|
||||||
'PhabricatorFileInfoController' => 'PhabricatorFileController',
|
'PhabricatorFileInfoController' => 'PhabricatorFileController',
|
||||||
|
'PhabricatorFileLightboxController' => 'PhabricatorFileController',
|
||||||
'PhabricatorFileLinkView' => 'AphrontView',
|
'PhabricatorFileLinkView' => 'AphrontView',
|
||||||
'PhabricatorFileListController' => 'PhabricatorFileController',
|
'PhabricatorFileListController' => 'PhabricatorFileController',
|
||||||
'PhabricatorFileQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'PhabricatorFileQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
|
@ -7687,6 +7708,10 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorGlobalLock' => 'PhutilLock',
|
'PhabricatorGlobalLock' => 'PhutilLock',
|
||||||
'PhabricatorGlobalUploadTargetView' => 'AphrontView',
|
'PhabricatorGlobalUploadTargetView' => 'AphrontView',
|
||||||
'PhabricatorGoogleAuthProvider' => 'PhabricatorOAuth2AuthProvider',
|
'PhabricatorGoogleAuthProvider' => 'PhabricatorOAuth2AuthProvider',
|
||||||
|
'PhabricatorGuidanceContext' => 'Phobject',
|
||||||
|
'PhabricatorGuidanceEngine' => 'Phobject',
|
||||||
|
'PhabricatorGuidanceEngineExtension' => 'Phobject',
|
||||||
|
'PhabricatorGuidanceMessage' => 'Phobject',
|
||||||
'PhabricatorGuideApplication' => 'PhabricatorApplication',
|
'PhabricatorGuideApplication' => 'PhabricatorApplication',
|
||||||
'PhabricatorGuideController' => 'PhabricatorController',
|
'PhabricatorGuideController' => 'PhabricatorController',
|
||||||
'PhabricatorGuideInstallModule' => 'PhabricatorGuideModule',
|
'PhabricatorGuideInstallModule' => 'PhabricatorGuideModule',
|
||||||
|
@ -7851,6 +7876,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorManiphestTaskTestDataGenerator' => 'PhabricatorTestDataGenerator',
|
'PhabricatorManiphestTaskTestDataGenerator' => 'PhabricatorTestDataGenerator',
|
||||||
'PhabricatorMarkupCache' => 'PhabricatorCacheDAO',
|
'PhabricatorMarkupCache' => 'PhabricatorCacheDAO',
|
||||||
'PhabricatorMarkupEngine' => 'Phobject',
|
'PhabricatorMarkupEngine' => 'Phobject',
|
||||||
|
'PhabricatorMarkupEngineTestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorMarkupOneOff' => array(
|
'PhabricatorMarkupOneOff' => array(
|
||||||
'Phobject',
|
'Phobject',
|
||||||
'PhabricatorMarkupInterface',
|
'PhabricatorMarkupInterface',
|
||||||
|
@ -8256,6 +8282,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPeopleApproveController' => 'PhabricatorPeopleController',
|
'PhabricatorPeopleApproveController' => 'PhabricatorPeopleController',
|
||||||
'PhabricatorPeopleController' => 'PhabricatorController',
|
'PhabricatorPeopleController' => 'PhabricatorController',
|
||||||
'PhabricatorPeopleCreateController' => 'PhabricatorPeopleController',
|
'PhabricatorPeopleCreateController' => 'PhabricatorPeopleController',
|
||||||
|
'PhabricatorPeopleCreateGuidanceContext' => 'PhabricatorGuidanceContext',
|
||||||
'PhabricatorPeopleDatasource' => 'PhabricatorTypeaheadDatasource',
|
'PhabricatorPeopleDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||||
'PhabricatorPeopleDeleteController' => 'PhabricatorPeopleController',
|
'PhabricatorPeopleDeleteController' => 'PhabricatorPeopleController',
|
||||||
'PhabricatorPeopleDetailsProfilePanel' => 'PhabricatorProfilePanel',
|
'PhabricatorPeopleDetailsProfilePanel' => 'PhabricatorProfilePanel',
|
||||||
|
@ -9304,6 +9331,7 @@ phutil_register_library_map(array(
|
||||||
'PhamePostEditEngine' => 'PhabricatorEditEngine',
|
'PhamePostEditEngine' => 'PhabricatorEditEngine',
|
||||||
'PhamePostEditor' => 'PhabricatorApplicationTransactionEditor',
|
'PhamePostEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||||
'PhamePostFulltextEngine' => 'PhabricatorFulltextEngine',
|
'PhamePostFulltextEngine' => 'PhabricatorFulltextEngine',
|
||||||
|
'PhamePostHeaderPictureController' => 'PhamePostController',
|
||||||
'PhamePostHistoryController' => 'PhamePostController',
|
'PhamePostHistoryController' => 'PhamePostController',
|
||||||
'PhamePostListController' => 'PhamePostController',
|
'PhamePostListController' => 'PhamePostController',
|
||||||
'PhamePostListView' => 'AphrontTagView',
|
'PhamePostListView' => 'AphrontTagView',
|
||||||
|
|
|
@ -94,58 +94,12 @@ final class PhabricatorAuthListController
|
||||||
$crumbs->addTextCrumb(pht('Auth Providers'));
|
$crumbs->addTextCrumb(pht('Auth Providers'));
|
||||||
$crumbs->setBorder(true);
|
$crumbs->setBorder(true);
|
||||||
|
|
||||||
$domains_key = 'auth.email-domains';
|
$guidance_context = new PhabricatorAuthProvidersGuidanceContext();
|
||||||
$domains_link = $this->renderConfigLink($domains_key);
|
|
||||||
$domains_value = PhabricatorEnv::getEnvConfig($domains_key);
|
|
||||||
|
|
||||||
$approval_key = 'auth.require-approval';
|
$guidance = id(new PhabricatorGuidanceEngine())
|
||||||
$approval_link = $this->renderConfigLink($approval_key);
|
->setViewer($viewer)
|
||||||
$approval_value = PhabricatorEnv::getEnvConfig($approval_key);
|
->setGuidanceContext($guidance_context)
|
||||||
|
->newInfoView();
|
||||||
$issues = array();
|
|
||||||
if ($domains_value) {
|
|
||||||
$issues[] = pht(
|
|
||||||
'Phabricator is configured with an email domain whitelist (in %s), so '.
|
|
||||||
'only users with a verified email address at one of these %s '.
|
|
||||||
'allowed domain(s) will be able to register an account: %s',
|
|
||||||
$domains_link,
|
|
||||||
phutil_count($domains_value),
|
|
||||||
phutil_tag('strong', array(), implode(', ', $domains_value)));
|
|
||||||
} else {
|
|
||||||
$issues[] = pht(
|
|
||||||
'Anyone who can browse to this Phabricator install will be able to '.
|
|
||||||
'register an account. To add email domain restrictions, configure '.
|
|
||||||
'%s.',
|
|
||||||
$domains_link);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($approval_value) {
|
|
||||||
$issues[] = pht(
|
|
||||||
'Administrative approvals are enabled (in %s), so all new users must '.
|
|
||||||
'have their accounts approved by an administrator.',
|
|
||||||
$approval_link);
|
|
||||||
} else {
|
|
||||||
$issues[] = pht(
|
|
||||||
'Administrative approvals are disabled, so users who register will '.
|
|
||||||
'be able to use their accounts immediately. To enable approvals, '.
|
|
||||||
'configure %s.',
|
|
||||||
$approval_link);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$domains_value && !$approval_value) {
|
|
||||||
$severity = PHUIInfoView::SEVERITY_WARNING;
|
|
||||||
$issues[] = pht(
|
|
||||||
'You can safely ignore this warning if the install itself has '.
|
|
||||||
'access controls (for example, it is deployed on a VPN) or if all of '.
|
|
||||||
'the configured providers have access controls (for example, they are '.
|
|
||||||
'all private LDAP or OAuth servers).');
|
|
||||||
} else {
|
|
||||||
$severity = PHUIInfoView::SEVERITY_NOTICE;
|
|
||||||
}
|
|
||||||
|
|
||||||
$warning = id(new PHUIInfoView())
|
|
||||||
->setSeverity($severity)
|
|
||||||
->setErrors($issues);
|
|
||||||
|
|
||||||
$button = id(new PHUIButtonView())
|
$button = id(new PHUIButtonView())
|
||||||
->setTag('a')
|
->setTag('a')
|
||||||
|
@ -170,7 +124,7 @@ final class PhabricatorAuthListController
|
||||||
$view = id(new PHUITwoColumnView())
|
$view = id(new PHUITwoColumnView())
|
||||||
->setHeader($header)
|
->setHeader($header)
|
||||||
->setFooter(array(
|
->setFooter(array(
|
||||||
$warning,
|
$guidance,
|
||||||
$list,
|
$list,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -180,14 +134,4 @@ final class PhabricatorAuthListController
|
||||||
->appendChild($view);
|
->appendChild($view);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function renderConfigLink($key) {
|
|
||||||
return phutil_tag(
|
|
||||||
'a',
|
|
||||||
array(
|
|
||||||
'href' => '/config/edit/'.$key.'/',
|
|
||||||
'target' => '_blank',
|
|
||||||
),
|
|
||||||
$key);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
36
src/applications/auth/data/PhabricatorAuthSessionInfo.php
Normal file
36
src/applications/auth/data/PhabricatorAuthSessionInfo.php
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorAuthSessionInfo extends Phobject {
|
||||||
|
|
||||||
|
private $sessionType;
|
||||||
|
private $identityPHID;
|
||||||
|
private $isPartial;
|
||||||
|
|
||||||
|
public function setSessionType($session_type) {
|
||||||
|
$this->sessionType = $session_type;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSessionType() {
|
||||||
|
return $this->sessionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setIdentityPHID($identity_phid) {
|
||||||
|
$this->identityPHID = $identity_phid;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIdentityPHID() {
|
||||||
|
return $this->identityPHID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setIsPartial($is_partial) {
|
||||||
|
$this->isPartial = $is_partial;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIsPartial() {
|
||||||
|
return $this->isPartial;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -270,6 +270,16 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||||
$log->save();
|
$log->save();
|
||||||
unset($unguarded);
|
unset($unguarded);
|
||||||
|
|
||||||
|
$info = id(new PhabricatorAuthSessionInfo())
|
||||||
|
->setSessionType($session_type)
|
||||||
|
->setIdentityPHID($identity_phid)
|
||||||
|
->setIsPartial($partial);
|
||||||
|
|
||||||
|
$extensions = PhabricatorAuthSessionEngineExtension::getAllExtensions();
|
||||||
|
foreach ($extensions as $extension) {
|
||||||
|
$extension->didEstablishSession($info);
|
||||||
|
}
|
||||||
|
|
||||||
return $session_key;
|
return $session_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -837,6 +847,11 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||||
|
|
||||||
// Switch to the user's translation.
|
// Switch to the user's translation.
|
||||||
PhabricatorEnv::setLocaleCode($user->getTranslation());
|
PhabricatorEnv::setLocaleCode($user->getTranslation());
|
||||||
|
|
||||||
|
$extensions = PhabricatorAuthSessionEngineExtension::getAllExtensions();
|
||||||
|
foreach ($extensions as $extension) {
|
||||||
|
$extension->willServeRequestForUser($user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,14 @@ abstract class PhabricatorAuthSessionEngineExtension
|
||||||
|
|
||||||
abstract public function getExtensionName();
|
abstract public function getExtensionName();
|
||||||
|
|
||||||
|
public function didEstablishSession(PhabricatorAuthSessionInfo $info) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function willServeRequestForUser(PhabricatorUser $user) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
public function didLogout(PhabricatorUser $user, array $sessions) {
|
public function didLogout(PhabricatorUser $user, array $sessions) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorAuthProvidersGuidanceContext
|
||||||
|
extends PhabricatorGuidanceContext {}
|
|
@ -0,0 +1,88 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorAuthProvidersGuidanceEngineExtension
|
||||||
|
extends PhabricatorGuidanceEngineExtension {
|
||||||
|
|
||||||
|
const GUIDANCEKEY = 'core.auth.providers';
|
||||||
|
|
||||||
|
public function canGenerateGuidance(PhabricatorGuidanceContext $context) {
|
||||||
|
return ($context instanceof PhabricatorAuthProvidersGuidanceContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateGuidance(PhabricatorGuidanceContext $context) {
|
||||||
|
$domains_key = 'auth.email-domains';
|
||||||
|
$domains_link = $this->renderConfigLink($domains_key);
|
||||||
|
$domains_value = PhabricatorEnv::getEnvConfig($domains_key);
|
||||||
|
|
||||||
|
$approval_key = 'auth.require-approval';
|
||||||
|
$approval_link = $this->renderConfigLink($approval_key);
|
||||||
|
$approval_value = PhabricatorEnv::getEnvConfig($approval_key);
|
||||||
|
|
||||||
|
$results = array();
|
||||||
|
|
||||||
|
if ($domains_value) {
|
||||||
|
$message = pht(
|
||||||
|
'Phabricator is configured with an email domain whitelist (in %s), so '.
|
||||||
|
'only users with a verified email address at one of these %s '.
|
||||||
|
'allowed domain(s) will be able to register an account: %s',
|
||||||
|
$domains_link,
|
||||||
|
phutil_count($domains_value),
|
||||||
|
phutil_tag('strong', array(), implode(', ', $domains_value)));
|
||||||
|
|
||||||
|
$results[] = $this->newGuidance('core.auth.email-domains.on')
|
||||||
|
->setMessage($message);
|
||||||
|
} else {
|
||||||
|
$message = pht(
|
||||||
|
'Anyone who can browse to this Phabricator install will be able to '.
|
||||||
|
'register an account. To add email domain restrictions, configure '.
|
||||||
|
'%s.',
|
||||||
|
$domains_link);
|
||||||
|
|
||||||
|
$results[] = $this->newGuidance('core.auth.email-domains.off')
|
||||||
|
->setMessage($message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($approval_value) {
|
||||||
|
$message = pht(
|
||||||
|
'Administrative approvals are enabled (in %s), so all new users must '.
|
||||||
|
'have their accounts approved by an administrator.',
|
||||||
|
$approval_link);
|
||||||
|
|
||||||
|
$results[] = $this->newGuidance('core.auth.require-approval.on')
|
||||||
|
->setMessage($message);
|
||||||
|
} else {
|
||||||
|
$message = pht(
|
||||||
|
'Administrative approvals are disabled, so users who register will '.
|
||||||
|
'be able to use their accounts immediately. To enable approvals, '.
|
||||||
|
'configure %s.',
|
||||||
|
$approval_link);
|
||||||
|
|
||||||
|
$results[] = $this->newGuidance('core.auth.require-approval.off')
|
||||||
|
->setMessage($message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$domains_value && !$approval_value) {
|
||||||
|
$message = pht(
|
||||||
|
'You can safely ignore these warnings if the install itself has '.
|
||||||
|
'access controls (for example, it is deployed on a VPN) or if all of '.
|
||||||
|
'the configured providers have access controls (for example, they are '.
|
||||||
|
'all private LDAP or OAuth servers).');
|
||||||
|
|
||||||
|
$results[] = $this->newWarning('core.auth.warning')
|
||||||
|
->setMessage($message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function renderConfigLink($key) {
|
||||||
|
return phutil_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => '/config/edit/'.$key.'/',
|
||||||
|
'target' => '_blank',
|
||||||
|
),
|
||||||
|
$key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -309,16 +309,48 @@ final class PhabricatorCalendarEventViewController
|
||||||
$status_declined => 'red',
|
$status_declined => 'red',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$viewer_phid = $viewer->getPHID();
|
||||||
|
$is_rsvp_invited = $event->isRSVPInvited($viewer_phid);
|
||||||
|
$type_user = PhabricatorPeopleUserPHIDType::TYPECONST;
|
||||||
|
|
||||||
|
$head = array();
|
||||||
|
$tail = array();
|
||||||
foreach ($invitees as $invitee) {
|
foreach ($invitees as $invitee) {
|
||||||
$item = new PHUIStatusItemView();
|
$item = new PHUIStatusItemView();
|
||||||
$invitee_phid = $invitee->getInviteePHID();
|
$invitee_phid = $invitee->getInviteePHID();
|
||||||
$status = $invitee->getStatus();
|
$status = $invitee->getStatus();
|
||||||
$target = $viewer->renderHandle($invitee_phid);
|
$target = $viewer->renderHandle($invitee_phid);
|
||||||
$icon = $icon_map[$status];
|
$is_user = (phid_get_type($invitee_phid) == $type_user);
|
||||||
$icon_color = $icon_color_map[$status];
|
|
||||||
|
if (!$is_user) {
|
||||||
|
$icon = 'fa-users';
|
||||||
|
$icon_color = 'blue';
|
||||||
|
} else {
|
||||||
|
$icon = $icon_map[$status];
|
||||||
|
$icon_color = $icon_color_map[$status];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Highlight invited groups which you're a member of if you have
|
||||||
|
// not RSVP'd to an event yet.
|
||||||
|
if ($is_rsvp_invited) {
|
||||||
|
if ($invitee_phid != $viewer_phid) {
|
||||||
|
if ($event->hasRSVPAuthority($viewer_phid, $invitee_phid)) {
|
||||||
|
$item->setHighlighted(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$item->setIcon($icon, $icon_color)
|
$item->setIcon($icon, $icon_color)
|
||||||
->setTarget($target);
|
->setTarget($target);
|
||||||
|
|
||||||
|
if ($is_user) {
|
||||||
|
$tail[] = $item;
|
||||||
|
} else {
|
||||||
|
$head[] = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (array_merge($head, $tail) as $item) {
|
||||||
$invitee_list->addItem($item);
|
$invitee_list->addItem($item);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -511,6 +543,7 @@ final class PhabricatorCalendarEventViewController
|
||||||
$event = id(new PhabricatorCalendarEventQuery())
|
$event = id(new PhabricatorCalendarEventQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withIDs(array($id))
|
->withIDs(array($id))
|
||||||
|
->needRSVPs(array($viewer->getPHID()))
|
||||||
->executeOne();
|
->executeOne();
|
||||||
if (!$event) {
|
if (!$event) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -586,10 +619,8 @@ final class PhabricatorCalendarEventViewController
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
$id = $event->getID();
|
$id = $event->getID();
|
||||||
|
|
||||||
$invite_status = $event->getUserInviteStatus($viewer->getPHID());
|
$is_pending = $event->isRSVPInvited($viewer->getPHID());
|
||||||
$status_invited = PhabricatorCalendarEventInvitee::STATUS_INVITED;
|
if (!$is_pending) {
|
||||||
$is_invite_pending = ($invite_status == $status_invited);
|
|
||||||
if (!$is_invite_pending) {
|
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,9 @@ final class PhabricatorCalendarExportICSController
|
||||||
$saved->setParameter('rangeEnd', null);
|
$saved->setParameter('rangeEnd', null);
|
||||||
$saved->setParameter('upcoming', null);
|
$saved->setParameter('upcoming', null);
|
||||||
|
|
||||||
|
// The "month" and "day" display modes imply time ranges.
|
||||||
|
$saved->setParameter('display', 'list');
|
||||||
|
|
||||||
$query = $engine->buildQueryFromSavedQuery($saved);
|
$query = $engine->buildQueryFromSavedQuery($saved);
|
||||||
|
|
||||||
$events = $query
|
$events = $query
|
||||||
|
|
|
@ -108,6 +108,8 @@ final class PhabricatorCalendarEventEditEngine
|
||||||
->setConduitTypeDescription(pht('New event name.'))
|
->setConduitTypeDescription(pht('New event name.'))
|
||||||
->setValue($object->getName()),
|
->setValue($object->getName()),
|
||||||
id(new PhabricatorBoolEditField())
|
id(new PhabricatorBoolEditField())
|
||||||
|
->setIsLockable(false)
|
||||||
|
->setIsDefaultable(false)
|
||||||
->setKey('isAllDay')
|
->setKey('isAllDay')
|
||||||
->setOptions(pht('Normal Event'), pht('All Day Event'))
|
->setOptions(pht('Normal Event'), pht('All Day Event'))
|
||||||
->setAsCheckbox(true)
|
->setAsCheckbox(true)
|
||||||
|
@ -151,6 +153,8 @@ final class PhabricatorCalendarEventEditEngine
|
||||||
->setConduitTypeDescription(pht('True to cancel the event.'))
|
->setConduitTypeDescription(pht('True to cancel the event.'))
|
||||||
->setValue($object->getIsCancelled()),
|
->setValue($object->getIsCancelled()),
|
||||||
id(new PhabricatorUsersEditField())
|
id(new PhabricatorUsersEditField())
|
||||||
|
->setIsLockable(false)
|
||||||
|
->setIsDefaultable(false)
|
||||||
->setKey('hostPHID')
|
->setKey('hostPHID')
|
||||||
->setAliases(array('host'))
|
->setAliases(array('host'))
|
||||||
->setLabel(pht('Host'))
|
->setLabel(pht('Host'))
|
||||||
|
@ -162,6 +166,8 @@ final class PhabricatorCalendarEventEditEngine
|
||||||
->setConduitTypeDescription(pht('New event host.'))
|
->setConduitTypeDescription(pht('New event host.'))
|
||||||
->setSingleValue($object->getHostPHID()),
|
->setSingleValue($object->getHostPHID()),
|
||||||
id(new PhabricatorDatasourceEditField())
|
id(new PhabricatorDatasourceEditField())
|
||||||
|
->setIsLockable(false)
|
||||||
|
->setIsDefaultable(false)
|
||||||
->setIsHidden($is_future)
|
->setIsHidden($is_future)
|
||||||
->setKey('inviteePHIDs')
|
->setKey('inviteePHIDs')
|
||||||
->setAliases(array('invite', 'invitee', 'invitees', 'inviteePHID'))
|
->setAliases(array('invite', 'invitee', 'invitees', 'inviteePHID'))
|
||||||
|
@ -203,6 +209,8 @@ final class PhabricatorCalendarEventEditEngine
|
||||||
|
|
||||||
id(new PhabricatorBoolEditField())
|
id(new PhabricatorBoolEditField())
|
||||||
->setIsHidden(true)
|
->setIsHidden(true)
|
||||||
|
->setIsLockable(false)
|
||||||
|
->setIsDefaultable(false)
|
||||||
->setKey('isRecurring')
|
->setKey('isRecurring')
|
||||||
->setLabel(pht('Recurring'))
|
->setLabel(pht('Recurring'))
|
||||||
->setOptions(pht('One-Time Event'), pht('Recurring Event'))
|
->setOptions(pht('One-Time Event'), pht('Recurring Event'))
|
||||||
|
@ -213,6 +221,8 @@ final class PhabricatorCalendarEventEditEngine
|
||||||
->setConduitTypeDescription(pht('Mark the event as a recurring event.'))
|
->setConduitTypeDescription(pht('Mark the event as a recurring event.'))
|
||||||
->setValue(true),
|
->setValue(true),
|
||||||
id(new PhabricatorSelectEditField())
|
id(new PhabricatorSelectEditField())
|
||||||
|
->setIsLockable(false)
|
||||||
|
->setIsDefaultable(false)
|
||||||
->setKey('frequency')
|
->setKey('frequency')
|
||||||
->setLabel(pht('Frequency'))
|
->setLabel(pht('Frequency'))
|
||||||
->setOptions($frequency_options)
|
->setOptions($frequency_options)
|
||||||
|
|
|
@ -43,7 +43,7 @@ final class PhabricatorCalendarEventInviteesPolicyRule
|
||||||
if (!isset($this->sourcePHIDs[$viewer_phid])) {
|
if (!isset($this->sourcePHIDs[$viewer_phid])) {
|
||||||
$source_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
$source_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
||||||
$viewer_phid,
|
$viewer_phid,
|
||||||
PhabricatorProjectMemberOfProjectEdgeType::EDGECONST);
|
PhabricatorProjectMaterializedMemberEdgeType::EDGECONST);
|
||||||
$source_phids[] = $viewer_phid;
|
$source_phids[] = $viewer_phid;
|
||||||
$this->sourcePHIDs[$viewer_phid] = $source_phids;
|
$this->sourcePHIDs[$viewer_phid] = $source_phids;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ final class PhabricatorCalendarEventQuery
|
||||||
private $utcInitialEpochMin;
|
private $utcInitialEpochMin;
|
||||||
private $utcInitialEpochMax;
|
private $utcInitialEpochMax;
|
||||||
private $isImported;
|
private $isImported;
|
||||||
|
private $needRSVPs;
|
||||||
|
|
||||||
private $generateGhosts = false;
|
private $generateGhosts = false;
|
||||||
|
|
||||||
|
@ -109,6 +110,11 @@ final class PhabricatorCalendarEventQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function needRSVPs(array $phids) {
|
||||||
|
$this->needRSVPs = $phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
protected function getDefaultOrderVector() {
|
protected function getDefaultOrderVector() {
|
||||||
return array('start', 'id');
|
return array('start', 'id');
|
||||||
}
|
}
|
||||||
|
@ -166,7 +172,6 @@ final class PhabricatorCalendarEventQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
$raw_limit = $this->getRawResultLimit();
|
$raw_limit = $this->getRawResultLimit();
|
||||||
|
|
||||||
if (!$raw_limit && !$this->rangeEnd) {
|
if (!$raw_limit && !$this->rangeEnd) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht(
|
pht(
|
||||||
|
@ -614,6 +619,70 @@ final class PhabricatorCalendarEventQuery
|
||||||
|
|
||||||
$events = msort($events, 'getStartDateTimeEpoch');
|
$events = msort($events, 'getStartDateTimeEpoch');
|
||||||
|
|
||||||
|
if ($this->needRSVPs) {
|
||||||
|
$rsvp_phids = $this->needRSVPs;
|
||||||
|
$project_type = PhabricatorProjectProjectPHIDType::TYPECONST;
|
||||||
|
|
||||||
|
$project_phids = array();
|
||||||
|
foreach ($events as $event) {
|
||||||
|
foreach ($event->getInvitees() as $invitee) {
|
||||||
|
$invitee_phid = $invitee->getInviteePHID();
|
||||||
|
if (phid_get_type($invitee_phid) == $project_type) {
|
||||||
|
$project_phids[] = $invitee_phid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($project_phids) {
|
||||||
|
$member_type = PhabricatorProjectMaterializedMemberEdgeType::EDGECONST;
|
||||||
|
|
||||||
|
$query = id(new PhabricatorEdgeQuery())
|
||||||
|
->withSourcePHIDs($project_phids)
|
||||||
|
->withEdgeTypes(array($member_type))
|
||||||
|
->withDestinationPHIDs($rsvp_phids);
|
||||||
|
|
||||||
|
$edges = $query->execute();
|
||||||
|
|
||||||
|
$project_map = array();
|
||||||
|
foreach ($edges as $src => $types) {
|
||||||
|
foreach ($types as $type => $dsts) {
|
||||||
|
foreach ($dsts as $dst => $edge) {
|
||||||
|
$project_map[$dst][] = $src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$project_map = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$membership_map = array();
|
||||||
|
foreach ($rsvp_phids as $rsvp_phid) {
|
||||||
|
$membership_map[$rsvp_phid] = array();
|
||||||
|
$membership_map[$rsvp_phid][] = $rsvp_phid;
|
||||||
|
|
||||||
|
$project_phids = idx($project_map, $rsvp_phid);
|
||||||
|
if ($project_phids) {
|
||||||
|
foreach ($project_phids as $project_phid) {
|
||||||
|
$membership_map[$rsvp_phid][] = $project_phid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($events as $event) {
|
||||||
|
$invitees = $event->getInvitees();
|
||||||
|
$invitees = mpull($invitees, null, 'getInviteePHID');
|
||||||
|
|
||||||
|
$rsvp_map = array();
|
||||||
|
foreach ($rsvp_phids as $rsvp_phid) {
|
||||||
|
$membership_phids = $membership_map[$rsvp_phid];
|
||||||
|
$rsvps = array_select_keys($invitees, $membership_phids);
|
||||||
|
$rsvp_map[$rsvp_phid] = $rsvps;
|
||||||
|
}
|
||||||
|
|
||||||
|
$event->attachRSVPs($rsvp_map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $events;
|
return $events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,10 @@ final class PhabricatorCalendarEventSearchEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
public function newQuery() {
|
public function newQuery() {
|
||||||
return new PhabricatorCalendarEventQuery();
|
$viewer = $this->requireViewer();
|
||||||
|
|
||||||
|
return id(new PhabricatorCalendarEventQuery())
|
||||||
|
->needRSVPs(array($viewer->getPHID()));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function shouldShowOrderField() {
|
protected function shouldShowOrderField() {
|
||||||
|
@ -33,7 +36,7 @@ final class PhabricatorCalendarEventSearchEngine
|
||||||
id(new PhabricatorSearchDatasourceField())
|
id(new PhabricatorSearchDatasourceField())
|
||||||
->setLabel(pht('Invited'))
|
->setLabel(pht('Invited'))
|
||||||
->setKey('invitedPHIDs')
|
->setKey('invitedPHIDs')
|
||||||
->setDatasource(new PhabricatorPeopleUserFunctionDatasource()),
|
->setDatasource(new PhabricatorCalendarInviteeDatasource()),
|
||||||
id(new PhabricatorSearchDateControlField())
|
id(new PhabricatorSearchDateControlField())
|
||||||
->setLabel(pht('Occurs After'))
|
->setLabel(pht('Occurs After'))
|
||||||
->setKey('rangeStart'),
|
->setKey('rangeStart'),
|
||||||
|
@ -79,6 +82,18 @@ final class PhabricatorCalendarEventSearchEngine
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
|
||||||
|
$query = parent::buildQueryFromSavedQuery($saved);
|
||||||
|
|
||||||
|
// If this is an export query for generating an ".ics" file, don't
|
||||||
|
// build ghost events.
|
||||||
|
if ($saved->getParameter('export')) {
|
||||||
|
$query->setGenerateGhosts(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
protected function buildQueryFromParameters(array $map) {
|
protected function buildQueryFromParameters(array $map) {
|
||||||
$query = $this->newQuery();
|
$query = $this->newQuery();
|
||||||
$viewer = $this->requireViewer();
|
$viewer = $this->requireViewer();
|
||||||
|
@ -122,13 +137,7 @@ final class PhabricatorCalendarEventSearchEngine
|
||||||
$query->withImportSourcePHIDs($map['importSourcePHIDs']);
|
$query->withImportSourcePHIDs($map['importSourcePHIDs']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate ghosts (and ignore stub events) if we aren't querying for
|
if (!$map['ids'] && !$map['phids']) {
|
||||||
// specific events or exporting.
|
|
||||||
if (!empty($map['export'])) {
|
|
||||||
// This is a specific mode enabled by event exports.
|
|
||||||
$query
|
|
||||||
->withIsStub(false);
|
|
||||||
} else if (!$map['ids'] && !$map['phids']) {
|
|
||||||
$query
|
$query
|
||||||
->withIsStub(false)
|
->withIsStub(false)
|
||||||
->setGenerateGhosts(true);
|
->setGenerateGhosts(true);
|
||||||
|
@ -361,10 +370,14 @@ final class PhabricatorCalendarEventSearchEngine
|
||||||
|
|
||||||
$month_view->setUser($viewer);
|
$month_view->setUser($viewer);
|
||||||
|
|
||||||
|
$viewer_phid = $viewer->getPHID();
|
||||||
foreach ($events as $event) {
|
foreach ($events as $event) {
|
||||||
$epoch_min = $event->getStartDateTimeEpoch();
|
$epoch_min = $event->getStartDateTimeEpoch();
|
||||||
$epoch_max = $event->getEndDateTimeEpoch();
|
$epoch_max = $event->getEndDateTimeEpoch();
|
||||||
|
|
||||||
|
$is_invited = $event->isRSVPInvited($viewer_phid);
|
||||||
|
$is_attending = $event->getIsUserAttending($viewer_phid);
|
||||||
|
|
||||||
$event_view = id(new AphrontCalendarEventView())
|
$event_view = id(new AphrontCalendarEventView())
|
||||||
->setHostPHID($event->getHostPHID())
|
->setHostPHID($event->getHostPHID())
|
||||||
->setEpochRange($epoch_min, $epoch_max)
|
->setEpochRange($epoch_min, $epoch_max)
|
||||||
|
@ -373,6 +386,8 @@ final class PhabricatorCalendarEventSearchEngine
|
||||||
->setURI($event->getURI())
|
->setURI($event->getURI())
|
||||||
->setIsAllDay($event->getIsAllDay())
|
->setIsAllDay($event->getIsAllDay())
|
||||||
->setIcon($event->getDisplayIcon($viewer))
|
->setIcon($event->getDisplayIcon($viewer))
|
||||||
|
->setViewerIsInvited($is_invited || $is_attending)
|
||||||
|
->setDatetimeSummary($event->renderEventDate($viewer, true))
|
||||||
->setIconColor($event->getDisplayIconColor($viewer));
|
->setIconColor($event->getDisplayIconColor($viewer));
|
||||||
|
|
||||||
$month_view->addEvent($event_view);
|
$month_view->addEvent($event_view);
|
||||||
|
@ -441,6 +456,7 @@ final class PhabricatorCalendarEventSearchEngine
|
||||||
->setIconColor($status_color)
|
->setIconColor($status_color)
|
||||||
->setName($event->getName())
|
->setName($event->getName())
|
||||||
->setURI($event->getURI())
|
->setURI($event->getURI())
|
||||||
|
->setDatetimeSummary($event->renderEventDate($viewer, true))
|
||||||
->setIsCancelled($event->getIsCancelled());
|
->setIsCancelled($event->getIsCancelled());
|
||||||
|
|
||||||
$day_view->addEvent($event_view);
|
$day_view->addEvent($event_view);
|
||||||
|
|
|
@ -50,6 +50,7 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
||||||
private $parentEvent = self::ATTACHABLE;
|
private $parentEvent = self::ATTACHABLE;
|
||||||
private $invitees = self::ATTACHABLE;
|
private $invitees = self::ATTACHABLE;
|
||||||
private $importSource = self::ATTACHABLE;
|
private $importSource = self::ATTACHABLE;
|
||||||
|
private $rsvps = self::ATTACHABLE;
|
||||||
|
|
||||||
private $viewerTimezone;
|
private $viewerTimezone;
|
||||||
|
|
||||||
|
@ -537,16 +538,6 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
||||||
return $is_attending;
|
return $is_attending;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getIsUserInvited($phid) {
|
|
||||||
$uninvited_status = PhabricatorCalendarEventInvitee::STATUS_UNINVITED;
|
|
||||||
$declined_status = PhabricatorCalendarEventInvitee::STATUS_DECLINED;
|
|
||||||
$status = $this->getUserInviteStatus($phid);
|
|
||||||
if ($status == $uninvited_status || $status == $declined_status) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getIsGhostEvent() {
|
public function getIsGhostEvent() {
|
||||||
return $this->isGhostEvent;
|
return $this->isGhostEvent;
|
||||||
}
|
}
|
||||||
|
@ -590,13 +581,15 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
||||||
$start = $this->newStartDateTime();
|
$start = $this->newStartDateTime();
|
||||||
$end = $this->newEndDateTime();
|
$end = $this->newEndDateTime();
|
||||||
|
|
||||||
if ($show_end) {
|
$min_date = $start->newPHPDateTime();
|
||||||
$min_date = $start->newPHPDateTime();
|
$max_date = $end->newPHPDateTime();
|
||||||
$max_date = $end->newPHPDateTime();
|
|
||||||
|
|
||||||
|
if ($this->getIsAllDay()) {
|
||||||
// Subtract one second since the stored date is exclusive.
|
// Subtract one second since the stored date is exclusive.
|
||||||
$max_date = $max_date->modify('-1 second');
|
$max_date = $max_date->modify('-1 second');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($show_end) {
|
||||||
$min_day = $min_date->format('Y m d');
|
$min_day = $min_date->format('Y m d');
|
||||||
$max_day = $max_date->format('Y m d');
|
$max_day = $max_date->format('Y m d');
|
||||||
|
|
||||||
|
@ -605,8 +598,8 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
||||||
$show_end_date = false;
|
$show_end_date = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$min_epoch = $start->getEpoch();
|
$min_epoch = $min_date->format('U');
|
||||||
$max_epoch = $end->getEpoch();
|
$max_epoch = $max_date->format('U');
|
||||||
|
|
||||||
if ($this->getIsAllDay()) {
|
if ($this->getIsAllDay()) {
|
||||||
if ($show_end_date) {
|
if ($show_end_date) {
|
||||||
|
@ -643,14 +636,19 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($viewer->isLoggedIn()) {
|
if ($viewer->isLoggedIn()) {
|
||||||
$status = $this->getUserInviteStatus($viewer->getPHID());
|
$viewer_phid = $viewer->getPHID();
|
||||||
switch ($status) {
|
if ($this->isRSVPInvited($viewer_phid)) {
|
||||||
case PhabricatorCalendarEventInvitee::STATUS_ATTENDING:
|
return 'fa-users';
|
||||||
return 'fa-check-circle';
|
} else {
|
||||||
case PhabricatorCalendarEventInvitee::STATUS_INVITED:
|
$status = $this->getUserInviteStatus($viewer_phid);
|
||||||
return 'fa-user-plus';
|
switch ($status) {
|
||||||
case PhabricatorCalendarEventInvitee::STATUS_DECLINED:
|
case PhabricatorCalendarEventInvitee::STATUS_ATTENDING:
|
||||||
return 'fa-times';
|
return 'fa-check-circle';
|
||||||
|
case PhabricatorCalendarEventInvitee::STATUS_INVITED:
|
||||||
|
return 'fa-user-plus';
|
||||||
|
case PhabricatorCalendarEventInvitee::STATUS_DECLINED:
|
||||||
|
return 'fa-times-circle';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -671,7 +669,12 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($viewer->isLoggedIn()) {
|
if ($viewer->isLoggedIn()) {
|
||||||
$status = $this->getUserInviteStatus($viewer->getPHID());
|
$viewer_phid = $viewer->getPHID();
|
||||||
|
if ($this->isRSVPInvited($viewer_phid)) {
|
||||||
|
return 'green';
|
||||||
|
}
|
||||||
|
|
||||||
|
$status = $this->getUserInviteStatus($viewer_phid);
|
||||||
switch ($status) {
|
switch ($status) {
|
||||||
case PhabricatorCalendarEventInvitee::STATUS_ATTENDING:
|
case PhabricatorCalendarEventInvitee::STATUS_ATTENDING:
|
||||||
return 'green';
|
return 'green';
|
||||||
|
@ -1121,6 +1124,52 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
||||||
return $phids;
|
return $phids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRSVPs($phid) {
|
||||||
|
return $this->assertAttachedKey($this->rsvps, $phid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attachRSVPs(array $rsvps) {
|
||||||
|
$this->rsvps = $rsvps;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isRSVPInvited($phid) {
|
||||||
|
$status_invited = PhabricatorCalendarEventInvitee::STATUS_INVITED;
|
||||||
|
return ($this->getRSVPStatus($phid) == $status_invited);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasRSVPAuthority($phid, $other_phid) {
|
||||||
|
foreach ($this->getRSVPs($phid) as $rsvp) {
|
||||||
|
if ($rsvp->getInviteePHID() == $other_phid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRSVPStatus($phid) {
|
||||||
|
// Check for an individual invitee record first.
|
||||||
|
$invitees = $this->invitees;
|
||||||
|
$invitees = mpull($invitees, null, 'getInviteePHID');
|
||||||
|
$invitee = idx($invitees, $phid);
|
||||||
|
if ($invitee) {
|
||||||
|
return $invitee->getStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't have one, try to find an invited status for the user's
|
||||||
|
// projects.
|
||||||
|
$status_invited = PhabricatorCalendarEventInvitee::STATUS_INVITED;
|
||||||
|
foreach ($this->getRSVPs($phid) as $rsvp) {
|
||||||
|
if ($rsvp->getStatus() == $status_invited) {
|
||||||
|
return $status_invited;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PhabricatorCalendarEventInvitee::STATUS_UNINVITED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* -( Markup Interface )--------------------------------------------------- */
|
/* -( Markup Interface )--------------------------------------------------- */
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorCalendarInviteeDatasource
|
||||||
|
extends PhabricatorTypeaheadCompositeDatasource {
|
||||||
|
|
||||||
|
public function getBrowseTitle() {
|
||||||
|
return pht('Browse Invitees');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlaceholderText() {
|
||||||
|
return pht('Type a user or project name, or function...');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatasourceApplicationClass() {
|
||||||
|
return 'PhabricatorCalendarApplication';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getComponentDatasources() {
|
||||||
|
return array(
|
||||||
|
new PhabricatorCalendarInviteeUserDatasource(),
|
||||||
|
new PhabricatorCalendarInviteeViewerFunctionDatasource(),
|
||||||
|
new DifferentialExactUserFunctionDatasource(),
|
||||||
|
new PhabricatorProjectDatasource(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function expandInvitees(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
array $values) {
|
||||||
|
|
||||||
|
$phids = array();
|
||||||
|
foreach ($values as $value) {
|
||||||
|
if (phid_get_type($value) == PhabricatorPeopleUserPHIDType::TYPECONST) {
|
||||||
|
$phids[] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$phids) {
|
||||||
|
return $values;
|
||||||
|
}
|
||||||
|
|
||||||
|
$projects = id(new PhabricatorProjectQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withMemberPHIDs($phids)
|
||||||
|
->execute();
|
||||||
|
foreach ($projects as $project) {
|
||||||
|
$values[] = $project->getPHID();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $values;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorCalendarInviteeUserDatasource
|
||||||
|
extends PhabricatorTypeaheadCompositeDatasource {
|
||||||
|
|
||||||
|
public function getBrowseTitle() {
|
||||||
|
return pht('Browse Users');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlaceholderText() {
|
||||||
|
return pht('Type a user name...');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatasourceApplicationClass() {
|
||||||
|
return 'PhabricatorCalendarApplication';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getComponentDatasources() {
|
||||||
|
return array(
|
||||||
|
new PhabricatorPeopleDatasource(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function evaluateValues(array $values) {
|
||||||
|
return PhabricatorCalendarInviteeDatasource::expandInvitees(
|
||||||
|
$this->getViewer(),
|
||||||
|
$values);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorCalendarInviteeViewerFunctionDatasource
|
||||||
|
extends PhabricatorTypeaheadDatasource {
|
||||||
|
|
||||||
|
public function getBrowseTitle() {
|
||||||
|
return pht('Browse Viewer');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlaceholderText() {
|
||||||
|
return pht('Type viewer()...');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatasourceApplicationClass() {
|
||||||
|
return 'PhabricatorPeopleApplication';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatasourceFunctions() {
|
||||||
|
return array(
|
||||||
|
'viewer' => array(
|
||||||
|
'name' => pht('Current Viewer'),
|
||||||
|
'summary' => pht('Use the current viewing user.'),
|
||||||
|
'description' => pht(
|
||||||
|
'Show invites the current viewer is invited to. This function '.
|
||||||
|
'includes events the user is invited to because a project they '.
|
||||||
|
'are a member of is invited.'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadResults() {
|
||||||
|
if ($this->getViewer()->getPHID()) {
|
||||||
|
$results = array($this->renderViewerFunctionToken());
|
||||||
|
} else {
|
||||||
|
$results = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->filterResultsAgainstTokens($results);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function canEvaluateFunction($function) {
|
||||||
|
if (!$this->getViewer()->getPHID()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::canEvaluateFunction($function);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function evaluateFunction($function, array $argv_list) {
|
||||||
|
$results = array();
|
||||||
|
foreach ($argv_list as $argv) {
|
||||||
|
$results[] = $this->getViewer()->getPHID();
|
||||||
|
}
|
||||||
|
|
||||||
|
return PhabricatorCalendarInviteeDatasource::expandInvitees(
|
||||||
|
$this->getViewer(),
|
||||||
|
$results);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderFunctionTokens($function, array $argv_list) {
|
||||||
|
$tokens = array();
|
||||||
|
foreach ($argv_list as $argv) {
|
||||||
|
$tokens[] = PhabricatorTypeaheadTokenView::newFromTypeaheadResult(
|
||||||
|
$this->renderViewerFunctionToken());
|
||||||
|
}
|
||||||
|
return $tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function renderViewerFunctionToken() {
|
||||||
|
return $this->newFunctionResult()
|
||||||
|
->setName(pht('Current Viewer'))
|
||||||
|
->setPHID('viewer()')
|
||||||
|
->setIcon('fa-user')
|
||||||
|
->setUnique(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ final class AphrontCalendarEventView extends AphrontView {
|
||||||
private $iconColor;
|
private $iconColor;
|
||||||
private $canEdit;
|
private $canEdit;
|
||||||
private $isCancelled;
|
private $isCancelled;
|
||||||
|
private $datetimeSummary;
|
||||||
|
|
||||||
public function setIconColor($icon_color) {
|
public function setIconColor($icon_color) {
|
||||||
$this->iconColor = $icon_color;
|
$this->iconColor = $icon_color;
|
||||||
|
@ -135,6 +136,15 @@ final class AphrontCalendarEventView extends AphrontView {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setDatetimeSummary($datetime_summary) {
|
||||||
|
$this->datetimeSummary = $datetime_summary;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatetimeSummary() {
|
||||||
|
return $this->datetimeSummary;
|
||||||
|
}
|
||||||
|
|
||||||
public function render() {
|
public function render() {
|
||||||
throw new Exception(pht('Events are only rendered indirectly.'));
|
throw new Exception(pht('Events are only rendered indirectly.'));
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,9 +45,10 @@ final class PhabricatorConfigApplication extends PhabricatorApplication {
|
||||||
'group/(?P<key>[^/]+)/' => 'PhabricatorConfigGroupController',
|
'group/(?P<key>[^/]+)/' => 'PhabricatorConfigGroupController',
|
||||||
'version/' => 'PhabricatorConfigVersionController',
|
'version/' => 'PhabricatorConfigVersionController',
|
||||||
'database/'.
|
'database/'.
|
||||||
|
'(?:(?P<ref>[^/]+)/'.
|
||||||
'(?:(?P<database>[^/]+)/'.
|
'(?:(?P<database>[^/]+)/'.
|
||||||
'(?:(?P<table>[^/]+)/'.
|
'(?:(?P<table>[^/]+)/'.
|
||||||
'(?:(?:col/(?P<column>[^/]+)|key/(?P<key>[^/]+))/)?)?)?'
|
'(?:(?:col/(?P<column>[^/]+)|key/(?P<key>[^/]+))/)?)?)?)?'
|
||||||
=> 'PhabricatorConfigDatabaseStatusController',
|
=> 'PhabricatorConfigDatabaseStatusController',
|
||||||
'dbissue/' => 'PhabricatorConfigDatabaseIssueController',
|
'dbissue/' => 'PhabricatorConfigDatabaseIssueController',
|
||||||
'(?P<verb>ignore|unignore)/(?P<key>[^/]+)/'
|
'(?P<verb>ignore|unignore)/(?P<key>[^/]+)/'
|
||||||
|
|
|
@ -12,89 +12,6 @@ final class PhabricatorDatabaseSetupCheck extends PhabricatorSetupCheck {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function executeChecks() {
|
protected function executeChecks() {
|
||||||
$master = PhabricatorDatabaseRef::getMasterDatabaseRef();
|
|
||||||
if (!$master) {
|
|
||||||
// If we're implicitly in read-only mode during disaster recovery,
|
|
||||||
// don't bother with these setup checks.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$conn_raw = $master->newManagementConnection();
|
|
||||||
|
|
||||||
try {
|
|
||||||
queryfx($conn_raw, 'SELECT 1');
|
|
||||||
$database_exception = null;
|
|
||||||
} catch (AphrontInvalidCredentialsQueryException $ex) {
|
|
||||||
$database_exception = $ex;
|
|
||||||
} catch (AphrontConnectionQueryException $ex) {
|
|
||||||
$database_exception = $ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($database_exception) {
|
|
||||||
$issue = PhabricatorSetupIssue::newDatabaseConnectionIssue(
|
|
||||||
$database_exception);
|
|
||||||
$this->addIssue($issue);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$engines = queryfx_all($conn_raw, 'SHOW ENGINES');
|
|
||||||
$engines = ipull($engines, 'Support', 'Engine');
|
|
||||||
|
|
||||||
$innodb = idx($engines, 'InnoDB');
|
|
||||||
if ($innodb != 'YES' && $innodb != 'DEFAULT') {
|
|
||||||
$message = pht(
|
|
||||||
"The 'InnoDB' engine is not available in MySQL. Enable InnoDB in ".
|
|
||||||
"your MySQL configuration.".
|
|
||||||
"\n\n".
|
|
||||||
"(If you aleady created tables, MySQL incorrectly used some other ".
|
|
||||||
"engine to create them. You need to convert them or drop and ".
|
|
||||||
"reinitialize them.)");
|
|
||||||
|
|
||||||
$this->newIssue('mysql.innodb')
|
|
||||||
->setName(pht('MySQL InnoDB Engine Not Available'))
|
|
||||||
->setMessage($message)
|
|
||||||
->setIsFatal(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$namespace = PhabricatorEnv::getEnvConfig('storage.default-namespace');
|
|
||||||
|
|
||||||
$databases = queryfx_all($conn_raw, 'SHOW DATABASES');
|
|
||||||
$databases = ipull($databases, 'Database', 'Database');
|
|
||||||
|
|
||||||
if (empty($databases[$namespace.'_meta_data'])) {
|
|
||||||
$message = pht(
|
|
||||||
"Run the storage upgrade script to setup Phabricator's database ".
|
|
||||||
"schema.");
|
|
||||||
|
|
||||||
$this->newIssue('storage.upgrade')
|
|
||||||
->setName(pht('Setup MySQL Schema'))
|
|
||||||
->setMessage($message)
|
|
||||||
->setIsFatal(true)
|
|
||||||
->addCommand(hsprintf('<tt>phabricator/ $</tt> ./bin/storage upgrade'));
|
|
||||||
} else {
|
|
||||||
$conn_meta = $master->newApplicationConnection(
|
|
||||||
$namespace.'_meta_data');
|
|
||||||
|
|
||||||
$applied = queryfx_all($conn_meta, 'SELECT patch FROM patch_status');
|
|
||||||
$applied = ipull($applied, 'patch', 'patch');
|
|
||||||
|
|
||||||
$all = PhabricatorSQLPatchList::buildAllPatches();
|
|
||||||
$diff = array_diff_key($all, $applied);
|
|
||||||
|
|
||||||
if ($diff) {
|
|
||||||
$this->newIssue('storage.patch')
|
|
||||||
->setName(pht('Upgrade MySQL Schema'))
|
|
||||||
->setMessage(
|
|
||||||
pht(
|
|
||||||
"Run the storage upgrade script to upgrade Phabricator's ".
|
|
||||||
"database schema. Missing patches:<br />%s<br />",
|
|
||||||
phutil_implode_html(phutil_tag('br'), array_keys($diff))))
|
|
||||||
->addCommand(
|
|
||||||
hsprintf('<tt>phabricator/ $</tt> ./bin/storage upgrade'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$host = PhabricatorEnv::getEnvConfig('mysql.host');
|
$host = PhabricatorEnv::getEnvConfig('mysql.host');
|
||||||
$matches = null;
|
$matches = null;
|
||||||
if (preg_match('/^([^:]+):(\d+)$/', $host, $matches)) {
|
if (preg_match('/^([^:]+):(\d+)$/', $host, $matches)) {
|
||||||
|
@ -126,5 +43,97 @@ final class PhabricatorDatabaseSetupCheck extends PhabricatorSetupCheck {
|
||||||
$port));
|
$port));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$masters = PhabricatorDatabaseRef::getMasterDatabaseRefs();
|
||||||
|
if (!$masters) {
|
||||||
|
// If we're implicitly in read-only mode during disaster recovery,
|
||||||
|
// don't bother with these setup checks.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($masters as $master) {
|
||||||
|
if ($this->checkMasterDatabase($master)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkMasterDatabase(PhabricatorDatabaseRef $master) {
|
||||||
|
$conn_raw = $master->newManagementConnection();
|
||||||
|
|
||||||
|
try {
|
||||||
|
queryfx($conn_raw, 'SELECT 1');
|
||||||
|
$database_exception = null;
|
||||||
|
} catch (AphrontInvalidCredentialsQueryException $ex) {
|
||||||
|
$database_exception = $ex;
|
||||||
|
} catch (AphrontConnectionQueryException $ex) {
|
||||||
|
$database_exception = $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($database_exception) {
|
||||||
|
$issue = PhabricatorSetupIssue::newDatabaseConnectionIssue(
|
||||||
|
$database_exception);
|
||||||
|
$this->addIssue($issue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$engines = queryfx_all($conn_raw, 'SHOW ENGINES');
|
||||||
|
$engines = ipull($engines, 'Support', 'Engine');
|
||||||
|
|
||||||
|
$innodb = idx($engines, 'InnoDB');
|
||||||
|
if ($innodb != 'YES' && $innodb != 'DEFAULT') {
|
||||||
|
$message = pht(
|
||||||
|
"The 'InnoDB' engine is not available in MySQL. Enable InnoDB in ".
|
||||||
|
"your MySQL configuration.".
|
||||||
|
"\n\n".
|
||||||
|
"(If you aleady created tables, MySQL incorrectly used some other ".
|
||||||
|
"engine to create them. You need to convert them or drop and ".
|
||||||
|
"reinitialize them.)");
|
||||||
|
|
||||||
|
$this->newIssue('mysql.innodb')
|
||||||
|
->setName(pht('MySQL InnoDB Engine Not Available'))
|
||||||
|
->setMessage($message)
|
||||||
|
->setIsFatal(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$namespace = PhabricatorEnv::getEnvConfig('storage.default-namespace');
|
||||||
|
|
||||||
|
$databases = queryfx_all($conn_raw, 'SHOW DATABASES');
|
||||||
|
$databases = ipull($databases, 'Database', 'Database');
|
||||||
|
|
||||||
|
if (empty($databases[$namespace.'_meta_data'])) {
|
||||||
|
$message = pht(
|
||||||
|
"Run the storage upgrade script to setup Phabricator's database ".
|
||||||
|
"schema.");
|
||||||
|
|
||||||
|
$this->newIssue('storage.upgrade')
|
||||||
|
->setName(pht('Setup MySQL Schema'))
|
||||||
|
->setMessage($message)
|
||||||
|
->setIsFatal(true)
|
||||||
|
->addCommand(hsprintf('<tt>phabricator/ $</tt> ./bin/storage upgrade'));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$conn_meta = $master->newApplicationConnection(
|
||||||
|
$namespace.'_meta_data');
|
||||||
|
|
||||||
|
$applied = queryfx_all($conn_meta, 'SELECT patch FROM patch_status');
|
||||||
|
$applied = ipull($applied, 'patch', 'patch');
|
||||||
|
|
||||||
|
$all = PhabricatorSQLPatchList::buildAllPatches();
|
||||||
|
$diff = array_diff_key($all, $applied);
|
||||||
|
|
||||||
|
if ($diff) {
|
||||||
|
$this->newIssue('storage.patch')
|
||||||
|
->setName(pht('Upgrade MySQL Schema'))
|
||||||
|
->setMessage(
|
||||||
|
pht(
|
||||||
|
"Run the storage upgrade script to upgrade Phabricator's ".
|
||||||
|
"database schema. Missing patches:<br />%s<br />",
|
||||||
|
phutil_implode_html(phutil_tag('br'), array_keys($diff))))
|
||||||
|
->addCommand(
|
||||||
|
hsprintf('<tt>phabricator/ $</tt> ./bin/storage upgrade'));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,22 +3,6 @@
|
||||||
abstract class PhabricatorConfigDatabaseController
|
abstract class PhabricatorConfigDatabaseController
|
||||||
extends PhabricatorConfigController {
|
extends PhabricatorConfigController {
|
||||||
|
|
||||||
protected function buildSchemaQuery() {
|
|
||||||
$ref = PhabricatorDatabaseRef::getMasterDatabaseRef();
|
|
||||||
|
|
||||||
$api = id(new PhabricatorStorageManagementAPI())
|
|
||||||
->setUser($ref->getUser())
|
|
||||||
->setHost($ref->getHost())
|
|
||||||
->setPort($ref->getPort())
|
|
||||||
->setNamespace(PhabricatorLiskDAO::getDefaultStorageNamespace())
|
|
||||||
->setPassword($ref->getPass());
|
|
||||||
|
|
||||||
$query = id(new PhabricatorConfigSchemaQuery())
|
|
||||||
->setAPI($api);
|
|
||||||
|
|
||||||
return $query;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function renderIcon($status) {
|
protected function renderIcon($status) {
|
||||||
switch ($status) {
|
switch ($status) {
|
||||||
case PhabricatorConfigStorageSchema::STATUS_OKAY:
|
case PhabricatorConfigStorageSchema::STATUS_OKAY:
|
||||||
|
|
|
@ -6,11 +6,11 @@ final class PhabricatorConfigDatabaseIssueController
|
||||||
public function handleRequest(AphrontRequest $request) {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
$viewer = $request->getViewer();
|
$viewer = $request->getViewer();
|
||||||
|
|
||||||
$query = $this->buildSchemaQuery();
|
$query = new PhabricatorConfigSchemaQuery();
|
||||||
|
|
||||||
$actual = $query->loadActualSchema();
|
$actual = $query->loadActualSchemata();
|
||||||
$expect = $query->loadExpectedSchema();
|
$expect = $query->loadExpectedSchemata();
|
||||||
$comp = $query->buildComparisonSchema($expect, $actual);
|
$comp_servers = $query->buildComparisonSchemata($expect, $actual);
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
$crumbs->addTextCrumb(pht('Database Issues'));
|
$crumbs->addTextCrumb(pht('Database Issues'));
|
||||||
|
@ -18,65 +18,70 @@ final class PhabricatorConfigDatabaseIssueController
|
||||||
|
|
||||||
// Collect all open issues.
|
// Collect all open issues.
|
||||||
$issues = array();
|
$issues = array();
|
||||||
foreach ($comp->getDatabases() as $database_name => $database) {
|
foreach ($comp_servers as $ref_name => $comp) {
|
||||||
foreach ($database->getLocalIssues() as $issue) {
|
foreach ($comp->getDatabases() as $database_name => $database) {
|
||||||
$issues[] = array(
|
foreach ($database->getLocalIssues() as $issue) {
|
||||||
$database_name,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
$issue,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
foreach ($database->getTables() as $table_name => $table) {
|
|
||||||
foreach ($table->getLocalIssues() as $issue) {
|
|
||||||
$issues[] = array(
|
$issues[] = array(
|
||||||
|
$ref_name,
|
||||||
$database_name,
|
$database_name,
|
||||||
$table_name,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
$issue,
|
$issue,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
foreach ($table->getColumns() as $column_name => $column) {
|
foreach ($database->getTables() as $table_name => $table) {
|
||||||
foreach ($column->getLocalIssues() as $issue) {
|
foreach ($table->getLocalIssues() as $issue) {
|
||||||
$issues[] = array(
|
$issues[] = array(
|
||||||
|
$ref_name,
|
||||||
$database_name,
|
$database_name,
|
||||||
$table_name,
|
$table_name,
|
||||||
'column',
|
null,
|
||||||
$column_name,
|
null,
|
||||||
$issue,
|
$issue,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
foreach ($table->getColumns() as $column_name => $column) {
|
||||||
foreach ($table->getKeys() as $key_name => $key) {
|
foreach ($column->getLocalIssues() as $issue) {
|
||||||
foreach ($key->getLocalIssues() as $issue) {
|
$issues[] = array(
|
||||||
$issues[] = array(
|
$ref_name,
|
||||||
$database_name,
|
$database_name,
|
||||||
$table_name,
|
$table_name,
|
||||||
'key',
|
'column',
|
||||||
$key_name,
|
$column_name,
|
||||||
$issue,
|
$issue,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($table->getKeys() as $key_name => $key) {
|
||||||
|
foreach ($key->getLocalIssues() as $issue) {
|
||||||
|
$issues[] = array(
|
||||||
|
$ref_name,
|
||||||
|
$database_name,
|
||||||
|
$table_name,
|
||||||
|
'key',
|
||||||
|
$key_name,
|
||||||
|
$issue,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Sort all open issues so that the most severe issues appear first.
|
// Sort all open issues so that the most severe issues appear first.
|
||||||
$order = array();
|
$order = array();
|
||||||
$counts = array();
|
$counts = array();
|
||||||
foreach ($issues as $key => $issue) {
|
foreach ($issues as $key => $issue) {
|
||||||
$const = $issue[4];
|
$const = $issue[5];
|
||||||
$status = PhabricatorConfigStorageSchema::getIssueStatus($const);
|
$status = PhabricatorConfigStorageSchema::getIssueStatus($const);
|
||||||
$severity = PhabricatorConfigStorageSchema::getStatusSeverity($status);
|
$severity = PhabricatorConfigStorageSchema::getStatusSeverity($status);
|
||||||
$order[$key] = sprintf(
|
$order[$key] = sprintf(
|
||||||
'~%d~%s%s%s',
|
'~%d~%s%s%s',
|
||||||
9 - $severity,
|
9 - $severity,
|
||||||
$issue[0],
|
|
||||||
$issue[1],
|
$issue[1],
|
||||||
$issue[3]);
|
$issue[2],
|
||||||
|
$issue[4]);
|
||||||
|
|
||||||
if (empty($counts[$status])) {
|
if (empty($counts[$status])) {
|
||||||
$counts[$status] = 0;
|
$counts[$status] = 0;
|
||||||
|
@ -91,22 +96,25 @@ final class PhabricatorConfigDatabaseIssueController
|
||||||
// Render the issues.
|
// Render the issues.
|
||||||
$rows = array();
|
$rows = array();
|
||||||
foreach ($issues as $issue) {
|
foreach ($issues as $issue) {
|
||||||
$const = $issue[4];
|
$const = $issue[5];
|
||||||
|
|
||||||
|
$uri = $this->getApplicationURI('/database/'.$issue[0].'/'.$issue[1].'/');
|
||||||
|
|
||||||
$database_link = phutil_tag(
|
$database_link = phutil_tag(
|
||||||
'a',
|
'a',
|
||||||
array(
|
array(
|
||||||
'href' => $this->getApplicationURI('/database/'.$issue[0].'/'),
|
'href' => $uri,
|
||||||
),
|
),
|
||||||
$issue[0]);
|
$issue[1]);
|
||||||
|
|
||||||
$rows[] = array(
|
$rows[] = array(
|
||||||
$this->renderIcon(
|
$this->renderIcon(
|
||||||
PhabricatorConfigStorageSchema::getIssueStatus($const)),
|
PhabricatorConfigStorageSchema::getIssueStatus($const)),
|
||||||
|
$issue[0],
|
||||||
$database_link,
|
$database_link,
|
||||||
$issue[1],
|
|
||||||
$issue[2],
|
$issue[2],
|
||||||
$issue[3],
|
$issue[3],
|
||||||
|
$issue[4],
|
||||||
PhabricatorConfigStorageSchema::getIssueDescription($const),
|
PhabricatorConfigStorageSchema::getIssueDescription($const),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -117,6 +125,7 @@ final class PhabricatorConfigDatabaseIssueController
|
||||||
->setHeaders(
|
->setHeaders(
|
||||||
array(
|
array(
|
||||||
null,
|
null,
|
||||||
|
pht('Server'),
|
||||||
pht('Database'),
|
pht('Database'),
|
||||||
pht('Table'),
|
pht('Table'),
|
||||||
pht('Type'),
|
pht('Type'),
|
||||||
|
@ -130,6 +139,7 @@ final class PhabricatorConfigDatabaseIssueController
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
'wide',
|
'wide',
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ final class PhabricatorConfigDatabaseStatusController
|
||||||
private $table;
|
private $table;
|
||||||
private $column;
|
private $column;
|
||||||
private $key;
|
private $key;
|
||||||
|
private $ref;
|
||||||
|
|
||||||
public function handleRequest(AphrontRequest $request) {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
$viewer = $request->getViewer();
|
$viewer = $request->getViewer();
|
||||||
|
@ -14,48 +15,59 @@ final class PhabricatorConfigDatabaseStatusController
|
||||||
$this->table = $request->getURIData('table');
|
$this->table = $request->getURIData('table');
|
||||||
$this->column = $request->getURIData('column');
|
$this->column = $request->getURIData('column');
|
||||||
$this->key = $request->getURIData('key');
|
$this->key = $request->getURIData('key');
|
||||||
|
$this->ref = $request->getURIData('ref');
|
||||||
|
|
||||||
$query = $this->buildSchemaQuery();
|
$query = new PhabricatorConfigSchemaQuery();
|
||||||
|
|
||||||
$actual = $query->loadActualSchema();
|
$actual = $query->loadActualSchemata();
|
||||||
$expect = $query->loadExpectedSchema();
|
$expect = $query->loadExpectedSchemata();
|
||||||
$comp = $query->buildComparisonSchema($expect, $actual);
|
$comp = $query->buildComparisonSchemata($expect, $actual);
|
||||||
|
|
||||||
if ($this->column) {
|
if ($this->ref !== null) {
|
||||||
return $this->renderColumn(
|
$server_actual = idx($actual, $this->ref);
|
||||||
$comp,
|
if (!$server_actual) {
|
||||||
$expect,
|
return new Aphront404Response();
|
||||||
$actual,
|
}
|
||||||
$this->database,
|
|
||||||
$this->table,
|
$server_comparison = $comp[$this->ref];
|
||||||
$this->column);
|
$server_expect = $expect[$this->ref];
|
||||||
} else if ($this->key) {
|
|
||||||
return $this->renderKey(
|
if ($this->column) {
|
||||||
$comp,
|
return $this->renderColumn(
|
||||||
$expect,
|
$server_comparison,
|
||||||
$actual,
|
$server_expect,
|
||||||
$this->database,
|
$server_actual,
|
||||||
$this->table,
|
$this->database,
|
||||||
$this->key);
|
$this->table,
|
||||||
} else if ($this->table) {
|
$this->column);
|
||||||
return $this->renderTable(
|
} else if ($this->key) {
|
||||||
$comp,
|
return $this->renderKey(
|
||||||
$expect,
|
$server_comparison,
|
||||||
$actual,
|
$server_expect,
|
||||||
$this->database,
|
$server_actual,
|
||||||
$this->table);
|
$this->database,
|
||||||
} else if ($this->database) {
|
$this->table,
|
||||||
return $this->renderDatabase(
|
$this->key);
|
||||||
$comp,
|
} else if ($this->table) {
|
||||||
$expect,
|
return $this->renderTable(
|
||||||
$actual,
|
$server_comparison,
|
||||||
$this->database);
|
$server_expect,
|
||||||
} else {
|
$server_actual,
|
||||||
return $this->renderServer(
|
$this->database,
|
||||||
$comp,
|
$this->table);
|
||||||
$expect,
|
} else if ($this->database) {
|
||||||
$actual);
|
return $this->renderDatabase(
|
||||||
|
$server_comparison,
|
||||||
|
$server_expect,
|
||||||
|
$server_actual,
|
||||||
|
$this->database);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $this->renderServers(
|
||||||
|
$comp,
|
||||||
|
$expect,
|
||||||
|
$actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildResponse($title, $body) {
|
private function buildResponse($title, $body) {
|
||||||
|
@ -66,34 +78,57 @@ final class PhabricatorConfigDatabaseStatusController
|
||||||
$title = pht('Database Status');
|
$title = pht('Database Status');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$ref = $this->ref;
|
||||||
|
$database = $this->database;
|
||||||
|
$table = $this->table;
|
||||||
|
$column = $this->column;
|
||||||
|
$key = $this->key;
|
||||||
|
|
||||||
|
$links = array();
|
||||||
|
$links[] = array(
|
||||||
|
pht('Database Status'),
|
||||||
|
'database/',
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($database) {
|
||||||
|
$links[] = array(
|
||||||
|
$database,
|
||||||
|
"database/{$ref}/{$database}/",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($table) {
|
||||||
|
$links[] = array(
|
||||||
|
$table,
|
||||||
|
"database/{$ref}/{$database}/{$table}/",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($column) {
|
||||||
|
$links[] = array(
|
||||||
|
$column,
|
||||||
|
"database/{$ref}/{$database}/{$table}/col/{$column}/",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($key) {
|
||||||
|
$links[] = array(
|
||||||
|
$key,
|
||||||
|
"database/{$ref}/{$database}/{$table}/key/{$key}/",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
$crumbs->setBorder(true);
|
$crumbs->setBorder(true);
|
||||||
if ($this->database) {
|
|
||||||
$crumbs->addTextCrumb(
|
$last_key = last_key($links);
|
||||||
pht('Database Status'),
|
foreach ($links as $link_key => $link) {
|
||||||
$this->getApplicationURI('database/'));
|
list($name, $href) = $link;
|
||||||
if ($this->table) {
|
if ($link_key == $last_key) {
|
||||||
$crumbs->addTextCrumb(
|
$crumbs->addTextCrumb($name);
|
||||||
$this->database,
|
|
||||||
$this->getApplicationURI('database/'.$this->database.'/'));
|
|
||||||
if ($this->column || $this->key) {
|
|
||||||
$crumbs->addTextCrumb(
|
|
||||||
$this->table,
|
|
||||||
$this->getApplicationURI(
|
|
||||||
'database/'.$this->database.'/'.$this->table.'/'));
|
|
||||||
if ($this->column) {
|
|
||||||
$crumbs->addTextCrumb($this->column);
|
|
||||||
} else {
|
|
||||||
$crumbs->addTextCrumb($this->key);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$crumbs->addTextCrumb($this->table);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
$crumbs->addTextCrumb($this->database);
|
$crumbs->addTextCrumb($name, $this->getApplicationURI($href));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
$crumbs->addTextCrumb(pht('Database Status'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$doc_link = PhabricatorEnv::getDoclink('Managing Storage Adjustments');
|
$doc_link = PhabricatorEnv::getDoclink('Managing Storage Adjustments');
|
||||||
|
@ -121,52 +156,64 @@ final class PhabricatorConfigDatabaseStatusController
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private function renderServer(
|
private function renderServers(
|
||||||
PhabricatorConfigServerSchema $comp,
|
array $comp_servers,
|
||||||
PhabricatorConfigServerSchema $expect,
|
array $expect_servers,
|
||||||
PhabricatorConfigServerSchema $actual) {
|
array $actual_servers) {
|
||||||
|
|
||||||
$charset_issue = PhabricatorConfigStorageSchema::ISSUE_CHARSET;
|
$charset_issue = PhabricatorConfigStorageSchema::ISSUE_CHARSET;
|
||||||
$collation_issue = PhabricatorConfigStorageSchema::ISSUE_COLLATION;
|
$collation_issue = PhabricatorConfigStorageSchema::ISSUE_COLLATION;
|
||||||
|
|
||||||
$rows = array();
|
$rows = array();
|
||||||
foreach ($comp->getDatabases() as $database_name => $database) {
|
foreach ($comp_servers as $ref_key => $comp) {
|
||||||
$actual_database = $actual->getDatabase($database_name);
|
$actual = $actual_servers[$ref_key];
|
||||||
if ($actual_database) {
|
$expect = $expect_servers[$ref_key];
|
||||||
$charset = $actual_database->getCharacterSet();
|
foreach ($comp->getDatabases() as $database_name => $database) {
|
||||||
$collation = $actual_database->getCollation();
|
$actual_database = $actual->getDatabase($database_name);
|
||||||
} else {
|
if ($actual_database) {
|
||||||
$charset = null;
|
$charset = $actual_database->getCharacterSet();
|
||||||
$collation = null;
|
$collation = $actual_database->getCollation();
|
||||||
}
|
} else {
|
||||||
|
$charset = null;
|
||||||
|
$collation = null;
|
||||||
|
}
|
||||||
|
|
||||||
$status = $database->getStatus();
|
$status = $database->getStatus();
|
||||||
$issues = $database->getIssues();
|
$issues = $database->getIssues();
|
||||||
|
|
||||||
$rows[] = array(
|
$uri = $this->getURI(
|
||||||
$this->renderIcon($status),
|
|
||||||
phutil_tag(
|
|
||||||
'a',
|
|
||||||
array(
|
array(
|
||||||
'href' => $this->getApplicationURI(
|
'ref' => $ref_key,
|
||||||
'/database/'.$database_name.'/'),
|
'database' => $database_name,
|
||||||
),
|
));
|
||||||
$database_name),
|
|
||||||
$this->renderAttr($charset, $database->hasIssue($charset_issue)),
|
$rows[] = array(
|
||||||
$this->renderAttr($collation, $database->hasIssue($collation_issue)),
|
$this->renderIcon($status),
|
||||||
);
|
$ref_key,
|
||||||
|
phutil_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => $uri,
|
||||||
|
),
|
||||||
|
$database_name),
|
||||||
|
$this->renderAttr($charset, $database->hasIssue($charset_issue)),
|
||||||
|
$this->renderAttr($collation, $database->hasIssue($collation_issue)),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$table = id(new AphrontTableView($rows))
|
$table = id(new AphrontTableView($rows))
|
||||||
->setHeaders(
|
->setHeaders(
|
||||||
array(
|
array(
|
||||||
null,
|
null,
|
||||||
|
pht('Server'),
|
||||||
pht('Database'),
|
pht('Database'),
|
||||||
pht('Charset'),
|
pht('Charset'),
|
||||||
pht('Collation'),
|
pht('Collation'),
|
||||||
))
|
))
|
||||||
->setColumnClasses(
|
->setColumnClasses(
|
||||||
array(
|
array(
|
||||||
|
null,
|
||||||
null,
|
null,
|
||||||
'wide pri',
|
'wide pri',
|
||||||
null,
|
null,
|
||||||
|
@ -200,13 +247,17 @@ final class PhabricatorConfigDatabaseStatusController
|
||||||
foreach ($database->getTables() as $table_name => $table) {
|
foreach ($database->getTables() as $table_name => $table) {
|
||||||
$status = $table->getStatus();
|
$status = $table->getStatus();
|
||||||
|
|
||||||
|
$uri = $this->getURI(
|
||||||
|
array(
|
||||||
|
'table' => $table_name,
|
||||||
|
));
|
||||||
|
|
||||||
$rows[] = array(
|
$rows[] = array(
|
||||||
$this->renderIcon($status),
|
$this->renderIcon($status),
|
||||||
phutil_tag(
|
phutil_tag(
|
||||||
'a',
|
'a',
|
||||||
array(
|
array(
|
||||||
'href' => $this->getApplicationURI(
|
'href' => $uri,
|
||||||
'/database/'.$database_name.'/'.$table_name.'/'),
|
|
||||||
),
|
),
|
||||||
$table_name),
|
$table_name),
|
||||||
$this->renderAttr(
|
$this->renderAttr(
|
||||||
|
@ -251,6 +302,10 @@ final class PhabricatorConfigDatabaseStatusController
|
||||||
|
|
||||||
$properties = $this->buildProperties(
|
$properties = $this->buildProperties(
|
||||||
array(
|
array(
|
||||||
|
array(
|
||||||
|
pht('Server'),
|
||||||
|
$this->ref,
|
||||||
|
),
|
||||||
array(
|
array(
|
||||||
pht('Character Set'),
|
pht('Character Set'),
|
||||||
$actual_charset,
|
$actual_charset,
|
||||||
|
@ -325,17 +380,17 @@ final class PhabricatorConfigDatabaseStatusController
|
||||||
$data_type = $expect_column->getDataType();
|
$data_type = $expect_column->getDataType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$uri = $this->getURI(
|
||||||
|
array(
|
||||||
|
'column' => $column_name,
|
||||||
|
));
|
||||||
|
|
||||||
$rows[] = array(
|
$rows[] = array(
|
||||||
$this->renderIcon($status),
|
$this->renderIcon($status),
|
||||||
phutil_tag(
|
phutil_tag(
|
||||||
'a',
|
'a',
|
||||||
array(
|
array(
|
||||||
'href' => $this->getApplicationURI(
|
'href' => $uri,
|
||||||
'database/'.
|
|
||||||
$database_name.'/'.
|
|
||||||
$table_name.'/'.
|
|
||||||
'col/'.
|
|
||||||
$column_name.'/'),
|
|
||||||
),
|
),
|
||||||
$column_name),
|
$column_name),
|
||||||
$data_type,
|
$data_type,
|
||||||
|
@ -407,17 +462,17 @@ final class PhabricatorConfigDatabaseStatusController
|
||||||
$key->hasIssue($longkey_issue));
|
$key->hasIssue($longkey_issue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$uri = $this->getURI(
|
||||||
|
array(
|
||||||
|
'key' => $key_name,
|
||||||
|
));
|
||||||
|
|
||||||
$key_rows[] = array(
|
$key_rows[] = array(
|
||||||
$this->renderIcon($status),
|
$this->renderIcon($status),
|
||||||
phutil_tag(
|
phutil_tag(
|
||||||
'a',
|
'a',
|
||||||
array(
|
array(
|
||||||
'href' => $this->getApplicationURI(
|
'href' => $uri,
|
||||||
'database/'.
|
|
||||||
$database_name.'/'.
|
|
||||||
$table_name.'/'.
|
|
||||||
'key/'.
|
|
||||||
$key_name.'/'),
|
|
||||||
),
|
),
|
||||||
$key_name),
|
$key_name),
|
||||||
$this->renderAttr(
|
$this->renderAttr(
|
||||||
|
@ -464,6 +519,10 @@ final class PhabricatorConfigDatabaseStatusController
|
||||||
|
|
||||||
$properties = $this->buildProperties(
|
$properties = $this->buildProperties(
|
||||||
array(
|
array(
|
||||||
|
array(
|
||||||
|
pht('Server'),
|
||||||
|
$this->ref,
|
||||||
|
),
|
||||||
array(
|
array(
|
||||||
pht('Collation'),
|
pht('Collation'),
|
||||||
$actual_collation,
|
$actual_collation,
|
||||||
|
@ -561,6 +620,10 @@ final class PhabricatorConfigDatabaseStatusController
|
||||||
|
|
||||||
$properties = $this->buildProperties(
|
$properties = $this->buildProperties(
|
||||||
array(
|
array(
|
||||||
|
array(
|
||||||
|
pht('Server'),
|
||||||
|
$this->ref,
|
||||||
|
),
|
||||||
array(
|
array(
|
||||||
pht('Data Type'),
|
pht('Data Type'),
|
||||||
$data_type,
|
$data_type,
|
||||||
|
@ -678,6 +741,10 @@ final class PhabricatorConfigDatabaseStatusController
|
||||||
|
|
||||||
$properties = $this->buildProperties(
|
$properties = $this->buildProperties(
|
||||||
array(
|
array(
|
||||||
|
array(
|
||||||
|
pht('Server'),
|
||||||
|
$this->ref,
|
||||||
|
),
|
||||||
array(
|
array(
|
||||||
pht('Unique'),
|
pht('Unique'),
|
||||||
$this->renderBoolean($actual_unique),
|
$this->renderBoolean($actual_unique),
|
||||||
|
@ -745,4 +812,40 @@ final class PhabricatorConfigDatabaseStatusController
|
||||||
return phutil_tag_div('config-page-property', $view);
|
return phutil_tag_div('config-page-property', $view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getURI(array $properties) {
|
||||||
|
$defaults = array(
|
||||||
|
'ref' => $this->ref,
|
||||||
|
'database' => $this->database,
|
||||||
|
'table' => $this->table,
|
||||||
|
'column' => $this->column,
|
||||||
|
'key' => $this->key,
|
||||||
|
);
|
||||||
|
|
||||||
|
$properties = $properties + $defaults;
|
||||||
|
$properties = array_select_keys($properties, array_keys($defaults));
|
||||||
|
|
||||||
|
$parts = array();
|
||||||
|
foreach ($properties as $key => $property) {
|
||||||
|
if (!strlen($property)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($key == 'column') {
|
||||||
|
$parts[] = 'col';
|
||||||
|
} else if ($key == 'key') {
|
||||||
|
$parts[] = 'key';
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts[] = $property;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($parts) {
|
||||||
|
$parts = implode('/', $parts).'/';
|
||||||
|
} else {
|
||||||
|
$parts = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->getApplicationURI('/database/'.$parts);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,36 +2,70 @@
|
||||||
|
|
||||||
final class PhabricatorConfigSchemaQuery extends Phobject {
|
final class PhabricatorConfigSchemaQuery extends Phobject {
|
||||||
|
|
||||||
private $api;
|
private $refs;
|
||||||
|
private $apis;
|
||||||
|
|
||||||
public function setAPI(PhabricatorStorageManagementAPI $api) {
|
public function setRefs(array $refs) {
|
||||||
$this->api = $api;
|
$this->refs = $refs;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getAPI() {
|
public function getRefs() {
|
||||||
if (!$this->api) {
|
if (!$this->refs) {
|
||||||
throw new PhutilInvalidStateException('setAPI');
|
return PhabricatorDatabaseRef::getMasterDatabaseRefs();
|
||||||
}
|
}
|
||||||
return $this->api;
|
return $this->refs;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getConn() {
|
public function setAPIs(array $apis) {
|
||||||
return $this->getAPI()->getConn(null);
|
$map = array();
|
||||||
|
foreach ($apis as $api) {
|
||||||
|
$map[$api->getRef()->getRefKey()] = $api;
|
||||||
|
}
|
||||||
|
$this->apis = $map;
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getDatabaseNames() {
|
private function getDatabaseNames(PhabricatorDatabaseRef $ref) {
|
||||||
$api = $this->getAPI();
|
$api = $this->getAPI($ref);
|
||||||
$patches = PhabricatorSQLPatchList::buildAllPatches();
|
$patches = PhabricatorSQLPatchList::buildAllPatches();
|
||||||
return $api->getDatabaseList(
|
return $api->getDatabaseList(
|
||||||
$patches,
|
$patches,
|
||||||
$only_living = true);
|
$only_living = true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadActualSchema() {
|
private function getAPI(PhabricatorDatabaseRef $ref) {
|
||||||
$databases = $this->getDatabaseNames();
|
$key = $ref->getRefKey();
|
||||||
|
|
||||||
|
if (isset($this->apis[$key])) {
|
||||||
|
return $this->apis[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return id(new PhabricatorStorageManagementAPI())
|
||||||
|
->setUser($ref->getUser())
|
||||||
|
->setHost($ref->getHost())
|
||||||
|
->setPort($ref->getPort())
|
||||||
|
->setNamespace(PhabricatorLiskDAO::getDefaultStorageNamespace())
|
||||||
|
->setPassword($ref->getPass());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadActualSchemata() {
|
||||||
|
$refs = $this->getRefs();
|
||||||
|
|
||||||
|
$schemata = array();
|
||||||
|
foreach ($refs as $ref) {
|
||||||
|
$schema = $this->loadActualSchemaForServer($ref);
|
||||||
|
$schemata[$schema->getRef()->getRefKey()] = $schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $schemata;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadActualSchemaForServer(PhabricatorDatabaseRef $ref) {
|
||||||
|
$databases = $this->getDatabaseNames($ref);
|
||||||
|
|
||||||
|
$conn = $ref->newManagementConnection();
|
||||||
|
|
||||||
$conn = $this->getConn();
|
|
||||||
$tables = queryfx_all(
|
$tables = queryfx_all(
|
||||||
$conn,
|
$conn,
|
||||||
'SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_COLLATION
|
'SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_COLLATION
|
||||||
|
@ -92,7 +126,8 @@ final class PhabricatorConfigSchemaQuery extends Phobject {
|
||||||
// primary, unique, and foreign keys, so we can't use them here. We pull
|
// primary, unique, and foreign keys, so we can't use them here. We pull
|
||||||
// indexes later on using SHOW INDEXES.
|
// indexes later on using SHOW INDEXES.
|
||||||
|
|
||||||
$server_schema = new PhabricatorConfigServerSchema();
|
$server_schema = id(new PhabricatorConfigServerSchema())
|
||||||
|
->setRef($ref);
|
||||||
|
|
||||||
$tables = igroup($tables, 'TABLE_SCHEMA');
|
$tables = igroup($tables, 'TABLE_SCHEMA');
|
||||||
foreach ($tables as $database_name => $database_tables) {
|
foreach ($tables as $database_name => $database_tables) {
|
||||||
|
@ -177,15 +212,29 @@ final class PhabricatorConfigSchemaQuery extends Phobject {
|
||||||
return $server_schema;
|
return $server_schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadExpectedSchema() {
|
public function loadExpectedSchemata() {
|
||||||
$databases = $this->getDatabaseNames();
|
$refs = $this->getRefs();
|
||||||
$info = $this->getAPI()->getCharsetInfo();
|
|
||||||
|
$schemata = array();
|
||||||
|
foreach ($refs as $ref) {
|
||||||
|
$schema = $this->loadExpectedSchemaForServer($ref);
|
||||||
|
$schemata[$schema->getRef()->getRefKey()] = $schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $schemata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadExpectedSchemaForServer(PhabricatorDatabaseRef $ref) {
|
||||||
|
$databases = $this->getDatabaseNames($ref);
|
||||||
|
$info = $this->getAPI($ref)->getCharsetInfo();
|
||||||
|
|
||||||
$specs = id(new PhutilClassMapQuery())
|
$specs = id(new PhutilClassMapQuery())
|
||||||
->setAncestorClass('PhabricatorConfigSchemaSpec')
|
->setAncestorClass('PhabricatorConfigSchemaSpec')
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
$server_schema = new PhabricatorConfigServerSchema();
|
$server_schema = id(new PhabricatorConfigServerSchema())
|
||||||
|
->setRef($ref);
|
||||||
|
|
||||||
foreach ($specs as $spec) {
|
foreach ($specs as $spec) {
|
||||||
$spec
|
$spec
|
||||||
->setUTF8Charset(
|
->setUTF8Charset(
|
||||||
|
@ -201,7 +250,21 @@ final class PhabricatorConfigSchemaQuery extends Phobject {
|
||||||
return $server_schema;
|
return $server_schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildComparisonSchema(
|
public function buildComparisonSchemata(
|
||||||
|
array $expect_servers,
|
||||||
|
array $actual_servers) {
|
||||||
|
|
||||||
|
$schemata = array();
|
||||||
|
foreach ($actual_servers as $key => $actual_server) {
|
||||||
|
$schemata[$key] = $this->buildComparisonSchemaForServer(
|
||||||
|
$expect_servers[$key],
|
||||||
|
$actual_server);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $schemata;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildComparisonSchemaForServer(
|
||||||
PhabricatorConfigServerSchema $expect,
|
PhabricatorConfigServerSchema $expect,
|
||||||
PhabricatorConfigServerSchema $actual) {
|
PhabricatorConfigServerSchema $actual) {
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,18 @@
|
||||||
final class PhabricatorConfigServerSchema
|
final class PhabricatorConfigServerSchema
|
||||||
extends PhabricatorConfigStorageSchema {
|
extends PhabricatorConfigStorageSchema {
|
||||||
|
|
||||||
|
private $ref;
|
||||||
private $databases = array();
|
private $databases = array();
|
||||||
|
|
||||||
|
public function setRef(PhabricatorDatabaseRef $ref) {
|
||||||
|
$this->ref = $ref;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRef() {
|
||||||
|
return $this->ref;
|
||||||
|
}
|
||||||
|
|
||||||
public function addDatabase(PhabricatorConfigDatabaseSchema $database) {
|
public function addDatabase(PhabricatorConfigDatabaseSchema $database) {
|
||||||
$key = $database->getName();
|
$key = $database->getName();
|
||||||
if (isset($this->databases[$key])) {
|
if (isset($this->databases[$key])) {
|
||||||
|
|
|
@ -29,18 +29,13 @@ final class DifferentialParseCommitMessageConduitAPIMethod
|
||||||
$corpus = $request->getValue('corpus');
|
$corpus = $request->getValue('corpus');
|
||||||
$is_partial = $request->getValue('partial');
|
$is_partial = $request->getValue('partial');
|
||||||
|
|
||||||
$revision = new DifferentialRevision();
|
|
||||||
|
|
||||||
$field_list = PhabricatorCustomField::getObjectFields(
|
$field_list = PhabricatorCustomField::getObjectFields(
|
||||||
$revision,
|
new DifferentialRevision(),
|
||||||
DifferentialCustomField::ROLE_COMMITMESSAGE);
|
DifferentialCustomField::ROLE_COMMITMESSAGE);
|
||||||
$field_list->setViewer($viewer);
|
$field_list->setViewer($viewer);
|
||||||
$field_map = mpull($field_list->getFields(), null, 'getFieldKeyForConduit');
|
$field_map = mpull($field_list->getFields(), null, 'getFieldKeyForConduit');
|
||||||
|
|
||||||
$this->errors = array();
|
$corpus_map = $this->parseCommitMessage($corpus);
|
||||||
|
|
||||||
$label_map = $this->buildLabelMap($field_list);
|
|
||||||
$corpus_map = $this->parseCommitMessage($corpus, $label_map);
|
|
||||||
|
|
||||||
$values = array();
|
$values = array();
|
||||||
foreach ($corpus_map as $field_key => $text_value) {
|
foreach ($corpus_map as $field_key => $text_value) {
|
||||||
|
@ -94,44 +89,12 @@ final class DifferentialParseCommitMessageConduitAPIMethod
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildLabelMap(PhabricatorCustomFieldList $field_list) {
|
private function parseCommitMessage($corpus) {
|
||||||
$label_map = array();
|
$viewer = $this->getViewer();
|
||||||
|
$parser = DifferentialCommitMessageParser::newStandardParser($viewer);
|
||||||
foreach ($field_list->getFields() as $key => $field) {
|
|
||||||
$labels = $field->getCommitMessageLabels();
|
|
||||||
$key = $field->getFieldKeyForConduit();
|
|
||||||
|
|
||||||
foreach ($labels as $label) {
|
|
||||||
$normal_label = DifferentialCommitMessageParser::normalizeFieldLabel(
|
|
||||||
$label);
|
|
||||||
if (!empty($label_map[$normal_label])) {
|
|
||||||
throw new Exception(
|
|
||||||
pht(
|
|
||||||
'Field label "%s" is parsed by two custom fields: "%s" and '.
|
|
||||||
'"%s". Each label must be parsed by only one field.',
|
|
||||||
$label,
|
|
||||||
$key,
|
|
||||||
$label_map[$normal_label]));
|
|
||||||
}
|
|
||||||
$label_map[$normal_label] = $key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $label_map;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private function parseCommitMessage($corpus, array $label_map) {
|
|
||||||
$key_title = id(new DifferentialTitleField())->getFieldKeyForConduit();
|
|
||||||
$key_summary = id(new DifferentialSummaryField())->getFieldKeyForConduit();
|
|
||||||
|
|
||||||
$parser = id(new DifferentialCommitMessageParser())
|
|
||||||
->setLabelMap($label_map)
|
|
||||||
->setTitleKey($key_title)
|
|
||||||
->setSummaryKey($key_summary);
|
|
||||||
|
|
||||||
$result = $parser->parseCorpus($corpus);
|
$result = $parser->parseCorpus($corpus);
|
||||||
|
|
||||||
|
$this->errors = array();
|
||||||
foreach ($parser->getErrors() as $error) {
|
foreach ($parser->getErrors() as $error) {
|
||||||
$this->errors[] = $error;
|
$this->errors[] = $error;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ abstract class DifferentialCoreCustomField
|
||||||
|
|
||||||
private $value;
|
private $value;
|
||||||
private $fieldError;
|
private $fieldError;
|
||||||
|
private $fieldParser;
|
||||||
|
|
||||||
abstract protected function readValueFromRevision(
|
abstract protected function readValueFromRevision(
|
||||||
DifferentialRevision $revision);
|
DifferentialRevision $revision);
|
||||||
|
@ -60,6 +61,32 @@ abstract class DifferentialCoreCustomField
|
||||||
$error->setIsMissingFieldError(true);
|
$error->setIsMissingFieldError(true);
|
||||||
$errors[] = $error;
|
$errors[] = $error;
|
||||||
$this->setFieldError(pht('Required'));
|
$this->setFieldError(pht('Required'));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_string($value)) {
|
||||||
|
$parser = $this->getFieldParser();
|
||||||
|
$result = $parser->parseCorpus($value);
|
||||||
|
|
||||||
|
unset($result['__title__']);
|
||||||
|
unset($result['__summary__']);
|
||||||
|
|
||||||
|
if ($result) {
|
||||||
|
$error = new PhabricatorApplicationTransactionValidationError(
|
||||||
|
$type,
|
||||||
|
pht('Invalid'),
|
||||||
|
pht(
|
||||||
|
'The value you have entered in "%s" can not be parsed '.
|
||||||
|
'unambiguously when rendered in a commit message. Edit the '.
|
||||||
|
'message so that keywords like "Summary:" and "Test Plan:" do '.
|
||||||
|
'not appear at the beginning of lines. Parsed keys: %s.',
|
||||||
|
$this->getFieldName(),
|
||||||
|
implode(', ', array_keys($result))),
|
||||||
|
$xaction);
|
||||||
|
$errors[] = $error;
|
||||||
|
$this->setFieldError(pht('Invalid'));
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,6 +94,22 @@ abstract class DifferentialCoreCustomField
|
||||||
return $errors;
|
return $errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getFieldParser() {
|
||||||
|
if (!$this->fieldParser) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$parser = DifferentialCommitMessageParser::newStandardParser($viewer);
|
||||||
|
|
||||||
|
// Set custom title and summary keys so we can detect the presence of
|
||||||
|
// "Summary:" in, e.g., a test plan.
|
||||||
|
$parser->setTitleKey('__title__');
|
||||||
|
$parser->setSummaryKey('__summary__');
|
||||||
|
|
||||||
|
$this->fieldParser = $parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->fieldParser;
|
||||||
|
}
|
||||||
|
|
||||||
public function canDisableField() {
|
public function canDisableField() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,45 @@ final class DifferentialCommitMessageParser extends Phobject {
|
||||||
private $errors;
|
private $errors;
|
||||||
|
|
||||||
|
|
||||||
|
public static function newStandardParser(PhabricatorUser $viewer) {
|
||||||
|
|
||||||
|
$key_title = id(new DifferentialTitleField())->getFieldKeyForConduit();
|
||||||
|
$key_summary = id(new DifferentialSummaryField())->getFieldKeyForConduit();
|
||||||
|
|
||||||
|
$field_list = PhabricatorCustomField::getObjectFields(
|
||||||
|
new DifferentialRevision(),
|
||||||
|
DifferentialCustomField::ROLE_COMMITMESSAGE);
|
||||||
|
$field_list->setViewer($viewer);
|
||||||
|
|
||||||
|
$label_map = array();
|
||||||
|
|
||||||
|
foreach ($field_list->getFields() as $field) {
|
||||||
|
$labels = $field->getCommitMessageLabels();
|
||||||
|
$key = $field->getFieldKeyForConduit();
|
||||||
|
|
||||||
|
foreach ($labels as $label) {
|
||||||
|
$normal_label = self::normalizeFieldLabel(
|
||||||
|
$label);
|
||||||
|
if (!empty($label_map[$normal_label])) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Field label "%s" is parsed by two custom fields: "%s" and '.
|
||||||
|
'"%s". Each label must be parsed by only one field.',
|
||||||
|
$label,
|
||||||
|
$key,
|
||||||
|
$label_map[$normal_label]));
|
||||||
|
}
|
||||||
|
$label_map[$normal_label] = $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return id(new self())
|
||||||
|
->setLabelMap($label_map)
|
||||||
|
->setTitleKey($key_title)
|
||||||
|
->setSummaryKey($key_summary);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Configuring the Parser )--------------------------------------------- */
|
/* -( Configuring the Parser )--------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ final class DifferentialExactUserFunctionDatasource
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDatasourceApplicationClass() {
|
public function getDatasourceApplicationClass() {
|
||||||
return 'PhabricatorDifferentialApplication';
|
return 'PhabricatorPeopleApplication';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getComponentDatasources() {
|
public function getComponentDatasources() {
|
||||||
|
|
|
@ -46,6 +46,53 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRoutes() {
|
public function getRoutes() {
|
||||||
|
$repository_routes = array(
|
||||||
|
'/' => array(
|
||||||
|
'' => 'DiffusionRepositoryController',
|
||||||
|
'repository/(?P<dblob>.*)' => 'DiffusionRepositoryController',
|
||||||
|
'change/(?P<dblob>.*)' => 'DiffusionChangeController',
|
||||||
|
'history/(?P<dblob>.*)' => 'DiffusionHistoryController',
|
||||||
|
'browse/(?P<dblob>.*)' => 'DiffusionBrowseController',
|
||||||
|
'lastmodified/(?P<dblob>.*)' => 'DiffusionLastModifiedController',
|
||||||
|
'diff/' => 'DiffusionDiffController',
|
||||||
|
'tags/(?P<dblob>.*)' => 'DiffusionTagListController',
|
||||||
|
'branches/(?P<dblob>.*)' => 'DiffusionBranchTableController',
|
||||||
|
'refs/(?P<dblob>.*)' => 'DiffusionRefTableController',
|
||||||
|
'lint/(?P<dblob>.*)' => 'DiffusionLintController',
|
||||||
|
'commit/(?P<commit>[a-z0-9]+)/branches/'
|
||||||
|
=> 'DiffusionCommitBranchesController',
|
||||||
|
'commit/(?P<commit>[a-z0-9]+)/tags/'
|
||||||
|
=> 'DiffusionCommitTagsController',
|
||||||
|
'commit/(?P<commit>[a-z0-9]+)/edit/'
|
||||||
|
=> 'DiffusionCommitEditController',
|
||||||
|
'manage/(?:(?P<panel>[^/]+)/)?'
|
||||||
|
=> 'DiffusionRepositoryManagePanelsController',
|
||||||
|
'uri/' => array(
|
||||||
|
'view/(?P<id>[0-9]\d*)/' => 'DiffusionRepositoryURIViewController',
|
||||||
|
'disable/(?P<id>[0-9]\d*)/'
|
||||||
|
=> 'DiffusionRepositoryURIDisableController',
|
||||||
|
$this->getEditRoutePattern('edit/')
|
||||||
|
=> 'DiffusionRepositoryURIEditController',
|
||||||
|
'credential/(?P<id>[0-9]\d*)/(?P<action>edit|remove)/'
|
||||||
|
=> 'DiffusionRepositoryURICredentialController',
|
||||||
|
),
|
||||||
|
'edit/' => array(
|
||||||
|
'activate/' => 'DiffusionRepositoryEditActivateController',
|
||||||
|
'dangerous/' => 'DiffusionRepositoryEditDangerousController',
|
||||||
|
'delete/' => 'DiffusionRepositoryEditDeleteController',
|
||||||
|
'update/' => 'DiffusionRepositoryEditUpdateController',
|
||||||
|
'testautomation/' => 'DiffusionRepositoryTestAutomationController',
|
||||||
|
),
|
||||||
|
'pathtree/(?P<dblob>.*)' => 'DiffusionPathTreeController',
|
||||||
|
),
|
||||||
|
|
||||||
|
// NOTE: This must come after the rules above; it just gives us a
|
||||||
|
// catch-all for serving repositories over HTTP. We must accept requests
|
||||||
|
// without the trailing "/" because SVN commands don't necessarily
|
||||||
|
// include it.
|
||||||
|
'(?:/.*)?' => 'DiffusionRepositoryDefaultController',
|
||||||
|
);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'/(?:'.
|
'/(?:'.
|
||||||
'r(?P<repositoryCallsign>[A-Z]+)'.
|
'r(?P<repositoryCallsign>[A-Z]+)'.
|
||||||
|
@ -54,6 +101,9 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
|
||||||
')(?P<commit>[a-f0-9]+)'
|
')(?P<commit>[a-f0-9]+)'
|
||||||
=> 'DiffusionCommitController',
|
=> 'DiffusionCommitController',
|
||||||
|
|
||||||
|
'/source/(?P<repositoryShortName>[^/.]+)(?P<dotgit>\.git)?'
|
||||||
|
=> $repository_routes,
|
||||||
|
|
||||||
'/diffusion/' => array(
|
'/diffusion/' => array(
|
||||||
$this->getQueryRoutePattern()
|
$this->getQueryRoutePattern()
|
||||||
=> 'DiffusionRepositoryListController',
|
=> 'DiffusionRepositoryListController',
|
||||||
|
@ -63,57 +113,8 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
|
||||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DiffusionPushLogListController',
|
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DiffusionPushLogListController',
|
||||||
'view/(?P<id>\d+)/' => 'DiffusionPushEventViewController',
|
'view/(?P<id>\d+)/' => 'DiffusionPushEventViewController',
|
||||||
),
|
),
|
||||||
'(?:'.
|
'(?P<repositoryCallsign>[A-Z]+)' => $repository_routes,
|
||||||
'(?P<repositoryCallsign>[A-Z]+)'.
|
'(?P<repositoryID>[1-9]\d*)' => $repository_routes,
|
||||||
'|'.
|
|
||||||
'(?P<repositoryID>[1-9]\d*)'.
|
|
||||||
')/' => array(
|
|
||||||
'' => 'DiffusionRepositoryController',
|
|
||||||
|
|
||||||
'repository/(?P<dblob>.*)' => 'DiffusionRepositoryController',
|
|
||||||
'change/(?P<dblob>.*)' => 'DiffusionChangeController',
|
|
||||||
'history/(?P<dblob>.*)' => 'DiffusionHistoryController',
|
|
||||||
'browse/(?P<dblob>.*)' => 'DiffusionBrowseController',
|
|
||||||
'lastmodified/(?P<dblob>.*)' => 'DiffusionLastModifiedController',
|
|
||||||
'diff/' => 'DiffusionDiffController',
|
|
||||||
'tags/(?P<dblob>.*)' => 'DiffusionTagListController',
|
|
||||||
'branches/(?P<dblob>.*)' => 'DiffusionBranchTableController',
|
|
||||||
'refs/(?P<dblob>.*)' => 'DiffusionRefTableController',
|
|
||||||
'lint/(?P<dblob>.*)' => 'DiffusionLintController',
|
|
||||||
'commit/(?P<commit>[a-z0-9]+)/branches/'
|
|
||||||
=> 'DiffusionCommitBranchesController',
|
|
||||||
'commit/(?P<commit>[a-z0-9]+)/tags/'
|
|
||||||
=> 'DiffusionCommitTagsController',
|
|
||||||
'commit/(?P<commit>[a-z0-9]+)/edit/'
|
|
||||||
=> 'DiffusionCommitEditController',
|
|
||||||
'manage/(?:(?P<panel>[^/]+)/)?'
|
|
||||||
=> 'DiffusionRepositoryManagePanelsController',
|
|
||||||
'uri/' => array(
|
|
||||||
'view/(?P<id>[0-9]\d*)/' => 'DiffusionRepositoryURIViewController',
|
|
||||||
'disable/(?P<id>[0-9]\d*)/'
|
|
||||||
=> 'DiffusionRepositoryURIDisableController',
|
|
||||||
$this->getEditRoutePattern('edit/')
|
|
||||||
=> 'DiffusionRepositoryURIEditController',
|
|
||||||
'credential/(?P<id>[0-9]\d*)/(?P<action>edit|remove)/'
|
|
||||||
=> 'DiffusionRepositoryURICredentialController',
|
|
||||||
),
|
|
||||||
'edit/' => array(
|
|
||||||
'activate/' => 'DiffusionRepositoryEditActivateController',
|
|
||||||
'dangerous/' => 'DiffusionRepositoryEditDangerousController',
|
|
||||||
'delete/' => 'DiffusionRepositoryEditDeleteController',
|
|
||||||
'update/' => 'DiffusionRepositoryEditUpdateController',
|
|
||||||
'testautomation/' => 'DiffusionRepositoryTestAutomationController',
|
|
||||||
),
|
|
||||||
'pathtree/(?P<dblob>.*)' => 'DiffusionPathTreeController',
|
|
||||||
),
|
|
||||||
|
|
||||||
// NOTE: This must come after the rule above; it just gives us a
|
|
||||||
// catch-all for serving repositories over HTTP. We must accept
|
|
||||||
// requests without the trailing "/" because SVN commands don't
|
|
||||||
// necessarily include it.
|
|
||||||
'(?:(?P<repositoryCallsign>[A-Z]+)|(?P<repositoryID>[1-9]\d*))'.
|
|
||||||
'(?:/.*)?'
|
|
||||||
=> 'DiffusionRepositoryDefaultController',
|
|
||||||
|
|
||||||
'inline/' => array(
|
'inline/' => array(
|
||||||
'edit/(?P<phid>[^/]+)/' => 'DiffusionInlineCommentController',
|
'edit/(?P<phid>[^/]+)/' => 'DiffusionInlineCommentController',
|
||||||
|
|
|
@ -90,6 +90,11 @@ abstract class DiffusionController extends PhabricatorController {
|
||||||
protected function getRepositoryIdentifierFromRequest(
|
protected function getRepositoryIdentifierFromRequest(
|
||||||
AphrontRequest $request) {
|
AphrontRequest $request) {
|
||||||
|
|
||||||
|
$short_name = $request->getURIData('repositoryShortName');
|
||||||
|
if (strlen($short_name)) {
|
||||||
|
return $short_name;
|
||||||
|
}
|
||||||
|
|
||||||
$identifier = $request->getURIData('repositoryCallsign');
|
$identifier = $request->getURIData('repositoryCallsign');
|
||||||
if (strlen($identifier)) {
|
if (strlen($identifier)) {
|
||||||
return $identifier;
|
return $identifier;
|
||||||
|
|
|
@ -16,6 +16,7 @@ final class DiffusionLastModifiedController extends DiffusionController {
|
||||||
$drequest = $this->getDiffusionRequest();
|
$drequest = $this->getDiffusionRequest();
|
||||||
|
|
||||||
$paths = $request->getStr('paths');
|
$paths = $request->getStr('paths');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$paths = phutil_json_decode($paths);
|
$paths = phutil_json_decode($paths);
|
||||||
} catch (PhutilJSONParserException $ex) {
|
} catch (PhutilJSONParserException $ex) {
|
||||||
|
|
|
@ -88,6 +88,13 @@ final class DiffusionServeController extends DiffusionController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the request was for a path like "/source/libphutil.git" but the
|
||||||
|
// repository is not a Git repository, reject the request.
|
||||||
|
$type_git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
|
||||||
|
if ($request->getURIData('dotgit') && ($vcs !== $type_git)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return $vcs;
|
return $vcs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -607,7 +614,9 @@ final class DiffusionServeController extends DiffusionController {
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
$request_path = $request->getRequestURI()->getPath();
|
$request_path = $request->getRequestURI()->getPath();
|
||||||
|
|
||||||
$info = PhabricatorRepository::parseRepositoryServicePath($request_path);
|
$info = PhabricatorRepository::parseRepositoryServicePath(
|
||||||
|
$request_path,
|
||||||
|
$repository->getVersionControlSystem());
|
||||||
$base_path = $info['path'];
|
$base_path = $info['path'];
|
||||||
|
|
||||||
// For Git repositories, strip an optional directory component if it
|
// For Git repositories, strip an optional directory component if it
|
||||||
|
|
|
@ -15,7 +15,9 @@ final class DiffusionGitLFSAuthenticateWorkflow
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function identifyRepository() {
|
protected function identifyRepository() {
|
||||||
return $this->loadRepositoryWithPath($this->getLFSPathArgument());
|
return $this->loadRepositoryWithPath(
|
||||||
|
$this->getLFSPathArgument(),
|
||||||
|
PhabricatorRepositoryType::REPOSITORY_TYPE_GIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getLFSPathArgument() {
|
private function getLFSPathArgument() {
|
||||||
|
|
|
@ -168,8 +168,7 @@ final class DiffusionRepositoryBasicsManagementPanel
|
||||||
|
|
||||||
$short_name = $repository->getRepositorySlug();
|
$short_name = $repository->getRepositorySlug();
|
||||||
if ($short_name === null) {
|
if ($short_name === null) {
|
||||||
$short_name = $repository->getCloneName();
|
$short_name = phutil_tag('em', array(), pht('No Short Name'));
|
||||||
$short_name = phutil_tag('em', array(), $short_name);
|
|
||||||
}
|
}
|
||||||
$view->addProperty(pht('Short Name'), $short_name);
|
$view->addProperty(pht('Short Name'), $short_name);
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,9 @@ abstract class DiffusionGitSSHWorkflow
|
||||||
protected function identifyRepository() {
|
protected function identifyRepository() {
|
||||||
$args = $this->getArgs();
|
$args = $this->getArgs();
|
||||||
$path = head($args->getArg('dir'));
|
$path = head($args->getArg('dir'));
|
||||||
return $this->loadRepositoryWithPath($path);
|
return $this->loadRepositoryWithPath(
|
||||||
|
$path,
|
||||||
|
PhabricatorRepositoryType::REPOSITORY_TYPE_GIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function waitForGitClient() {
|
protected function waitForGitClient() {
|
||||||
|
|
|
@ -27,7 +27,9 @@ final class DiffusionMercurialServeSSHWorkflow
|
||||||
protected function identifyRepository() {
|
protected function identifyRepository() {
|
||||||
$args = $this->getArgs();
|
$args = $this->getArgs();
|
||||||
$path = $args->getArg('repository');
|
$path = $args->getArg('repository');
|
||||||
return $this->loadRepositoryWithPath($path);
|
return $this->loadRepositoryWithPath(
|
||||||
|
$path,
|
||||||
|
PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function executeRepositoryOperations() {
|
protected function executeRepositoryOperations() {
|
||||||
|
|
|
@ -161,18 +161,19 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function loadRepositoryWithPath($path) {
|
protected function loadRepositoryWithPath($path, $vcs) {
|
||||||
$viewer = $this->getUser();
|
$viewer = $this->getUser();
|
||||||
|
|
||||||
$info = PhabricatorRepository::parseRepositoryServicePath($path);
|
$info = PhabricatorRepository::parseRepositoryServicePath($path, $vcs);
|
||||||
if ($info === null) {
|
if ($info === null) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht(
|
pht(
|
||||||
'Unrecognized repository path "%s". Expected a path like "%s" '.
|
'Unrecognized repository path "%s". Expected a path like "%s", '.
|
||||||
'or "%s".',
|
'"%s", or "%s".',
|
||||||
$path,
|
$path,
|
||||||
'/diffusion/X/',
|
'/diffusion/X/',
|
||||||
'/diffusion/123/'));
|
'/diffusion/123/',
|
||||||
|
'/source/thaumaturgy.git'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$identifier = $info['identifier'];
|
$identifier = $info['identifier'];
|
||||||
|
|
|
@ -117,7 +117,9 @@ final class DiffusionSubversionServeSSHWorkflow
|
||||||
$uri = $struct[2]['value'];
|
$uri = $struct[2]['value'];
|
||||||
$path = $this->getPathFromSubversionURI($uri);
|
$path = $this->getPathFromSubversionURI($uri);
|
||||||
|
|
||||||
return $this->loadRepositoryWithPath($path);
|
return $this->loadRepositoryWithPath(
|
||||||
|
$path,
|
||||||
|
PhabricatorRepositoryType::REPOSITORY_TYPE_SVN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,7 @@ final class PhabricatorFilesApplication extends PhabricatorApplication {
|
||||||
'dropupload/' => 'PhabricatorFileDropUploadController',
|
'dropupload/' => 'PhabricatorFileDropUploadController',
|
||||||
'compose/' => 'PhabricatorFileComposeController',
|
'compose/' => 'PhabricatorFileComposeController',
|
||||||
'comment/(?P<id>[1-9]\d*)/' => 'PhabricatorFileCommentController',
|
'comment/(?P<id>[1-9]\d*)/' => 'PhabricatorFileCommentController',
|
||||||
|
'thread/(?P<phid>[^/]+)/' => 'PhabricatorFileLightboxController',
|
||||||
'delete/(?P<id>[1-9]\d*)/' => 'PhabricatorFileDeleteController',
|
'delete/(?P<id>[1-9]\d*)/' => 'PhabricatorFileDeleteController',
|
||||||
'edit/(?P<id>[1-9]\d*)/' => 'PhabricatorFileEditController',
|
'edit/(?P<id>[1-9]\d*)/' => 'PhabricatorFileEditController',
|
||||||
'info/(?P<phid>[^/]+)/' => 'PhabricatorFileInfoController',
|
'info/(?P<phid>[^/]+)/' => 'PhabricatorFileInfoController',
|
||||||
|
@ -129,4 +130,10 @@ final class PhabricatorFilesApplication extends PhabricatorApplication {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getQuicksandURIPatternBlacklist() {
|
||||||
|
return array(
|
||||||
|
'/file/data/.*',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorFileLightboxController
|
||||||
|
extends PhabricatorFileController {
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$viewer = $request->getViewer();
|
||||||
|
$phid = $request->getURIData('phid');
|
||||||
|
|
||||||
|
$file = id(new PhabricatorFileQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs(array($phid))
|
||||||
|
->executeOne();
|
||||||
|
if (!$file) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$transactions = id(new PhabricatorFileTransactionQuery())
|
||||||
|
->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT));
|
||||||
|
$timeline = $this->buildTransactionTimeline($file, $transactions);
|
||||||
|
|
||||||
|
if ($timeline->isTimelineEmpty()) {
|
||||||
|
$timeline = phutil_tag(
|
||||||
|
'div',
|
||||||
|
array(
|
||||||
|
'class' => 'phui-comment-panel-empty',
|
||||||
|
),
|
||||||
|
pht('No comments.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
require_celerity_resource('phui-comment-panel-css');
|
||||||
|
$content = phutil_tag(
|
||||||
|
'div',
|
||||||
|
array(
|
||||||
|
'class' => 'phui-comment-panel',
|
||||||
|
),
|
||||||
|
$timeline);
|
||||||
|
|
||||||
|
return id(new AphrontAjaxResponse())
|
||||||
|
->setContent($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -98,7 +98,7 @@ final class PhabricatorEmbedFileRemarkupRule
|
||||||
PhabricatorObjectHandle $handle,
|
PhabricatorObjectHandle $handle,
|
||||||
array $options) {
|
array $options) {
|
||||||
|
|
||||||
require_celerity_resource('lightbox-attachment-css');
|
require_celerity_resource('phui-lightbox-css');
|
||||||
|
|
||||||
$attrs = array();
|
$attrs = array();
|
||||||
$image_class = 'phabricator-remarkup-embed-image';
|
$image_class = 'phabricator-remarkup-embed-image';
|
||||||
|
@ -176,6 +176,7 @@ final class PhabricatorEmbedFileRemarkupRule
|
||||||
'uri' => $file->getBestURI(),
|
'uri' => $file->getBestURI(),
|
||||||
'dUri' => $file->getDownloadURI(),
|
'dUri' => $file->getDownloadURI(),
|
||||||
'viewable' => true,
|
'viewable' => true,
|
||||||
|
'monogram' => $file->getMonogram(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
$img);
|
$img);
|
||||||
|
@ -279,7 +280,8 @@ final class PhabricatorEmbedFileRemarkupRule
|
||||||
->setFileName($this->assertFlatText($options['name']))
|
->setFileName($this->assertFlatText($options['name']))
|
||||||
->setFileDownloadURI($file->getDownloadURI())
|
->setFileDownloadURI($file->getDownloadURI())
|
||||||
->setFileViewURI($file->getBestURI())
|
->setFileViewURI($file->getBestURI())
|
||||||
->setFileViewable((bool)$options['viewable']);
|
->setFileViewable((bool)$options['viewable'])
|
||||||
|
->setFileMonogram($file->getMonogram());
|
||||||
}
|
}
|
||||||
|
|
||||||
private function parseDimension($string) {
|
private function parseDimension($string) {
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class PhabricatorGuidanceContext
|
||||||
|
extends Phobject {}
|
|
@ -0,0 +1,96 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorGuidanceEngine
|
||||||
|
extends Phobject {
|
||||||
|
|
||||||
|
private $viewer;
|
||||||
|
private $guidanceContext;
|
||||||
|
|
||||||
|
public function setGuidanceContext(
|
||||||
|
PhabricatorGuidanceContext $guidance_context) {
|
||||||
|
$this->guidanceContext = $guidance_context;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGuidanceContext() {
|
||||||
|
return $this->guidanceContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setViewer(PhabricatorUser $viewer) {
|
||||||
|
$this->viewer = $viewer;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getViewer() {
|
||||||
|
return $this->viewer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newInfoView() {
|
||||||
|
$extensions = PhabricatorGuidanceEngineExtension::getAllExtensions();
|
||||||
|
$context = $this->getGuidanceContext();
|
||||||
|
|
||||||
|
$keep = array();
|
||||||
|
foreach ($extensions as $key => $extension) {
|
||||||
|
if (!$extension->canGenerateGuidance($context)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$keep[$key] = id(clone $extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
$guidance_map = array();
|
||||||
|
foreach ($keep as $extension) {
|
||||||
|
$guidance_list = $extension->generateGuidance($context);
|
||||||
|
foreach ($guidance_list as $guidance) {
|
||||||
|
$key = $guidance->getKey();
|
||||||
|
|
||||||
|
if (isset($guidance_map[$key])) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Two guidance extensions generated guidance with the same '.
|
||||||
|
'key ("%s"). Each piece of guidance must have a unique key.',
|
||||||
|
$key));
|
||||||
|
}
|
||||||
|
|
||||||
|
$guidance_map[$key] = $guidance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($keep as $extension) {
|
||||||
|
$guidance_map = $extension->didGenerateGuidance($context, $guidance_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$guidance_map) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$guidance_map = msortv($guidance_map, 'getSortVector');
|
||||||
|
|
||||||
|
$severity = PhabricatorGuidanceMessage::SEVERITY_NOTICE;
|
||||||
|
$strength = null;
|
||||||
|
foreach ($guidance_map as $guidance) {
|
||||||
|
if ($strength !== null) {
|
||||||
|
if ($guidance->getSeverityStrength() <= $strength) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$strength = $guidance->getSeverityStrength();
|
||||||
|
$severity = $guidance->getSeverity();
|
||||||
|
}
|
||||||
|
|
||||||
|
$severity_map = array(
|
||||||
|
PhabricatorGuidanceMessage::SEVERITY_NOTICE
|
||||||
|
=> PHUIInfoView::SEVERITY_NOTICE,
|
||||||
|
PhabricatorGuidanceMessage::SEVERITY_WARNING
|
||||||
|
=> PHUIInfoView::SEVERITY_WARNING,
|
||||||
|
);
|
||||||
|
|
||||||
|
$messages = mpull($guidance_map, 'getMessage', 'getKey');
|
||||||
|
|
||||||
|
return id(new PHUIInfoView())
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->setSeverity(idx($severity_map, $severity, $severity))
|
||||||
|
->setErrors($messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class PhabricatorGuidanceEngineExtension
|
||||||
|
extends Phobject {
|
||||||
|
|
||||||
|
final public function getExtensionKey() {
|
||||||
|
return $this->getPhobjectClassConstant('GUIDANCEKEY', 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function canGenerateGuidance(
|
||||||
|
PhabricatorGuidanceContext $context);
|
||||||
|
|
||||||
|
abstract public function generateGuidance(
|
||||||
|
PhabricatorGuidanceContext $context);
|
||||||
|
|
||||||
|
public function didGenerateGuidance(
|
||||||
|
PhabricatorGuidanceContext $context,
|
||||||
|
array $guidance) {
|
||||||
|
return $guidance;
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected function newGuidance($key) {
|
||||||
|
return id(new PhabricatorGuidanceMessage())
|
||||||
|
->setKey($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected function newWarning($key) {
|
||||||
|
return $this->newGuidance($key)
|
||||||
|
->setSeverity(PhabricatorGuidanceMessage::SEVERITY_WARNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
final public static function getAllExtensions() {
|
||||||
|
return id(new PhutilClassMapQuery())
|
||||||
|
->setAncestorClass(__CLASS__)
|
||||||
|
->setUniqueMethod('getExtensionKey')
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorGuidanceMessage
|
||||||
|
extends Phobject {
|
||||||
|
|
||||||
|
private $key;
|
||||||
|
private $message;
|
||||||
|
private $severity = self::SEVERITY_NOTICE;
|
||||||
|
private $priority = 1000;
|
||||||
|
|
||||||
|
const SEVERITY_NOTICE = 'notice';
|
||||||
|
const SEVERITY_WARNING = 'warning';
|
||||||
|
|
||||||
|
public function setSeverity($severity) {
|
||||||
|
$this->severity = $severity;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSeverity() {
|
||||||
|
return $this->severity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setKey($key) {
|
||||||
|
$this->key = $key;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKey() {
|
||||||
|
return $this->key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMessage($message) {
|
||||||
|
$this->message = $message;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMessage() {
|
||||||
|
return $this->message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSortVector() {
|
||||||
|
return id(new PhutilSortVector())
|
||||||
|
->addInt($this->getPriority());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPriority($priority) {
|
||||||
|
$this->priority = $priority;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPriority() {
|
||||||
|
return $this->priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSeverityStrength() {
|
||||||
|
$map = array(
|
||||||
|
self::SEVERITY_NOTICE => 1,
|
||||||
|
self::SEVERITY_WARNING => 2,
|
||||||
|
);
|
||||||
|
|
||||||
|
return idx($map, $this->getSeverity(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -16,6 +16,10 @@ abstract class HarbormasterArtifact extends Phobject {
|
||||||
abstract public function getArtifactParameterDescriptions();
|
abstract public function getArtifactParameterDescriptions();
|
||||||
abstract public function willCreateArtifact(PhabricatorUser $actor);
|
abstract public function willCreateArtifact(PhabricatorUser $actor);
|
||||||
|
|
||||||
|
public function readArtifactHTTPParameter($key, $value) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
public function validateArtifactData(array $artifact_data) {
|
public function validateArtifactData(array $artifact_data) {
|
||||||
$artifact_spec = $this->getArtifactParameterSpecification();
|
$artifact_spec = $this->getArtifactParameterSpecification();
|
||||||
PhutilTypeSpec::checkMap($artifact_data, $artifact_spec);
|
PhutilTypeSpec::checkMap($artifact_data, $artifact_spec);
|
||||||
|
|
|
@ -27,6 +27,16 @@ final class HarbormasterURIArtifact extends HarbormasterArtifact {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function readArtifactHTTPParameter($key, $value) {
|
||||||
|
// TODO: This is hacky and artifact parameters should be replaced more
|
||||||
|
// broadly, likely with EditFields. See T11887.
|
||||||
|
switch ($key) {
|
||||||
|
case 'ui.external':
|
||||||
|
return (bool)$value;
|
||||||
|
}
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
public function getArtifactParameterDescriptions() {
|
public function getArtifactParameterDescriptions() {
|
||||||
return array(
|
return array(
|
||||||
'uri' => pht('The URI to store.'),
|
'uri' => pht('The URI to store.'),
|
||||||
|
|
|
@ -115,11 +115,28 @@ final class HarbormasterCreateArtifactConduitAPIMethod
|
||||||
$build_target_phid));
|
$build_target_phid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$artifact_type = $request->getValue('artifactType');
|
||||||
|
|
||||||
|
// Cast "artifactData" parameters to acceptable types if this request
|
||||||
|
// is submitting raw HTTP parameters. This is not ideal. See T11887 for
|
||||||
|
// discussion.
|
||||||
|
$artifact_data = $request->getValue('artifactData');
|
||||||
|
if (!$request->getIsStrictlyTyped()) {
|
||||||
|
$impl = HarbormasterArtifact::getArtifactType($artifact_type);
|
||||||
|
if ($impl) {
|
||||||
|
foreach ($artifact_data as $key => $value) {
|
||||||
|
$artifact_data[$key] = $impl->readArtifactHTTPParameter(
|
||||||
|
$key,
|
||||||
|
$value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$artifact = $build_target->createArtifact(
|
$artifact = $build_target->createArtifact(
|
||||||
$viewer,
|
$viewer,
|
||||||
$request->getValue('artifactKey'),
|
$request->getValue('artifactKey'),
|
||||||
$request->getValue('artifactType'),
|
$artifact_type,
|
||||||
$request->getValue('artifactData'));
|
$artifact_data);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'data' => $this->returnArtifactList(array($artifact)),
|
'data' => $this->returnArtifactList(array($artifact)),
|
||||||
|
|
|
@ -510,15 +510,23 @@ final class ManiphestTaskDetailController extends ManiphestController {
|
||||||
}
|
}
|
||||||
|
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
|
$in_handles = $viewer->loadHandles($in_phids);
|
||||||
|
$out_handles = $viewer->loadHandles($out_phids);
|
||||||
|
|
||||||
|
$in_handles = $this->getCompleteHandles($in_handles);
|
||||||
|
$out_handles = $this->getCompleteHandles($out_handles);
|
||||||
|
|
||||||
|
if (!count($in_handles) && !count($out_handles)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
$view = new PHUIPropertyListView();
|
$view = new PHUIPropertyListView();
|
||||||
|
|
||||||
if ($in_phids) {
|
if (count($in_handles)) {
|
||||||
$in_handles = $viewer->loadHandles($in_phids);
|
|
||||||
$view->addProperty(pht('Mentioned In'), $in_handles->renderList());
|
$view->addProperty(pht('Mentioned In'), $in_handles->renderList());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($out_phids) {
|
if (count($out_handles)) {
|
||||||
$out_handles = $viewer->loadHandles($out_phids);
|
|
||||||
$view->addProperty(pht('Mentioned Here'), $out_handles->renderList());
|
$view->addProperty(pht('Mentioned Here'), $out_handles->renderList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -528,4 +536,18 @@ final class ManiphestTaskDetailController extends ManiphestController {
|
||||||
->appendChild($view);
|
->appendChild($view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getCompleteHandles(PhabricatorHandleList $handles) {
|
||||||
|
$phids = array();
|
||||||
|
|
||||||
|
foreach ($handles as $phid => $handle) {
|
||||||
|
if (!$handle->isComplete()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$phids[] = $phid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $handles->newSublist($phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,7 @@ final class PassphraseQueryConduitAPIMethod
|
||||||
|
|
||||||
switch ($credential->getCredentialType()) {
|
switch ($credential->getCredentialType()) {
|
||||||
case PassphraseSSHPrivateKeyFileCredentialType::CREDENTIAL_TYPE:
|
case PassphraseSSHPrivateKeyFileCredentialType::CREDENTIAL_TYPE:
|
||||||
if ($secret) {
|
if ($secret !== null) {
|
||||||
$material['file'] = $secret;
|
$material['file'] = $secret;
|
||||||
}
|
}
|
||||||
if ($public_key) {
|
if ($public_key) {
|
||||||
|
@ -91,7 +91,7 @@ final class PassphraseQueryConduitAPIMethod
|
||||||
break;
|
break;
|
||||||
case PassphraseSSHGeneratedKeyCredentialType::CREDENTIAL_TYPE:
|
case PassphraseSSHGeneratedKeyCredentialType::CREDENTIAL_TYPE:
|
||||||
case PassphraseSSHPrivateKeyTextCredentialType::CREDENTIAL_TYPE:
|
case PassphraseSSHPrivateKeyTextCredentialType::CREDENTIAL_TYPE:
|
||||||
if ($secret) {
|
if ($secret !== null) {
|
||||||
$material['privateKey'] = $secret;
|
$material['privateKey'] = $secret;
|
||||||
}
|
}
|
||||||
if ($public_key) {
|
if ($public_key) {
|
||||||
|
@ -99,10 +99,15 @@ final class PassphraseQueryConduitAPIMethod
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PassphrasePasswordCredentialType::CREDENTIAL_TYPE:
|
case PassphrasePasswordCredentialType::CREDENTIAL_TYPE:
|
||||||
if ($secret) {
|
if ($secret !== null) {
|
||||||
$material['password'] = $secret;
|
$material['password'] = $secret;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case PassphraseTokenCredentialType::CREDENTIAL_TYPE:
|
||||||
|
if ($secret !== null) {
|
||||||
|
$material['token'] = $secret;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$allow_api) {
|
if (!$allow_api) {
|
||||||
|
|
|
@ -101,9 +101,20 @@ final class PhabricatorPeopleCreateController
|
||||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||||
->setForm($form);
|
->setForm($form);
|
||||||
|
|
||||||
|
$guidance_context = new PhabricatorPeopleCreateGuidanceContext();
|
||||||
|
|
||||||
|
$guidance = id(new PhabricatorGuidanceEngine())
|
||||||
|
->setViewer($admin)
|
||||||
|
->setGuidanceContext($guidance_context)
|
||||||
|
->newInfoView();
|
||||||
|
|
||||||
$view = id(new PHUITwoColumnView())
|
$view = id(new PHUITwoColumnView())
|
||||||
->setHeader($header)
|
->setHeader($header)
|
||||||
->setFooter($box);
|
->setFooter(
|
||||||
|
array(
|
||||||
|
$guidance,
|
||||||
|
$box,
|
||||||
|
));
|
||||||
|
|
||||||
return $this->newPage()
|
return $this->newPage()
|
||||||
->setTitle($title)
|
->setTitle($title)
|
||||||
|
|
|
@ -194,11 +194,12 @@ final class PhabricatorPeopleProfileViewController
|
||||||
->withDateRange($range_start, $range_end)
|
->withDateRange($range_start, $range_end)
|
||||||
->withInvitedPHIDs(array($user->getPHID()))
|
->withInvitedPHIDs(array($user->getPHID()))
|
||||||
->withIsCancelled(false)
|
->withIsCancelled(false)
|
||||||
|
->needRSVPs(array($viewer->getPHID()))
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
$event_views = array();
|
$event_views = array();
|
||||||
foreach ($events as $event) {
|
foreach ($events as $event) {
|
||||||
$viewer_is_invited = $event->getIsUserInvited($viewer->getPHID());
|
$viewer_is_invited = $event->isRSVPInvited($viewer->getPHID());
|
||||||
|
|
||||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||||
$viewer,
|
$viewer,
|
||||||
|
@ -216,6 +217,7 @@ final class PhabricatorPeopleProfileViewController
|
||||||
->setIcon($event->getIcon())
|
->setIcon($event->getIcon())
|
||||||
->setViewerIsInvited($viewer_is_invited)
|
->setViewerIsInvited($viewer_is_invited)
|
||||||
->setName($event->getName())
|
->setName($event->getName())
|
||||||
|
->setDatetimeSummary($event->renderEventDate($viewer, true))
|
||||||
->setURI($event->getURI());
|
->setURI($event->getURI());
|
||||||
|
|
||||||
$event_views[] = $event_view;
|
$event_views[] = $event_view;
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorPeopleCreateGuidanceContext
|
||||||
|
extends PhabricatorGuidanceContext {}
|
|
@ -53,6 +53,7 @@ final class PhabricatorPhameApplication extends PhabricatorApplication {
|
||||||
'preview/' => 'PhabricatorMarkupPreviewController',
|
'preview/' => 'PhabricatorMarkupPreviewController',
|
||||||
'move/(?P<id>\d+)/' => 'PhamePostMoveController',
|
'move/(?P<id>\d+)/' => 'PhamePostMoveController',
|
||||||
'archive/(?P<id>\d+)/' => 'PhamePostArchiveController',
|
'archive/(?P<id>\d+)/' => 'PhamePostArchiveController',
|
||||||
|
'header/(?P<id>[1-9]\d*)/' => 'PhamePostHeaderPictureController',
|
||||||
),
|
),
|
||||||
'blog/' => array(
|
'blog/' => array(
|
||||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'PhameBlogListController',
|
'(?:query/(?P<queryKey>[^/]+)/)?' => 'PhameBlogListController',
|
||||||
|
|
|
@ -90,6 +90,7 @@ abstract class PhameLiveController extends PhameController {
|
||||||
if (strlen($post_id)) {
|
if (strlen($post_id)) {
|
||||||
$post_query = id(new PhamePostQuery())
|
$post_query = id(new PhamePostQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
|
->needHeaderImage(true)
|
||||||
->withIDs(array($post_id));
|
->withIDs(array($post_id));
|
||||||
|
|
||||||
if ($blog) {
|
if ($blog) {
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhamePostHeaderPictureController
|
||||||
|
extends PhamePostController {
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$viewer = $request->getViewer();
|
||||||
|
$id = $request->getURIData('id');
|
||||||
|
|
||||||
|
$post = id(new PhamePostQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(array($id))
|
||||||
|
->needHeaderImage(true)
|
||||||
|
->requireCapabilities(
|
||||||
|
array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
))
|
||||||
|
->executeOne();
|
||||||
|
if (!$post) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$post_uri = '/phame/post/view/'.$id;
|
||||||
|
|
||||||
|
$supported_formats = PhabricatorFile::getTransformableImageFormats();
|
||||||
|
$e_file = true;
|
||||||
|
$errors = array();
|
||||||
|
$delete_header = ($request->getInt('delete') == 1);
|
||||||
|
|
||||||
|
if ($request->isFormPost()) {
|
||||||
|
if ($request->getFileExists('header')) {
|
||||||
|
$file = PhabricatorFile::newFromPHPUpload(
|
||||||
|
$_FILES['header'],
|
||||||
|
array(
|
||||||
|
'authorPHID' => $viewer->getPHID(),
|
||||||
|
'canCDN' => true,
|
||||||
|
));
|
||||||
|
} else if (!$delete_header) {
|
||||||
|
$e_file = pht('Required');
|
||||||
|
$errors[] = pht(
|
||||||
|
'You must choose a file when uploading a new post header.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$errors && !$delete_header) {
|
||||||
|
if (!$file->isTransformableImage()) {
|
||||||
|
$e_file = pht('Not Supported');
|
||||||
|
$errors[] = pht(
|
||||||
|
'This server only supports these image formats: %s.',
|
||||||
|
implode(', ', $supported_formats));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$errors) {
|
||||||
|
if ($delete_header) {
|
||||||
|
$new_value = null;
|
||||||
|
} else {
|
||||||
|
$file->attachToObject($post->getPHID());
|
||||||
|
$new_value = $file->getPHID();
|
||||||
|
}
|
||||||
|
|
||||||
|
$xactions = array();
|
||||||
|
$xactions[] = id(new PhamePostTransaction())
|
||||||
|
->setTransactionType(PhamePostTransaction::TYPE_HEADERIMAGE)
|
||||||
|
->setNewValue($new_value);
|
||||||
|
|
||||||
|
$editor = id(new PhamePostEditor())
|
||||||
|
->setActor($viewer)
|
||||||
|
->setContentSourceFromRequest($request)
|
||||||
|
->setContinueOnMissingFields(true)
|
||||||
|
->setContinueOnNoEffect(true);
|
||||||
|
|
||||||
|
$editor->applyTransactions($post, $xactions);
|
||||||
|
|
||||||
|
return id(new AphrontRedirectResponse())->setURI($post_uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$title = pht('Edit Post Header');
|
||||||
|
|
||||||
|
$upload_form = id(new AphrontFormView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setEncType('multipart/form-data')
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormFileControl())
|
||||||
|
->setName('header')
|
||||||
|
->setLabel(pht('Upload Header'))
|
||||||
|
->setError($e_file)
|
||||||
|
->setCaption(
|
||||||
|
pht('Supported formats: %s', implode(', ', $supported_formats))))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormCheckboxControl())
|
||||||
|
->setName('delete')
|
||||||
|
->setLabel(pht('Delete Header'))
|
||||||
|
->addCheckbox(
|
||||||
|
'delete',
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
null))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormSubmitControl())
|
||||||
|
->addCancelButton($post_uri)
|
||||||
|
->setValue(pht('Upload Header')));
|
||||||
|
|
||||||
|
$upload_box = id(new PHUIObjectBoxView())
|
||||||
|
->setHeaderText(pht('Upload New Header'))
|
||||||
|
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||||
|
->setForm($upload_form);
|
||||||
|
|
||||||
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
|
$crumbs->addTextCrumb(
|
||||||
|
$post->getTitle(),
|
||||||
|
$this->getApplicationURI('post/view/'.$id));
|
||||||
|
$crumbs->addTextCrumb(pht('Post Header'));
|
||||||
|
$crumbs->setBorder(true);
|
||||||
|
|
||||||
|
$header = id(new PHUIHeaderView())
|
||||||
|
->setHeader(pht('Edit Post Header'))
|
||||||
|
->setHeaderIcon('fa-camera');
|
||||||
|
|
||||||
|
$view = id(new PHUITwoColumnView())
|
||||||
|
->setHeader($header)
|
||||||
|
->setFooter(array(
|
||||||
|
$upload_box,
|
||||||
|
));
|
||||||
|
|
||||||
|
return $this->newPage()
|
||||||
|
->setTitle($title)
|
||||||
|
->setCrumbs($crumbs)
|
||||||
|
->appendChild(
|
||||||
|
array(
|
||||||
|
$view,
|
||||||
|
));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,9 +19,11 @@ final class PhamePostViewController
|
||||||
$is_external = $this->getIsExternal();
|
$is_external = $this->getIsExternal();
|
||||||
|
|
||||||
$header = id(new PHUIHeaderView())
|
$header = id(new PHUIHeaderView())
|
||||||
->setHeader($post->getTitle())
|
->addClass('phame-header-bar')
|
||||||
->setUser($viewer);
|
->setUser($viewer);
|
||||||
|
|
||||||
|
$hero = $this->buildPhamePostHeader($post);
|
||||||
|
|
||||||
if (!$is_external) {
|
if (!$is_external) {
|
||||||
$actions = $this->renderActions($post);
|
$actions = $this->renderActions($post);
|
||||||
$header->setPolicyObject($post);
|
$header->setPolicyObject($post);
|
||||||
|
@ -167,6 +169,7 @@ final class PhamePostViewController
|
||||||
->setCrumbs($crumbs)
|
->setCrumbs($crumbs)
|
||||||
->appendChild(
|
->appendChild(
|
||||||
array(
|
array(
|
||||||
|
$hero,
|
||||||
$document,
|
$document,
|
||||||
$about,
|
$about,
|
||||||
$properties,
|
$properties,
|
||||||
|
@ -204,6 +207,13 @@ final class PhamePostViewController
|
||||||
->setName(pht('Edit Post'))
|
->setName(pht('Edit Post'))
|
||||||
->setDisabled(!$can_edit));
|
->setDisabled(!$can_edit));
|
||||||
|
|
||||||
|
$actions->addAction(
|
||||||
|
id(new PhabricatorActionView())
|
||||||
|
->setIcon('fa-camera-retro')
|
||||||
|
->setHref($this->getApplicationURI('post/header/'.$id.'/'))
|
||||||
|
->setName(pht('Edit Header Image'))
|
||||||
|
->setDisabled(!$can_edit));
|
||||||
|
|
||||||
$actions->addAction(
|
$actions->addAction(
|
||||||
id(new PhabricatorActionView())
|
id(new PhabricatorActionView())
|
||||||
->setIcon('fa-arrows')
|
->setIcon('fa-arrows')
|
||||||
|
@ -307,4 +317,33 @@ final class PhamePostViewController
|
||||||
return array(head($prev), head($next));
|
return array(head($prev), head($next));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function buildPhamePostHeader(
|
||||||
|
PhamePost $post) {
|
||||||
|
|
||||||
|
$image = null;
|
||||||
|
if ($post->getHeaderImagePHID()) {
|
||||||
|
$image = phutil_tag(
|
||||||
|
'div',
|
||||||
|
array(
|
||||||
|
'class' => 'phame-header-hero',
|
||||||
|
),
|
||||||
|
phutil_tag(
|
||||||
|
'img',
|
||||||
|
array(
|
||||||
|
'src' => $post->getHeaderImageURI(),
|
||||||
|
'class' => 'phame-header-image',
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$title = phutil_tag_div('phame-header-title', $post->getTitle());
|
||||||
|
$subtitle = null;
|
||||||
|
if ($post->getSubtitle()) {
|
||||||
|
$subtitle = phutil_tag_div('phame-header-subtitle', $post->getSubtitle());
|
||||||
|
}
|
||||||
|
|
||||||
|
return phutil_tag_div(
|
||||||
|
'phame-mega-header', array($image, $title, $subtitle));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,6 +99,14 @@ final class PhamePostEditEngine
|
||||||
->setConduitTypeDescription(pht('New post title.'))
|
->setConduitTypeDescription(pht('New post title.'))
|
||||||
->setTransactionType(PhamePostTransaction::TYPE_TITLE)
|
->setTransactionType(PhamePostTransaction::TYPE_TITLE)
|
||||||
->setValue($object->getTitle()),
|
->setValue($object->getTitle()),
|
||||||
|
id(new PhabricatorTextEditField())
|
||||||
|
->setKey('subtitle')
|
||||||
|
->setLabel(pht('Subtitle'))
|
||||||
|
->setDescription(pht('Post subtitle.'))
|
||||||
|
->setConduitDescription(pht('Change the post subtitle.'))
|
||||||
|
->setConduitTypeDescription(pht('New post subtitle.'))
|
||||||
|
->setTransactionType(PhamePostTransaction::TYPE_SUBTITLE)
|
||||||
|
->setValue($object->getSubtitle()),
|
||||||
id(new PhabricatorSelectEditField())
|
id(new PhabricatorSelectEditField())
|
||||||
->setKey('visibility')
|
->setKey('visibility')
|
||||||
->setLabel(pht('Visibility'))
|
->setLabel(pht('Visibility'))
|
||||||
|
|
|
@ -16,8 +16,10 @@ final class PhamePostEditor
|
||||||
|
|
||||||
$types[] = PhamePostTransaction::TYPE_BLOG;
|
$types[] = PhamePostTransaction::TYPE_BLOG;
|
||||||
$types[] = PhamePostTransaction::TYPE_TITLE;
|
$types[] = PhamePostTransaction::TYPE_TITLE;
|
||||||
|
$types[] = PhamePostTransaction::TYPE_SUBTITLE;
|
||||||
$types[] = PhamePostTransaction::TYPE_BODY;
|
$types[] = PhamePostTransaction::TYPE_BODY;
|
||||||
$types[] = PhamePostTransaction::TYPE_VISIBILITY;
|
$types[] = PhamePostTransaction::TYPE_VISIBILITY;
|
||||||
|
$types[] = PhamePostTransaction::TYPE_HEADERIMAGE;
|
||||||
$types[] = PhabricatorTransactions::TYPE_COMMENT;
|
$types[] = PhabricatorTransactions::TYPE_COMMENT;
|
||||||
|
|
||||||
return $types;
|
return $types;
|
||||||
|
@ -32,10 +34,14 @@ final class PhamePostEditor
|
||||||
return $object->getBlogPHID();
|
return $object->getBlogPHID();
|
||||||
case PhamePostTransaction::TYPE_TITLE:
|
case PhamePostTransaction::TYPE_TITLE:
|
||||||
return $object->getTitle();
|
return $object->getTitle();
|
||||||
|
case PhamePostTransaction::TYPE_SUBTITLE:
|
||||||
|
return $object->getSubtitle();
|
||||||
case PhamePostTransaction::TYPE_BODY:
|
case PhamePostTransaction::TYPE_BODY:
|
||||||
return $object->getBody();
|
return $object->getBody();
|
||||||
case PhamePostTransaction::TYPE_VISIBILITY:
|
case PhamePostTransaction::TYPE_VISIBILITY:
|
||||||
return $object->getVisibility();
|
return $object->getVisibility();
|
||||||
|
case PhamePostTransaction::TYPE_HEADERIMAGE:
|
||||||
|
return $object->getHeaderImagePHID();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,8 +51,10 @@ final class PhamePostEditor
|
||||||
|
|
||||||
switch ($xaction->getTransactionType()) {
|
switch ($xaction->getTransactionType()) {
|
||||||
case PhamePostTransaction::TYPE_TITLE:
|
case PhamePostTransaction::TYPE_TITLE:
|
||||||
|
case PhamePostTransaction::TYPE_SUBTITLE:
|
||||||
case PhamePostTransaction::TYPE_BODY:
|
case PhamePostTransaction::TYPE_BODY:
|
||||||
case PhamePostTransaction::TYPE_VISIBILITY:
|
case PhamePostTransaction::TYPE_VISIBILITY:
|
||||||
|
case PhamePostTransaction::TYPE_HEADERIMAGE:
|
||||||
case PhamePostTransaction::TYPE_BLOG:
|
case PhamePostTransaction::TYPE_BLOG:
|
||||||
return $xaction->getNewValue();
|
return $xaction->getNewValue();
|
||||||
}
|
}
|
||||||
|
@ -59,10 +67,14 @@ final class PhamePostEditor
|
||||||
switch ($xaction->getTransactionType()) {
|
switch ($xaction->getTransactionType()) {
|
||||||
case PhamePostTransaction::TYPE_TITLE:
|
case PhamePostTransaction::TYPE_TITLE:
|
||||||
return $object->setTitle($xaction->getNewValue());
|
return $object->setTitle($xaction->getNewValue());
|
||||||
|
case PhamePostTransaction::TYPE_SUBTITLE:
|
||||||
|
return $object->setSubtitle($xaction->getNewValue());
|
||||||
case PhamePostTransaction::TYPE_BODY:
|
case PhamePostTransaction::TYPE_BODY:
|
||||||
return $object->setBody($xaction->getNewValue());
|
return $object->setBody($xaction->getNewValue());
|
||||||
case PhamePostTransaction::TYPE_BLOG:
|
case PhamePostTransaction::TYPE_BLOG:
|
||||||
return $object->setBlogPHID($xaction->getNewValue());
|
return $object->setBlogPHID($xaction->getNewValue());
|
||||||
|
case PhamePostTransaction::TYPE_HEADERIMAGE:
|
||||||
|
return $object->setHeaderImagePHID($xaction->getNewValue());
|
||||||
case PhamePostTransaction::TYPE_VISIBILITY:
|
case PhamePostTransaction::TYPE_VISIBILITY:
|
||||||
if ($xaction->getNewValue() == PhameConstants::VISIBILITY_DRAFT) {
|
if ($xaction->getNewValue() == PhameConstants::VISIBILITY_DRAFT) {
|
||||||
$object->setDatePublished(0);
|
$object->setDatePublished(0);
|
||||||
|
@ -84,8 +96,10 @@ final class PhamePostEditor
|
||||||
|
|
||||||
switch ($xaction->getTransactionType()) {
|
switch ($xaction->getTransactionType()) {
|
||||||
case PhamePostTransaction::TYPE_TITLE:
|
case PhamePostTransaction::TYPE_TITLE:
|
||||||
|
case PhamePostTransaction::TYPE_SUBTITLE:
|
||||||
case PhamePostTransaction::TYPE_BODY:
|
case PhamePostTransaction::TYPE_BODY:
|
||||||
case PhamePostTransaction::TYPE_VISIBILITY:
|
case PhamePostTransaction::TYPE_VISIBILITY:
|
||||||
|
case PhamePostTransaction::TYPE_HEADERIMAGE:
|
||||||
case PhamePostTransaction::TYPE_BLOG:
|
case PhamePostTransaction::TYPE_BLOG:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ final class PhamePostQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
private $publishedAfter;
|
private $publishedAfter;
|
||||||
private $phids;
|
private $phids;
|
||||||
|
|
||||||
|
private $needHeaderImage;
|
||||||
|
|
||||||
public function withIDs(array $ids) {
|
public function withIDs(array $ids) {
|
||||||
$this->ids = $ids;
|
$this->ids = $ids;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -39,6 +41,11 @@ final class PhamePostQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function needHeaderImage($need) {
|
||||||
|
$this->needHeaderImage = $need;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function newResultObject() {
|
public function newResultObject() {
|
||||||
return new PhamePost();
|
return new PhamePost();
|
||||||
}
|
}
|
||||||
|
@ -71,6 +78,28 @@ final class PhamePostQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
$post->attachBlog($blog);
|
$post->attachBlog($blog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->needHeaderImage) {
|
||||||
|
$file_phids = mpull($posts, 'getHeaderImagePHID');
|
||||||
|
$file_phids = array_filter($file_phids);
|
||||||
|
if ($file_phids) {
|
||||||
|
$files = id(new PhabricatorFileQuery())
|
||||||
|
->setParentQuery($this)
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->withPHIDs($file_phids)
|
||||||
|
->execute();
|
||||||
|
$files = mpull($files, null, 'getPHID');
|
||||||
|
} else {
|
||||||
|
$files = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($posts as $post) {
|
||||||
|
$file = idx($files, $post->getHeaderImagePHID());
|
||||||
|
if ($file) {
|
||||||
|
$post->attachHeaderImageFile($file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $posts;
|
return $posts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ final class PhamePost extends PhameDAO
|
||||||
|
|
||||||
protected $bloggerPHID;
|
protected $bloggerPHID;
|
||||||
protected $title;
|
protected $title;
|
||||||
|
protected $subtitle;
|
||||||
protected $phameTitle;
|
protected $phameTitle;
|
||||||
protected $body;
|
protected $body;
|
||||||
protected $visibility;
|
protected $visibility;
|
||||||
|
@ -25,8 +26,10 @@ final class PhamePost extends PhameDAO
|
||||||
protected $datePublished;
|
protected $datePublished;
|
||||||
protected $blogPHID;
|
protected $blogPHID;
|
||||||
protected $mailKey;
|
protected $mailKey;
|
||||||
|
protected $headerImagePHID;
|
||||||
|
|
||||||
private $blog = self::ATTACHABLE;
|
private $blog = self::ATTACHABLE;
|
||||||
|
private $headerImageFile = self::ATTACHABLE;
|
||||||
|
|
||||||
public static function initializePost(
|
public static function initializePost(
|
||||||
PhabricatorUser $blogger,
|
PhabricatorUser $blogger,
|
||||||
|
@ -122,9 +125,11 @@ final class PhamePost extends PhameDAO
|
||||||
),
|
),
|
||||||
self::CONFIG_COLUMN_SCHEMA => array(
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
'title' => 'text255',
|
'title' => 'text255',
|
||||||
|
'subtitle' => 'text64',
|
||||||
'phameTitle' => 'sort64?',
|
'phameTitle' => 'sort64?',
|
||||||
'visibility' => 'uint32',
|
'visibility' => 'uint32',
|
||||||
'mailKey' => 'bytes20',
|
'mailKey' => 'bytes20',
|
||||||
|
'headerImagePHID' => 'phid?',
|
||||||
|
|
||||||
// T6203/NULLABILITY
|
// T6203/NULLABILITY
|
||||||
// These seem like they should always be non-null?
|
// These seem like they should always be non-null?
|
||||||
|
@ -170,6 +175,19 @@ final class PhamePost extends PhameDAO
|
||||||
return PhabricatorSlug::normalizeProjectSlug($this->getTitle(), true);
|
return PhabricatorSlug::normalizeProjectSlug($this->getTitle(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getHeaderImageURI() {
|
||||||
|
return $this->getHeaderImageFile()->getBestURI();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attachHeaderImageFile(PhabricatorFile $file) {
|
||||||
|
$this->headerImageFile = $file;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHeaderImageFile() {
|
||||||
|
return $this->assertAttached($this->headerImageFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorPolicyInterface Implementation )-------------------------- */
|
/* -( PhabricatorPolicyInterface Implementation )-------------------------- */
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,10 @@ final class PhamePostTransaction
|
||||||
extends PhabricatorApplicationTransaction {
|
extends PhabricatorApplicationTransaction {
|
||||||
|
|
||||||
const TYPE_TITLE = 'phame.post.title';
|
const TYPE_TITLE = 'phame.post.title';
|
||||||
|
const TYPE_SUBTITLE = 'phame.post.subtitle';
|
||||||
const TYPE_BODY = 'phame.post.body';
|
const TYPE_BODY = 'phame.post.body';
|
||||||
const TYPE_VISIBILITY = 'phame.post.visibility';
|
const TYPE_VISIBILITY = 'phame.post.visibility';
|
||||||
|
const TYPE_HEADERIMAGE = 'phame.post.headerimage';
|
||||||
const TYPE_BLOG = 'phame.post.blog';
|
const TYPE_BLOG = 'phame.post.blog';
|
||||||
|
|
||||||
const MAILTAG_CONTENT = 'phame-post-content';
|
const MAILTAG_CONTENT = 'phame-post-content';
|
||||||
|
@ -70,6 +72,9 @@ final class PhamePostTransaction
|
||||||
case PhabricatorTransactions::TYPE_CREATE:
|
case PhabricatorTransactions::TYPE_CREATE:
|
||||||
return 'fa-plus';
|
return 'fa-plus';
|
||||||
break;
|
break;
|
||||||
|
case self::TYPE_HEADERIMAGE:
|
||||||
|
return 'fa-camera-retro';
|
||||||
|
break;
|
||||||
case self::TYPE_VISIBILITY:
|
case self::TYPE_VISIBILITY:
|
||||||
if ($new == PhameConstants::VISIBILITY_PUBLISHED) {
|
if ($new == PhameConstants::VISIBILITY_PUBLISHED) {
|
||||||
return 'fa-globe';
|
return 'fa-globe';
|
||||||
|
@ -94,6 +99,7 @@ final class PhamePostTransaction
|
||||||
$tags[] = self::MAILTAG_SUBSCRIBERS;
|
$tags[] = self::MAILTAG_SUBSCRIBERS;
|
||||||
break;
|
break;
|
||||||
case self::TYPE_TITLE:
|
case self::TYPE_TITLE:
|
||||||
|
case self::TYPE_SUBTITLE:
|
||||||
case self::TYPE_BODY:
|
case self::TYPE_BODY:
|
||||||
$tags[] = self::MAILTAG_CONTENT;
|
$tags[] = self::MAILTAG_CONTENT;
|
||||||
break;
|
break;
|
||||||
|
@ -136,11 +142,29 @@ final class PhamePostTransaction
|
||||||
$new);
|
$new);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case self::TYPE_SUBTITLE:
|
||||||
|
if ($old === null) {
|
||||||
|
return pht(
|
||||||
|
'%s set the post\'s subtitle to "%s".',
|
||||||
|
$this->renderHandleLink($author_phid),
|
||||||
|
$new);
|
||||||
|
} else {
|
||||||
|
return pht(
|
||||||
|
'%s updated the post\'s subtitle to "%s".',
|
||||||
|
$this->renderHandleLink($author_phid),
|
||||||
|
$new);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case self::TYPE_BODY:
|
case self::TYPE_BODY:
|
||||||
return pht(
|
return pht(
|
||||||
'%s updated the blog post.',
|
'%s updated the blog post.',
|
||||||
$this->renderHandleLink($author_phid));
|
$this->renderHandleLink($author_phid));
|
||||||
break;
|
break;
|
||||||
|
case self::TYPE_HEADERIMAGE:
|
||||||
|
return pht(
|
||||||
|
'%s updated the header image.',
|
||||||
|
$this->renderHandleLink($author_phid));
|
||||||
|
break;
|
||||||
case self::TYPE_VISIBILITY:
|
case self::TYPE_VISIBILITY:
|
||||||
if ($new == PhameConstants::VISIBILITY_DRAFT) {
|
if ($new == PhameConstants::VISIBILITY_DRAFT) {
|
||||||
return pht(
|
return pht(
|
||||||
|
@ -195,12 +219,24 @@ final class PhamePostTransaction
|
||||||
$this->renderHandleLink($object_phid));
|
$this->renderHandleLink($object_phid));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case self::TYPE_SUBTITLE:
|
||||||
|
return pht(
|
||||||
|
'%s updated the subtitle for %s.',
|
||||||
|
$this->renderHandleLink($author_phid),
|
||||||
|
$this->renderHandleLink($object_phid));
|
||||||
|
break;
|
||||||
case self::TYPE_BODY:
|
case self::TYPE_BODY:
|
||||||
return pht(
|
return pht(
|
||||||
'%s updated the blog post %s.',
|
'%s updated the blog post %s.',
|
||||||
$this->renderHandleLink($author_phid),
|
$this->renderHandleLink($author_phid),
|
||||||
$this->renderHandleLink($object_phid));
|
$this->renderHandleLink($object_phid));
|
||||||
break;
|
break;
|
||||||
|
case self::TYPE_HEADERIMAGE:
|
||||||
|
return pht(
|
||||||
|
'%s updated the header image for post %s.',
|
||||||
|
$this->renderHandleLink($author_phid),
|
||||||
|
$this->renderHandleLink($object_phid));
|
||||||
|
break;
|
||||||
case self::TYPE_VISIBILITY:
|
case self::TYPE_VISIBILITY:
|
||||||
if ($new == PhameConstants::VISIBILITY_DRAFT) {
|
if ($new == PhameConstants::VISIBILITY_DRAFT) {
|
||||||
return pht(
|
return pht(
|
||||||
|
|
|
@ -74,6 +74,24 @@ final class PhabricatorHandleList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new list with a subset of the PHIDs in this list.
|
||||||
|
*/
|
||||||
|
public function newSublist(array $phids) {
|
||||||
|
foreach ($phids as $phid) {
|
||||||
|
if (!isset($this[$phid])) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Trying to create a new sublist of an existsing handle list, '.
|
||||||
|
'but PHID "%s" does not appear in the parent list.',
|
||||||
|
$phid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->handlePool->newHandleList($phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Rendering )---------------------------------------------------------- */
|
/* -( Rendering )---------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -205,11 +205,7 @@ final class PhabricatorPhurlURLEditor
|
||||||
protected function getMailTo(PhabricatorLiskDAO $object) {
|
protected function getMailTo(PhabricatorLiskDAO $object) {
|
||||||
$phids = array();
|
$phids = array();
|
||||||
|
|
||||||
if ($object->getPHID()) {
|
|
||||||
$phids[] = $object->getPHID();
|
|
||||||
}
|
|
||||||
$phids[] = $this->getActingAsPHID();
|
$phids[] = $this->getActingAsPHID();
|
||||||
$phids = array_unique($phids);
|
|
||||||
|
|
||||||
return $phids;
|
return $phids;
|
||||||
}
|
}
|
||||||
|
|
|
@ -416,7 +416,6 @@ final class PhabricatorProjectBoardViewController
|
||||||
->appendChild($board)
|
->appendChild($board)
|
||||||
->addClass('project-board-wrapper');
|
->addClass('project-board-wrapper');
|
||||||
|
|
||||||
$nav = $this->getProfileMenu();
|
|
||||||
$divider = id(new PHUIListItemView())
|
$divider = id(new PHUIListItemView())
|
||||||
->setType(PHUIListItemView::TYPE_DIVIDER);
|
->setType(PHUIListItemView::TYPE_DIVIDER);
|
||||||
$fullscreen = $this->buildFullscreenMenu();
|
$fullscreen = $this->buildFullscreenMenu();
|
||||||
|
@ -439,7 +438,6 @@ final class PhabricatorProjectBoardViewController
|
||||||
))
|
))
|
||||||
->setPageObjectPHIDs(array($project->getPHID()))
|
->setPageObjectPHIDs(array($project->getPHID()))
|
||||||
->setShowFooter(false)
|
->setShowFooter(false)
|
||||||
->setNavigation($nav)
|
|
||||||
->setCrumbs($crumbs)
|
->setCrumbs($crumbs)
|
||||||
->addQuicksandConfig(
|
->addQuicksandConfig(
|
||||||
array(
|
array(
|
||||||
|
|
|
@ -119,7 +119,8 @@ abstract class PhabricatorProjectController extends PhabricatorController {
|
||||||
foreach ($ancestors as $ancestor) {
|
foreach ($ancestors as $ancestor) {
|
||||||
$crumbs->addTextCrumb(
|
$crumbs->addTextCrumb(
|
||||||
$ancestor->getName(),
|
$ancestor->getName(),
|
||||||
$ancestor->getURI());
|
$ancestor->getProfileURI()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -367,6 +367,11 @@ final class PhabricatorProject extends PhabricatorProjectDAO
|
||||||
return "/project/view/{$id}/";
|
return "/project/view/{$id}/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getProfileURI() {
|
||||||
|
$id = $this->getID();
|
||||||
|
return "/project/profile/{$id}/";
|
||||||
|
}
|
||||||
|
|
||||||
public function save() {
|
public function save() {
|
||||||
if (!$this->getMailKey()) {
|
if (!$this->getMailKey()) {
|
||||||
$this->setMailKey(Filesystem::readRandomCharacters(20));
|
$this->setMailKey(Filesystem::readRandomCharacters(20));
|
||||||
|
|
|
@ -103,15 +103,10 @@ final class PhabricatorProjectDatasource
|
||||||
|
|
||||||
$all_strings = array();
|
$all_strings = array();
|
||||||
$all_strings[] = $proj->getDisplayName();
|
$all_strings[] = $proj->getDisplayName();
|
||||||
|
|
||||||
// Add an extra space after the name so that the original project
|
|
||||||
// sorts ahead of milestones. This is kind of a hack but ehh?
|
|
||||||
$all_strings[] = null;
|
|
||||||
|
|
||||||
foreach ($proj->getSlugs() as $project_slug) {
|
foreach ($proj->getSlugs() as $project_slug) {
|
||||||
$all_strings[] = $project_slug->getSlug();
|
$all_strings[] = $project_slug->getSlug();
|
||||||
}
|
}
|
||||||
$all_strings = implode(' ', $all_strings);
|
$all_strings = implode("\n", $all_strings);
|
||||||
|
|
||||||
$proj_result = id(new PhabricatorTypeaheadResult())
|
$proj_result = id(new PhabricatorTypeaheadResult())
|
||||||
->setName($all_strings)
|
->setName($all_strings)
|
||||||
|
@ -135,7 +130,7 @@ final class PhabricatorProjectDatasource
|
||||||
|
|
||||||
$description = idx($descriptions, $phid);
|
$description = idx($descriptions, $phid);
|
||||||
if (strlen($description)) {
|
if (strlen($description)) {
|
||||||
$summary = PhabricatorMarkupEngine::summarize($description);
|
$summary = PhabricatorMarkupEngine::summarizeSentence($description);
|
||||||
$proj_result->addAttribute($summary);
|
$proj_result->addAttribute($summary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -563,6 +563,11 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getURI() {
|
public function getURI() {
|
||||||
|
$short_name = $this->getRepositorySlug();
|
||||||
|
if (strlen($short_name)) {
|
||||||
|
return "/source/{$short_name}/";
|
||||||
|
}
|
||||||
|
|
||||||
$callsign = $this->getCallsign();
|
$callsign = $this->getCallsign();
|
||||||
if (strlen($callsign)) {
|
if (strlen($callsign)) {
|
||||||
return "/diffusion/{$callsign}/";
|
return "/diffusion/{$callsign}/";
|
||||||
|
@ -573,7 +578,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPathURI($path) {
|
public function getPathURI($path) {
|
||||||
return $this->getURI().$path;
|
return $this->getURI().ltrim($path, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCommitURI($identifier) {
|
public function getCommitURI($identifier) {
|
||||||
|
@ -586,14 +591,22 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
return "/R{$id}:{$identifier}";
|
return "/R{$id}:{$identifier}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function parseRepositoryServicePath($request_path) {
|
public static function parseRepositoryServicePath($request_path, $vcs) {
|
||||||
|
|
||||||
// NOTE: In Mercurial over SSH, the path will begin without a leading "/",
|
// NOTE: In Mercurial over SSH, the path will begin without a leading "/",
|
||||||
// so we're matching it optionally.
|
// so we're matching it optionally.
|
||||||
|
|
||||||
|
if ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT) {
|
||||||
|
$maybe_git = '(?:\\.git)?';
|
||||||
|
} else {
|
||||||
|
$maybe_git = null;
|
||||||
|
}
|
||||||
|
|
||||||
$patterns = array(
|
$patterns = array(
|
||||||
'(^'.
|
'(^'.
|
||||||
'(?P<base>/?diffusion/(?P<identifier>[A-Z]+|[0-9]\d*))'.
|
'(?P<base>/?(?:diffusion|source)/(?P<identifier>[^/.]+))'.
|
||||||
'(?P<path>(?:/.*)?)'.
|
$maybe_git.
|
||||||
|
'(?P<path>(?:/|.*)?)'.
|
||||||
'\z)',
|
'\z)',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -624,28 +637,15 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
public function getCanonicalPath($request_path) {
|
public function getCanonicalPath($request_path) {
|
||||||
$standard_pattern =
|
$standard_pattern =
|
||||||
'(^'.
|
'(^'.
|
||||||
'(?P<prefix>/diffusion/)'.
|
'(?P<prefix>/(?:diffusion|source)/)'.
|
||||||
'(?P<identifier>[^/]+)'.
|
'(?P<identifier>[^/]+)'.
|
||||||
'(?P<suffix>(?:/.*)?)'.
|
'(?P<suffix>(?:/.*)?)'.
|
||||||
'\z)';
|
'\z)';
|
||||||
|
|
||||||
$matches = null;
|
$matches = null;
|
||||||
if (preg_match($standard_pattern, $request_path, $matches)) {
|
if (preg_match($standard_pattern, $request_path, $matches)) {
|
||||||
$prefix = $matches['prefix'];
|
|
||||||
|
|
||||||
$callsign = $this->getCallsign();
|
|
||||||
if ($callsign) {
|
|
||||||
$identifier = $callsign;
|
|
||||||
} else {
|
|
||||||
$identifier = $this->getID();
|
|
||||||
}
|
|
||||||
|
|
||||||
$suffix = $matches['suffix'];
|
$suffix = $matches['suffix'];
|
||||||
if (!strlen($suffix)) {
|
return $this->getPathURI($suffix);
|
||||||
$suffix = '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $prefix.$identifier.$suffix;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$commit_pattern =
|
$commit_pattern =
|
||||||
|
@ -724,18 +724,6 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
return $this->getCommitURI($commit);
|
return $this->getCommitURI($commit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$identifier = $this->getID();
|
|
||||||
|
|
||||||
$callsign = $this->getCallsign();
|
|
||||||
if ($callsign !== null) {
|
|
||||||
$identifier = $callsign;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strlen($identifier)) {
|
|
||||||
$identifier = phutil_escape_uri_path_component($identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strlen($path)) {
|
if (strlen($path)) {
|
||||||
$path = ltrim($path, '/');
|
$path = ltrim($path, '/');
|
||||||
$path = str_replace(array(';', '$'), array(';;', '$$'), $path);
|
$path = str_replace(array(';', '$'), array(';;', '$$'), $path);
|
||||||
|
@ -766,13 +754,13 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
case 'lint':
|
case 'lint':
|
||||||
case 'pathtree':
|
case 'pathtree':
|
||||||
case 'refs':
|
case 'refs':
|
||||||
$uri = "/diffusion/{$identifier}/{$action}/{$path}{$commit}{$line}";
|
$uri = $this->getPathURI("/{$action}/{$path}{$commit}{$line}");
|
||||||
break;
|
break;
|
||||||
case 'branch':
|
case 'branch':
|
||||||
if (strlen($path)) {
|
if (strlen($path)) {
|
||||||
$uri = "/diffusion/{$identifier}/repository/{$path}";
|
$uri = $this->getPathURI("/repository/{$path}");
|
||||||
} else {
|
} else {
|
||||||
$uri = "/diffusion/{$identifier}/";
|
$uri = $this->getPathURI('/');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'external':
|
case 'external':
|
||||||
|
@ -2108,9 +2096,6 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
$has_callsign = ($this->getCallsign() !== null);
|
$has_callsign = ($this->getCallsign() !== null);
|
||||||
$has_shortname = ($this->getRepositorySlug() !== null);
|
$has_shortname = ($this->getRepositorySlug() !== null);
|
||||||
|
|
||||||
// TODO: For now, never enable these because they don't work yet.
|
|
||||||
$has_shortname = false;
|
|
||||||
|
|
||||||
$identifier_map = array(
|
$identifier_map = array(
|
||||||
PhabricatorRepositoryURI::BUILTIN_IDENTIFIER_CALLSIGN => $has_callsign,
|
PhabricatorRepositoryURI::BUILTIN_IDENTIFIER_CALLSIGN => $has_callsign,
|
||||||
PhabricatorRepositoryURI::BUILTIN_IDENTIFIER_SHORTNAME => $has_shortname,
|
PhabricatorRepositoryURI::BUILTIN_IDENTIFIER_SHORTNAME => $has_shortname,
|
||||||
|
|
|
@ -125,8 +125,8 @@ final class PhabricatorRepositoryURI
|
||||||
$other_uris = $repository->getURIs();
|
$other_uris = $repository->getURIs();
|
||||||
|
|
||||||
$identifier_value = array(
|
$identifier_value = array(
|
||||||
self::BUILTIN_IDENTIFIER_CALLSIGN => 3,
|
self::BUILTIN_IDENTIFIER_SHORTNAME => 3,
|
||||||
self::BUILTIN_IDENTIFIER_SHORTNAME => 2,
|
self::BUILTIN_IDENTIFIER_CALLSIGN => 2,
|
||||||
self::BUILTIN_IDENTIFIER_ID => 1,
|
self::BUILTIN_IDENTIFIER_ID => 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -233,6 +233,10 @@ class PhabricatorApplicationTransactionView extends AphrontView {
|
||||||
return $view;
|
return $view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isTimelineEmpty() {
|
||||||
|
return !count($this->buildEvents(true));
|
||||||
|
}
|
||||||
|
|
||||||
protected function getOrBuildEngine() {
|
protected function getOrBuildEngine() {
|
||||||
if (!$this->engine) {
|
if (!$this->engine) {
|
||||||
$field = PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT;
|
$field = PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT;
|
||||||
|
|
|
@ -311,6 +311,11 @@ final class PhabricatorTypeaheadModularDatasourceController
|
||||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||||
->setForm($form);
|
->setForm($form);
|
||||||
|
|
||||||
|
// Make "\n" delimiters more visible.
|
||||||
|
foreach ($content as $key => $row) {
|
||||||
|
$content[$key][0] = str_replace("\n", '<\n>', $row[0]);
|
||||||
|
}
|
||||||
|
|
||||||
$table = new AphrontTableView($content);
|
$table = new AphrontTableView($content);
|
||||||
$table->setHeaders(
|
$table->setHeaders(
|
||||||
array(
|
array(
|
||||||
|
|
|
@ -204,8 +204,8 @@ For many linters, you can do this by providing a `severity` map:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Here, the lint message "E221" (which is "multiple spaces before operator") is
|
Here, the lint message `E221` (which is "multiple spaces before operator") is
|
||||||
disabled, so it won't be shown. The message "E401" (which is "multiple imports
|
disabled, so it won't be shown. The message `E401` (which is "multiple imports
|
||||||
on one line") is set to "warning" severity.
|
on one line") is set to "warning" severity.
|
||||||
|
|
||||||
If you want to remap a large number of messages, you can use `severity.rules`
|
If you want to remap a large number of messages, you can use `severity.rules`
|
||||||
|
|
|
@ -37,6 +37,22 @@ revision or task.
|
||||||
You can click through to a user's profile to see more details about their
|
You can click through to a user's profile to see more details about their
|
||||||
availability.
|
availability.
|
||||||
|
|
||||||
|
Status Icons
|
||||||
|
============
|
||||||
|
|
||||||
|
On the month and day views, Calendar shows an icon next to each event to
|
||||||
|
indicate status. The icons are:
|
||||||
|
|
||||||
|
- {icon user-plus, color=green} **Invited, Individual**: You're personally
|
||||||
|
invited to the event.
|
||||||
|
- {icon users, color=green} **Invited, Group**: A project you are a member
|
||||||
|
of is invited to the event.
|
||||||
|
- {icon check-circle, color=green} **Attending**: You're attending the event.
|
||||||
|
- {icon times-circle, color=grey} **Declined**: You've declined the event.
|
||||||
|
- {icon times, color=red} **Cancelled**: The event has been cancelled.
|
||||||
|
|
||||||
|
If you don't have any special relationship to the event and the event does not
|
||||||
|
have any special status, an event-specific icon is shown instead.
|
||||||
|
|
||||||
Importing Events
|
Importing Events
|
||||||
================
|
================
|
||||||
|
|
|
@ -222,23 +222,92 @@ other Phabricator SSH services.
|
||||||
NOTE: The Phabricator `sshd` service **MUST** be 6.2 or newer, because
|
NOTE: The Phabricator `sshd` service **MUST** be 6.2 or newer, because
|
||||||
Phabricator relies on the `AuthorizedKeysCommand` option.
|
Phabricator relies on the `AuthorizedKeysCommand` option.
|
||||||
|
|
||||||
**Choose a Port**: These instructions will configure the alternate `sshd` on
|
Before continuing, you must choose a strategy for which port each copy of
|
||||||
|
`sshd` will run on. The next section lays out various approaches.
|
||||||
|
|
||||||
|
|
||||||
|
SSHD Port Assignment
|
||||||
|
====================
|
||||||
|
|
||||||
|
The normal `sshd` that lets you administrate the host and the special `sshd`
|
||||||
|
which serves repositories can't run on the same port. In particular, only one
|
||||||
|
of them can run on port `22`, which will make it a bit inconvenient to access
|
||||||
|
the other one.
|
||||||
|
|
||||||
|
These instructions will walk you through configuring the alternate `sshd` on
|
||||||
port `2222`. This is easy to configure, but if you run the service on this port
|
port `2222`. This is easy to configure, but if you run the service on this port
|
||||||
users will clone and push to URIs like `ssh://git@host.com:2222/`, which is
|
users will clone and push to URIs like `ssh://git@host.com:2222/`, which is a
|
||||||
a little ugly.
|
little ugly.
|
||||||
|
|
||||||
The easiest way to fix this is to put a load balancer in front of the host and
|
There are several different approaches you can use to mitigate or eliminate
|
||||||
have it forward TCP traffic on port `22` to port `2222`. Then users can clone
|
this problem.
|
||||||
from `ssh://git@host.com/` without an explicit port number and you don't need
|
|
||||||
to do anything else.
|
|
||||||
|
|
||||||
Alternatively, you can move the administrative `sshd` to a new port, then run
|
**Run on Port 2222**: You can do nothing, and just run the repository `sshd` on
|
||||||
Phabricator `sshd` on port 22. This is complicated and risky. See "Moving the
|
port `2222` and accept the explicit port in the URIs. This is the simplest
|
||||||
sshd Port" below for help.
|
approach, and you can always start here and clean things up later if you grow
|
||||||
|
tired of dealing with the port number.
|
||||||
|
|
||||||
Finally, you can just run on port `2222` and accept the explicit port in the
|
**Use a Load Balancer**: You can configure a load balancer in front of the host
|
||||||
URIs. This is the simplest approach, and you can start here and clean things
|
and have it forward TCP traffic on port `22` to port `2222`. Then users can
|
||||||
up later.
|
clone from `ssh://git@host.com/` without an explicit port number and you don't
|
||||||
|
need to do anything else.
|
||||||
|
|
||||||
|
This may be very easy to set up, particularly if you are hosted in AWS, and
|
||||||
|
is often the simplest and cleanest approach.
|
||||||
|
|
||||||
|
**Swap Ports**: You can move the administrative `sshd` to a new port, then run
|
||||||
|
Phabricator `sshd` on port 22. This is somewhat complicated and can be a bit
|
||||||
|
risky if you make a mistake. See "Moving the sshd Port" below for help.
|
||||||
|
|
||||||
|
**Change Client Config**: You can run on a nonstandard port, but configure SSH
|
||||||
|
on the client side so that `ssh` automatically defaults to the correct port
|
||||||
|
when connecting to the host. To do this, add a section like this to your
|
||||||
|
`~/.ssh/config`:
|
||||||
|
|
||||||
|
```
|
||||||
|
Host phabricator.corporation.com
|
||||||
|
Port 2222
|
||||||
|
```
|
||||||
|
|
||||||
|
(If you want, you can also add a default `User`.)
|
||||||
|
|
||||||
|
Command line tools like `ssh`, `git` and `hg` will now default to port
|
||||||
|
`2222` when connecting to this host.
|
||||||
|
|
||||||
|
A downside to this approach is that your users will each need to set up their
|
||||||
|
`~/.ssh/config` files individually.
|
||||||
|
|
||||||
|
This file also allows you to define short names for hosts using the `Host` and
|
||||||
|
`HostName` options. If you choose to do this, be aware that Phabricator uses
|
||||||
|
remote/clone URIs to figure out which repository it is operating in, but can
|
||||||
|
not resolve host aliases defined in your `ssh` config. If you create host
|
||||||
|
aliases they may break some features related to repository identification.
|
||||||
|
|
||||||
|
If you use this approach, you will also need to specify a port explicitly when
|
||||||
|
connecting to administrate the host. Any unit tests or other build automation
|
||||||
|
will also need to be configured or use explicit port numbers.
|
||||||
|
|
||||||
|
**Port Multiplexing**: If you have hardware access, you can power down the host
|
||||||
|
and find the network I/O pins on the motherboard (for onboard networking) or
|
||||||
|
network card.
|
||||||
|
|
||||||
|
Carefully strip and solder a short piece of copper wire between the pins for
|
||||||
|
the external interface `22` and internal `2222`, so the external interface can
|
||||||
|
receive traffic for both services.
|
||||||
|
|
||||||
|
(Make sure not to desolder the existing connection between external `22` and
|
||||||
|
internal `22` or you won't be able to connect normally to administrate the
|
||||||
|
host.)
|
||||||
|
|
||||||
|
The obvious downside to this approach is that it requires physical access to
|
||||||
|
the machine, so it won't work if you're hosted on a cloud provider.
|
||||||
|
|
||||||
|
|
||||||
|
SSHD Setup
|
||||||
|
==========
|
||||||
|
|
||||||
|
Now that you've decided how you'll handle port assignment, you're ready to
|
||||||
|
continue `sshd` setup.
|
||||||
|
|
||||||
If you plan to connect to a port other than `22`, you should set this port
|
If you plan to connect to a port other than `22`, you should set this port
|
||||||
as `diffusion.ssh-port` in your Phabricator config:
|
as `diffusion.ssh-port` in your Phabricator config:
|
||||||
|
|
|
@ -85,14 +85,6 @@ final class PhabricatorClusterDatabasesConfigOptionType
|
||||||
$map[$key] = true;
|
$map[$key] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count($masters) > 1) {
|
|
||||||
throw new Exception(
|
|
||||||
pht(
|
|
||||||
'Database cluster configuration is invalid: it describes multiple '.
|
|
||||||
'masters. No more than one host may be a master. Hosts currently '.
|
|
||||||
'configured as masters: %s.',
|
|
||||||
implode(', ', $masters)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,6 +157,17 @@ final class PhabricatorDatabaseRef
|
||||||
return $this->isIndividual;
|
return $this->isIndividual;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRefKey() {
|
||||||
|
$host = $this->getHost();
|
||||||
|
|
||||||
|
$port = $this->getPort();
|
||||||
|
if (strlen($port)) {
|
||||||
|
return "{$host}:{$port}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $host;
|
||||||
|
}
|
||||||
|
|
||||||
public static function getConnectionStatusMap() {
|
public static function getConnectionStatusMap() {
|
||||||
return array(
|
return array(
|
||||||
self::STATUS_OKAY => array(
|
self::STATUS_OKAY => array(
|
||||||
|
@ -212,7 +223,7 @@ final class PhabricatorDatabaseRef
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getLiveRefs() {
|
public static function getClusterRefs() {
|
||||||
$cache = PhabricatorCaches::getRequestCache();
|
$cache = PhabricatorCaches::getRequestCache();
|
||||||
|
|
||||||
$refs = $cache->getKey(self::KEY_REFS);
|
$refs = $cache->getKey(self::KEY_REFS);
|
||||||
|
@ -446,24 +457,46 @@ final class PhabricatorDatabaseRef
|
||||||
return $this->healthRecord;
|
return $this->healthRecord;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getMasterDatabaseRef() {
|
public static function getActiveDatabaseRefs() {
|
||||||
$refs = self::getLiveRefs();
|
$refs = array();
|
||||||
|
|
||||||
if (!$refs) {
|
foreach (self::getMasterDatabaseRefs() as $ref) {
|
||||||
return self::getLiveIndividualRef();
|
$refs[] = $ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
$master = null;
|
foreach (self::getReplicaDatabaseRefs() as $ref) {
|
||||||
|
$refs[] = $ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $refs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getMasterDatabaseRefs() {
|
||||||
|
$refs = self::getClusterRefs();
|
||||||
|
|
||||||
|
if (!$refs) {
|
||||||
|
return array(self::getLiveIndividualRef());
|
||||||
|
}
|
||||||
|
|
||||||
|
$masters = array();
|
||||||
foreach ($refs as $ref) {
|
foreach ($refs as $ref) {
|
||||||
if ($ref->getDisabled()) {
|
if ($ref->getDisabled()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($ref->getIsMaster()) {
|
if ($ref->getIsMaster()) {
|
||||||
return $ref;
|
$masters[] = $ref;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return $masters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getMasterDatabaseRefForDatabase($database) {
|
||||||
|
$masters = self::getMasterDatabaseRefs();
|
||||||
|
|
||||||
|
// TODO: Actually implement this.
|
||||||
|
|
||||||
|
return head($masters);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function newIndividualRef() {
|
public static function newIndividualRef() {
|
||||||
|
@ -480,18 +513,14 @@ final class PhabricatorDatabaseRef
|
||||||
->setIsMaster(true);
|
->setIsMaster(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getReplicaDatabaseRef() {
|
public static function getReplicaDatabaseRefs() {
|
||||||
$refs = self::getLiveRefs();
|
$refs = self::getClusterRefs();
|
||||||
|
|
||||||
if (!$refs) {
|
if (!$refs) {
|
||||||
return null;
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: We may have multiple replicas to choose from, and could make
|
$replicas = array();
|
||||||
// more of an effort to pick the "best" one here instead of always
|
|
||||||
// picking the first one. Once we've picked one, we should try to use
|
|
||||||
// the same replica for the rest of the request, though.
|
|
||||||
|
|
||||||
foreach ($refs as $ref) {
|
foreach ($refs as $ref) {
|
||||||
if ($ref->getDisabled()) {
|
if ($ref->getDisabled()) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -499,10 +528,24 @@ final class PhabricatorDatabaseRef
|
||||||
if ($ref->getIsMaster()) {
|
if ($ref->getIsMaster()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return $ref;
|
|
||||||
|
$replicas[] = $ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return $replicas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getReplicaDatabaseRefForDatabase($database) {
|
||||||
|
$replicas = self::getReplicaDatabaseRefs();
|
||||||
|
|
||||||
|
// TODO: Actually implement this.
|
||||||
|
|
||||||
|
// TODO: We may have multiple replicas to choose from, and could make
|
||||||
|
// more of an effort to pick the "best" one here instead of always
|
||||||
|
// picking the first one. Once we've picked one, we should try to use
|
||||||
|
// the same replica for the rest of the request, though.
|
||||||
|
|
||||||
|
return head($replicas);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function newConnection(array $options) {
|
private function newConnection(array $options) {
|
||||||
|
|
23
src/infrastructure/env/PhabricatorEnv.php
vendored
23
src/infrastructure/env/PhabricatorEnv.php
vendored
|
@ -223,13 +223,24 @@ final class PhabricatorEnv extends Phobject {
|
||||||
$stack->pushSource($site_source);
|
$stack->pushSource($site_source);
|
||||||
}
|
}
|
||||||
|
|
||||||
$master = PhabricatorDatabaseRef::getMasterDatabaseRef();
|
$masters = PhabricatorDatabaseRef::getMasterDatabaseRefs();
|
||||||
if (!$master) {
|
if (!$masters) {
|
||||||
self::setReadOnly(true, self::READONLY_MASTERLESS);
|
self::setReadOnly(true, self::READONLY_MASTERLESS);
|
||||||
} else if ($master->isSevered()) {
|
} else {
|
||||||
$master->checkHealth();
|
// If any master is severed, we drop to readonly mode. In theory we
|
||||||
if ($master->isSevered()) {
|
// could try to continue if we're only missing some applications, but
|
||||||
self::setReadOnly(true, self::READONLY_SEVERED);
|
// this is very complex and we're unlikely to get it right.
|
||||||
|
|
||||||
|
foreach ($masters as $master) {
|
||||||
|
// Give severed masters one last chance to get healthy.
|
||||||
|
if ($master->isSevered()) {
|
||||||
|
$master->checkHealth();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($master->isSevered()) {
|
||||||
|
self::setReadOnly(true, self::READONLY_SEVERED);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1580,6 +1580,14 @@ final class PhabricatorUSEnglishTranslation
|
||||||
'Restart %s build?',
|
'Restart %s build?',
|
||||||
'Restart %s builds?',
|
'Restart %s builds?',
|
||||||
),
|
),
|
||||||
|
|
||||||
|
'%s is starting in %s minute(s), at %s.' => array(
|
||||||
|
array(
|
||||||
|
'%s is starting in one minute, at %3$s.',
|
||||||
|
'%s is starting in %s minutes, at %s.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -607,6 +607,28 @@ final class PhabricatorMarkupEngine extends Phobject {
|
||||||
return array_values($files);
|
return array_values($files);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function summarizeSentence($corpus) {
|
||||||
|
$corpus = trim($corpus);
|
||||||
|
$blocks = preg_split('/\n+/', $corpus, 2);
|
||||||
|
$block = head($blocks);
|
||||||
|
|
||||||
|
$sentences = preg_split(
|
||||||
|
'/\b([.?!]+)\B/u',
|
||||||
|
$block,
|
||||||
|
2,
|
||||||
|
PREG_SPLIT_DELIM_CAPTURE);
|
||||||
|
|
||||||
|
if (count($sentences) > 1) {
|
||||||
|
$result = $sentences[0].$sentences[1];
|
||||||
|
} else {
|
||||||
|
$result = head($sentences);
|
||||||
|
}
|
||||||
|
|
||||||
|
return id(new PhutilUTF8StringTruncator())
|
||||||
|
->setMaximumGlyphs(128)
|
||||||
|
->truncateString($result);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a corpus summary, in a way that shortens the underlying text
|
* Produce a corpus summary, in a way that shortens the underlying text
|
||||||
* without truncating it somewhere awkward.
|
* without truncating it somewhere awkward.
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorMarkupEngineTestCase
|
||||||
|
extends PhabricatorTestCase {
|
||||||
|
|
||||||
|
public function testRemarkupSentenceSummmaries() {
|
||||||
|
$this->assertSentenceSummary(
|
||||||
|
'The quick brown fox. Jumped over the lazy dog.',
|
||||||
|
'The quick brown fox.');
|
||||||
|
|
||||||
|
$this->assertSentenceSummary(
|
||||||
|
'Go to www.help.com for details. Good day.',
|
||||||
|
'Go to www.help.com for details.');
|
||||||
|
|
||||||
|
$this->assertSentenceSummary(
|
||||||
|
'Coxy lummox gives squid who asks for job pen.',
|
||||||
|
'Coxy lummox gives squid who asks for job pen.');
|
||||||
|
|
||||||
|
$this->assertSentenceSummary(
|
||||||
|
'DEPRECATED',
|
||||||
|
'DEPRECATED');
|
||||||
|
|
||||||
|
$this->assertSentenceSummary(
|
||||||
|
'Never use this! It is deadly poison.',
|
||||||
|
'Never use this!');
|
||||||
|
|
||||||
|
$this->assertSentenceSummary(
|
||||||
|
"a short poem\nmeow meow meow\nmeow meow meow\n\n- cat",
|
||||||
|
'a short poem');
|
||||||
|
|
||||||
|
$this->assertSentenceSummary(
|
||||||
|
'WOW!! GREAT PROJECT!',
|
||||||
|
'WOW!!');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertSentenceSummary($corpus, $summary) {
|
||||||
|
$this->assertEqual(
|
||||||
|
$summary,
|
||||||
|
PhabricatorMarkupEngine::summarizeSentence($corpus),
|
||||||
|
pht('Summary of: %s', $corpus));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -114,7 +114,8 @@ abstract class PhabricatorLiskDAO extends LiskDAO {
|
||||||
}
|
}
|
||||||
|
|
||||||
private function newClusterConnection($database, $mode) {
|
private function newClusterConnection($database, $mode) {
|
||||||
$master = PhabricatorDatabaseRef::getMasterDatabaseRef();
|
$master = PhabricatorDatabaseRef::getMasterDatabaseRefForDatabase(
|
||||||
|
$database);
|
||||||
|
|
||||||
if ($master && !$master->isSevered()) {
|
if ($master && !$master->isSevered()) {
|
||||||
$connection = $master->newApplicationConnection($database);
|
$connection = $master->newApplicationConnection($database);
|
||||||
|
@ -130,7 +131,8 @@ abstract class PhabricatorLiskDAO extends LiskDAO {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$replica = PhabricatorDatabaseRef::getReplicaDatabaseRef();
|
$replica = PhabricatorDatabaseRef::getReplicaDatabaseRefForDatabase(
|
||||||
|
$database);
|
||||||
if ($replica) {
|
if ($replica) {
|
||||||
$connection = $replica->newApplicationConnection($database);
|
$connection = $replica->newApplicationConnection($database);
|
||||||
$connection->setReadOnly(true);
|
$connection->setReadOnly(true);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
final class PhabricatorStorageManagementAPI extends Phobject {
|
final class PhabricatorStorageManagementAPI extends Phobject {
|
||||||
|
|
||||||
|
private $ref;
|
||||||
private $host;
|
private $host;
|
||||||
private $user;
|
private $user;
|
||||||
private $port;
|
private $port;
|
||||||
|
@ -74,6 +75,15 @@ final class PhabricatorStorageManagementAPI extends Phobject {
|
||||||
return $this->port;
|
return $this->port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setRef(PhabricatorDatabaseRef $ref) {
|
||||||
|
$this->ref = $ref;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRef() {
|
||||||
|
return $this->ref;
|
||||||
|
}
|
||||||
|
|
||||||
public function getDatabaseName($fragment) {
|
public function getDatabaseName($fragment) {
|
||||||
return $this->namespace.'_'.$fragment;
|
return $this->namespace.'_'.$fragment;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,12 +27,19 @@ final class PhabricatorStorageManagementAdjustWorkflow
|
||||||
public function didExecute(PhutilArgumentParser $args) {
|
public function didExecute(PhutilArgumentParser $args) {
|
||||||
$unsafe = $args->getArg('unsafe');
|
$unsafe = $args->getArg('unsafe');
|
||||||
|
|
||||||
$this->requireAllPatchesApplied();
|
foreach ($this->getMasterAPIs() as $api) {
|
||||||
return $this->adjustSchemata($unsafe);
|
$this->requireAllPatchesApplied($api);
|
||||||
|
$err = $this->adjustSchemata($api, $unsafe);
|
||||||
|
if ($err) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function requireAllPatchesApplied() {
|
private function requireAllPatchesApplied(
|
||||||
$api = $this->getAPI();
|
PhabricatorStorageManagementAPI $api) {
|
||||||
$applied = $api->getAppliedPatches();
|
$applied = $api->getAppliedPatches();
|
||||||
|
|
||||||
if ($applied === null) {
|
if ($applied === null) {
|
||||||
|
|
|
@ -15,7 +15,8 @@ final class PhabricatorStorageManagementDatabasesWorkflow
|
||||||
}
|
}
|
||||||
|
|
||||||
public function didExecute(PhutilArgumentParser $args) {
|
public function didExecute(PhutilArgumentParser $args) {
|
||||||
$api = $this->getAPI();
|
$api = $this->getAnyAPI();
|
||||||
|
|
||||||
$patches = $this->getPatches();
|
$patches = $this->getPatches();
|
||||||
|
|
||||||
$databases = $api->getDatabaseList($patches, true);
|
$databases = $api->getDatabaseList($patches, true);
|
||||||
|
|
|
@ -23,6 +23,8 @@ final class PhabricatorStorageManagementDestroyWorkflow
|
||||||
public function didExecute(PhutilArgumentParser $args) {
|
public function didExecute(PhutilArgumentParser $args) {
|
||||||
$console = PhutilConsole::getConsole();
|
$console = PhutilConsole::getConsole();
|
||||||
|
|
||||||
|
$api = $this->getSingleAPI();
|
||||||
|
|
||||||
if (!$this->isDryRun() && !$this->isForce()) {
|
if (!$this->isDryRun() && !$this->isForce()) {
|
||||||
$console->writeOut(
|
$console->writeOut(
|
||||||
phutil_console_wrap(
|
phutil_console_wrap(
|
||||||
|
@ -42,7 +44,6 @@ final class PhabricatorStorageManagementDestroyWorkflow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$api = $this->getAPI();
|
|
||||||
$patches = $this->getPatches();
|
$patches = $this->getPatches();
|
||||||
|
|
||||||
if ($args->getArg('unittest-fixtures')) {
|
if ($args->getArg('unittest-fixtures')) {
|
||||||
|
|
|
@ -44,7 +44,7 @@ final class PhabricatorStorageManagementDumpWorkflow
|
||||||
}
|
}
|
||||||
|
|
||||||
public function didExecute(PhutilArgumentParser $args) {
|
public function didExecute(PhutilArgumentParser $args) {
|
||||||
$api = $this->getAPI();
|
$api = $this->getSingleAPI();
|
||||||
$patches = $this->getPatches();
|
$patches = $this->getPatches();
|
||||||
|
|
||||||
$console = PhutilConsole::getConsole();
|
$console = PhutilConsole::getConsole();
|
||||||
|
|
|
@ -15,13 +15,14 @@ final class PhabricatorStorageManagementProbeWorkflow
|
||||||
}
|
}
|
||||||
|
|
||||||
public function didExecute(PhutilArgumentParser $args) {
|
public function didExecute(PhutilArgumentParser $args) {
|
||||||
|
$api = $this->getSingleAPI();
|
||||||
|
|
||||||
$console = PhutilConsole::getConsole();
|
$console = PhutilConsole::getConsole();
|
||||||
$console->writeErr(
|
$console->writeErr(
|
||||||
"%s\n",
|
"%s\n",
|
||||||
pht('Analyzing table sizes (this may take a moment)...'));
|
pht('Analyzing table sizes (this may take a moment)...'));
|
||||||
|
|
||||||
$api = $this->getAPI();
|
$patches = $this->getPatches();
|
||||||
$patches = $this->getPatches();
|
|
||||||
$databases = $api->getDatabaseList($patches, true);
|
$databases = $api->getDatabaseList($patches, true);
|
||||||
|
|
||||||
$conn_r = $api->getConn(null);
|
$conn_r = $api->getConn(null);
|
||||||
|
|
|
@ -36,7 +36,14 @@ final class PhabricatorStorageManagementQuickstartWorkflow
|
||||||
|
|
||||||
$bin = dirname(phutil_get_library_root('phabricator')).'/bin/storage';
|
$bin = dirname(phutil_get_library_root('phabricator')).'/bin/storage';
|
||||||
|
|
||||||
if (!$this->getAPI()->isCharacterSetAvailable('utf8mb4')) {
|
// We don't care which database we're using to generate a quickstart file,
|
||||||
|
// since all of the schemata should be identical.
|
||||||
|
$api = $this->getAnyAPI();
|
||||||
|
|
||||||
|
$ref = $api->getRef();
|
||||||
|
$ref_key = $ref->getRefKey();
|
||||||
|
|
||||||
|
if (!$api->isCharacterSetAvailable('utf8mb4')) {
|
||||||
throw new PhutilArgumentUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
pht(
|
pht(
|
||||||
'You can only generate a new quickstart file if MySQL supports '.
|
'You can only generate a new quickstart file if MySQL supports '.
|
||||||
|
@ -47,35 +54,39 @@ final class PhabricatorStorageManagementQuickstartWorkflow
|
||||||
}
|
}
|
||||||
|
|
||||||
$err = phutil_passthru(
|
$err = phutil_passthru(
|
||||||
'%s upgrade --force --no-quickstart --namespace %s',
|
'%s upgrade --force --no-quickstart --namespace %s --ref %s',
|
||||||
$bin,
|
$bin,
|
||||||
$namespace);
|
$namespace,
|
||||||
|
$ref_key);
|
||||||
if ($err) {
|
if ($err) {
|
||||||
return $err;
|
return $err;
|
||||||
}
|
}
|
||||||
|
|
||||||
$err = phutil_passthru(
|
$err = phutil_passthru(
|
||||||
'%s adjust --force --namespace %s',
|
'%s adjust --force --namespace %s --ref %s',
|
||||||
$bin,
|
$bin,
|
||||||
$namespace);
|
$namespace,
|
||||||
|
$ref_key);
|
||||||
if ($err) {
|
if ($err) {
|
||||||
return $err;
|
return $err;
|
||||||
}
|
}
|
||||||
|
|
||||||
$tmp = new TempFile();
|
$tmp = new TempFile();
|
||||||
$err = phutil_passthru(
|
$err = phutil_passthru(
|
||||||
'%s dump --namespace %s > %s',
|
'%s dump --namespace %s --ref %s > %s',
|
||||||
$bin,
|
$bin,
|
||||||
$namespace,
|
$namespace,
|
||||||
|
$ref_key,
|
||||||
$tmp);
|
$tmp);
|
||||||
if ($err) {
|
if ($err) {
|
||||||
return $err;
|
return $err;
|
||||||
}
|
}
|
||||||
|
|
||||||
$err = phutil_passthru(
|
$err = phutil_passthru(
|
||||||
'%s destroy --force --namespace %s',
|
'%s destroy --force --namespace %s --ref %s',
|
||||||
$bin,
|
$bin,
|
||||||
$namespace);
|
$namespace,
|
||||||
|
$ref_key);
|
||||||
if ($err) {
|
if ($err) {
|
||||||
return $err;
|
return $err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ final class PhabricatorStorageManagementRenamespaceWorkflow
|
||||||
if (!strlen($input) && !$is_live) {
|
if (!strlen($input) && !$is_live) {
|
||||||
throw new PhutilArgumentUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
pht(
|
pht(
|
||||||
'Specify the dumpfile to read with "--in", or use "--live" to '.
|
'Specify the dumpfile to read with "--input", or use "--live" to '.
|
||||||
'generate one automatically.'));
|
'generate one automatically.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,11 +108,15 @@ final class PhabricatorStorageManagementRenamespaceWorkflow
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($is_live) {
|
if ($is_live) {
|
||||||
|
$api = $this->getSingleAPI();
|
||||||
|
$ref_key = $api->getRef()->getRefKey();
|
||||||
|
|
||||||
$root = dirname(phutil_get_library_root('phabricator'));
|
$root = dirname(phutil_get_library_root('phabricator'));
|
||||||
|
|
||||||
$future = new ExecFuture(
|
$future = new ExecFuture(
|
||||||
'%R dump',
|
'%R dump --ref %s',
|
||||||
$root.'/bin/storage');
|
$root.'/bin/storage',
|
||||||
|
$ref_key);
|
||||||
|
|
||||||
$lines = new LinesOfALargeExecFuture($future);
|
$lines = new LinesOfALargeExecFuture($future);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -15,7 +15,7 @@ final class PhabricatorStorageManagementShellWorkflow
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(PhutilArgumentParser $args) {
|
public function execute(PhutilArgumentParser $args) {
|
||||||
$api = $this->getAPI();
|
$api = $this->getSingleAPI();
|
||||||
list($host, $port) = $this->getBareHostAndPort($api->getHost());
|
list($host, $port) = $this->getBareHostAndPort($api->getHost());
|
||||||
|
|
||||||
$flag_port = $port
|
$flag_port = $port
|
||||||
|
|
|
@ -15,50 +15,54 @@ final class PhabricatorStorageManagementStatusWorkflow
|
||||||
}
|
}
|
||||||
|
|
||||||
public function didExecute(PhutilArgumentParser $args) {
|
public function didExecute(PhutilArgumentParser $args) {
|
||||||
$api = $this->getAPI();
|
foreach ($this->getAPIs() as $api) {
|
||||||
$patches = $this->getPatches();
|
$patches = $this->getPatches();
|
||||||
|
$applied = $api->getAppliedPatches();
|
||||||
|
|
||||||
$applied = $api->getAppliedPatches();
|
if ($applied === null) {
|
||||||
|
echo phutil_console_format(
|
||||||
|
"**%s**: %s\n",
|
||||||
|
pht('Database Not Initialized'),
|
||||||
|
pht('Run **%s** to initialize.', './bin/storage upgrade'));
|
||||||
|
|
||||||
if ($applied === null) {
|
return 1;
|
||||||
echo phutil_console_format(
|
|
||||||
"**%s**: %s\n",
|
|
||||||
pht('Database Not Initialized'),
|
|
||||||
pht('Run **%s** to initialize.', './bin/storage upgrade'));
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
$table = id(new PhutilConsoleTable())
|
|
||||||
->setShowHeader(false)
|
|
||||||
->addColumn('id', array('title' => pht('ID')))
|
|
||||||
->addColumn('status', array('title' => pht('Status')))
|
|
||||||
->addColumn('duration', array('title' => pht('Duration')))
|
|
||||||
->addColumn('type', array('title' => pht('Type')))
|
|
||||||
->addColumn('name', array('title' => pht('Name')));
|
|
||||||
|
|
||||||
$durations = $api->getPatchDurations();
|
|
||||||
|
|
||||||
foreach ($patches as $patch) {
|
|
||||||
$duration = idx($durations, $patch->getFullKey());
|
|
||||||
if ($duration === null) {
|
|
||||||
$duration = '-';
|
|
||||||
} else {
|
|
||||||
$duration = pht('%s us', new PhutilNumber($duration));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$table->addRow(array(
|
$ref = $api->getRef();
|
||||||
'id' => $patch->getFullKey(),
|
|
||||||
'status' => in_array($patch->getFullKey(), $applied)
|
|
||||||
? pht('Applied')
|
|
||||||
: pht('Not Applied'),
|
|
||||||
'duration' => $duration,
|
|
||||||
'type' => $patch->getType(),
|
|
||||||
'name' => $patch->getName(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
$table->draw();
|
$table = id(new PhutilConsoleTable())
|
||||||
|
->setShowHeader(false)
|
||||||
|
->addColumn('id', array('title' => pht('ID')))
|
||||||
|
->addColumn('host', array('title' => pht('Host')))
|
||||||
|
->addColumn('status', array('title' => pht('Status')))
|
||||||
|
->addColumn('duration', array('title' => pht('Duration')))
|
||||||
|
->addColumn('type', array('title' => pht('Type')))
|
||||||
|
->addColumn('name', array('title' => pht('Name')));
|
||||||
|
|
||||||
|
$durations = $api->getPatchDurations();
|
||||||
|
|
||||||
|
foreach ($patches as $patch) {
|
||||||
|
$duration = idx($durations, $patch->getFullKey());
|
||||||
|
if ($duration === null) {
|
||||||
|
$duration = '-';
|
||||||
|
} else {
|
||||||
|
$duration = pht('%s us', new PhutilNumber($duration));
|
||||||
|
}
|
||||||
|
|
||||||
|
$table->addRow(array(
|
||||||
|
'id' => $patch->getFullKey(),
|
||||||
|
'host' => $ref->getRefKey(),
|
||||||
|
'status' => in_array($patch->getFullKey(), $applied)
|
||||||
|
? pht('Applied')
|
||||||
|
: pht('Not Applied'),
|
||||||
|
'duration' => $duration,
|
||||||
|
'type' => $patch->getType(),
|
||||||
|
'name' => $patch->getName(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$table->draw();
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,16 +73,24 @@ final class PhabricatorStorageManagementUpgradeWorkflow
|
||||||
$init_only = $args->getArg('init-only');
|
$init_only = $args->getArg('init-only');
|
||||||
$no_adjust = $args->getArg('no-adjust');
|
$no_adjust = $args->getArg('no-adjust');
|
||||||
|
|
||||||
$this->upgradeSchemata($apply_only, $no_quickstart, $init_only);
|
$apis = $this->getMasterAPIs();
|
||||||
|
|
||||||
if ($no_adjust || $init_only || $apply_only) {
|
foreach ($apis as $api) {
|
||||||
$console->writeOut(
|
$this->upgradeSchemata($api, $apply_only, $no_quickstart, $init_only);
|
||||||
"%s\n",
|
|
||||||
pht('Declining to apply storage adjustments.'));
|
if ($no_adjust || $init_only || $apply_only) {
|
||||||
return 0;
|
$console->writeOut(
|
||||||
} else {
|
"%s\n",
|
||||||
return $this->adjustSchemata(false);
|
pht('Declining to apply storage adjustments.'));
|
||||||
|
} else {
|
||||||
|
$err = $this->adjustSchemata($api, false);
|
||||||
|
if ($err) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,20 +3,56 @@
|
||||||
abstract class PhabricatorStorageManagementWorkflow
|
abstract class PhabricatorStorageManagementWorkflow
|
||||||
extends PhabricatorManagementWorkflow {
|
extends PhabricatorManagementWorkflow {
|
||||||
|
|
||||||
private $api;
|
private $apis = array();
|
||||||
private $dryRun;
|
private $dryRun;
|
||||||
private $force;
|
private $force;
|
||||||
private $patches;
|
private $patches;
|
||||||
|
|
||||||
private $didInitialize;
|
private $didInitialize;
|
||||||
|
|
||||||
final public function getAPI() {
|
final public function setAPIs(array $apis) {
|
||||||
return $this->api;
|
$this->apis = $apis;
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function setAPI(PhabricatorStorageManagementAPI $api) {
|
final public function getAnyAPI() {
|
||||||
$this->api = $api;
|
return head($this->getAPIs());
|
||||||
return $this;
|
}
|
||||||
|
|
||||||
|
final public function getMasterAPIs() {
|
||||||
|
$apis = $this->getAPIs();
|
||||||
|
|
||||||
|
$results = array();
|
||||||
|
foreach ($apis as $api) {
|
||||||
|
if ($api->getRef()->getIsMaster()) {
|
||||||
|
$results[] = $api;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$results) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'This command only operates on database masters, but the selected '.
|
||||||
|
'database hosts do not include any masters.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function getSingleAPI() {
|
||||||
|
$apis = $this->getAPIs();
|
||||||
|
if (count($apis) == 1) {
|
||||||
|
return head($apis);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Phabricator is configured in cluster mode, with multiple database '.
|
||||||
|
'hosts. Use "--host" to specify which host you want to operate on.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function getAPIs() {
|
||||||
|
return $this->apis;
|
||||||
}
|
}
|
||||||
|
|
||||||
final protected function isDryRun() {
|
final protected function isDryRun() {
|
||||||
|
@ -73,22 +109,34 @@ abstract class PhabricatorStorageManagementWorkflow
|
||||||
|
|
||||||
public function didExecute(PhutilArgumentParser $args) {}
|
public function didExecute(PhutilArgumentParser $args) {}
|
||||||
|
|
||||||
private function loadSchemata() {
|
private function loadSchemata(PhabricatorStorageManagementAPI $api) {
|
||||||
$query = id(new PhabricatorConfigSchemaQuery())
|
$query = id(new PhabricatorConfigSchemaQuery());
|
||||||
->setAPI($this->getAPI());
|
|
||||||
|
|
||||||
$actual = $query->loadActualSchema();
|
$ref = $api->getRef();
|
||||||
$expect = $query->loadExpectedSchema();
|
$ref_key = $ref->getRefKey();
|
||||||
$comp = $query->buildComparisonSchema($expect, $actual);
|
|
||||||
|
|
||||||
return array($comp, $expect, $actual);
|
$query->setAPIs(array($api));
|
||||||
|
$query->setRefs(array($ref));
|
||||||
|
|
||||||
|
$actual = $query->loadActualSchemata();
|
||||||
|
$expect = $query->loadExpectedSchemata();
|
||||||
|
$comp = $query->buildComparisonSchemata($expect, $actual);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
$comp[$ref_key],
|
||||||
|
$expect[$ref_key],
|
||||||
|
$actual[$ref_key],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final protected function adjustSchemata($unsafe) {
|
final protected function adjustSchemata(
|
||||||
$lock = $this->lock();
|
PhabricatorStorageManagementAPI $api,
|
||||||
|
$unsafe) {
|
||||||
|
|
||||||
|
$lock = $this->lock($api);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$err = $this->doAdjustSchemata($unsafe);
|
$err = $this->doAdjustSchemata($api, $unsafe);
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
$lock->unlock();
|
$lock->unlock();
|
||||||
throw $ex;
|
throw $ex;
|
||||||
|
@ -99,15 +147,19 @@ abstract class PhabricatorStorageManagementWorkflow
|
||||||
return $err;
|
return $err;
|
||||||
}
|
}
|
||||||
|
|
||||||
final private function doAdjustSchemata($unsafe) {
|
final private function doAdjustSchemata(
|
||||||
|
PhabricatorStorageManagementAPI $api,
|
||||||
|
$unsafe) {
|
||||||
|
|
||||||
$console = PhutilConsole::getConsole();
|
$console = PhutilConsole::getConsole();
|
||||||
|
|
||||||
$console->writeOut(
|
$console->writeOut(
|
||||||
"%s\n",
|
"%s\n",
|
||||||
pht('Verifying database schemata...'));
|
pht(
|
||||||
|
'Verifying database schemata on "%s"...',
|
||||||
|
$api->getRef()->getRefKey()));
|
||||||
|
|
||||||
list($adjustments, $errors) = $this->findAdjustments();
|
list($adjustments, $errors) = $this->findAdjustments($api);
|
||||||
$api = $this->getAPI();
|
|
||||||
|
|
||||||
if (!$adjustments) {
|
if (!$adjustments) {
|
||||||
$console->writeOut(
|
$console->writeOut(
|
||||||
|
@ -415,8 +467,9 @@ abstract class PhabricatorStorageManagementWorkflow
|
||||||
return $this->printErrors($errors, $err);
|
return $this->printErrors($errors, $err);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function findAdjustments() {
|
private function findAdjustments(
|
||||||
list($comp, $expect, $actual) = $this->loadSchemata();
|
PhabricatorStorageManagementAPI $api) {
|
||||||
|
list($comp, $expect, $actual) = $this->loadSchemata($api);
|
||||||
|
|
||||||
$issue_charset = PhabricatorConfigStorageSchema::ISSUE_CHARSET;
|
$issue_charset = PhabricatorConfigStorageSchema::ISSUE_CHARSET;
|
||||||
$issue_collation = PhabricatorConfigStorageSchema::ISSUE_COLLATION;
|
$issue_collation = PhabricatorConfigStorageSchema::ISSUE_COLLATION;
|
||||||
|
@ -766,14 +819,15 @@ abstract class PhabricatorStorageManagementWorkflow
|
||||||
}
|
}
|
||||||
|
|
||||||
final protected function upgradeSchemata(
|
final protected function upgradeSchemata(
|
||||||
|
PhabricatorStorageManagementAPI $api,
|
||||||
$apply_only = null,
|
$apply_only = null,
|
||||||
$no_quickstart = false,
|
$no_quickstart = false,
|
||||||
$init_only = false) {
|
$init_only = false) {
|
||||||
|
|
||||||
$lock = $this->lock();
|
$lock = $this->lock($api);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->doUpgradeSchemata($apply_only, $no_quickstart, $init_only);
|
$this->doUpgradeSchemata($api, $apply_only, $no_quickstart, $init_only);
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
$lock->unlock();
|
$lock->unlock();
|
||||||
throw $ex;
|
throw $ex;
|
||||||
|
@ -783,13 +837,12 @@ abstract class PhabricatorStorageManagementWorkflow
|
||||||
}
|
}
|
||||||
|
|
||||||
final private function doUpgradeSchemata(
|
final private function doUpgradeSchemata(
|
||||||
|
PhabricatorStorageManagementAPI $api,
|
||||||
$apply_only,
|
$apply_only,
|
||||||
$no_quickstart,
|
$no_quickstart,
|
||||||
$init_only) {
|
$init_only) {
|
||||||
|
|
||||||
$api = $this->getAPI();
|
$applied = $api->getAppliedPatches();
|
||||||
|
|
||||||
$applied = $this->getApi()->getAppliedPatches();
|
|
||||||
if ($applied === null) {
|
if ($applied === null) {
|
||||||
if ($this->dryRun) {
|
if ($this->dryRun) {
|
||||||
echo pht(
|
echo pht(
|
||||||
|
@ -923,11 +976,13 @@ abstract class PhabricatorStorageManagementWorkflow
|
||||||
if (count($this->patches)) {
|
if (count($this->patches)) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht(
|
pht(
|
||||||
'Some patches could not be applied: %s',
|
'Some patches could not be applied to "%s": %s',
|
||||||
|
$api->getRef()->getRefKey(),
|
||||||
implode(', ', array_keys($this->patches))));
|
implode(', ', array_keys($this->patches))));
|
||||||
} else if (!$this->dryRun && !$apply_only) {
|
} else if (!$this->dryRun && !$apply_only) {
|
||||||
echo pht(
|
echo pht(
|
||||||
"Storage is up to date. Use '%s' for details.",
|
'Storage is up to date on "%s". Use "%s" for details.',
|
||||||
|
$api->getRef()->getRefKey(),
|
||||||
'storage status')."\n";
|
'storage status')."\n";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -955,9 +1010,9 @@ abstract class PhabricatorStorageManagementWorkflow
|
||||||
*
|
*
|
||||||
* @return PhabricatorGlobalLock
|
* @return PhabricatorGlobalLock
|
||||||
*/
|
*/
|
||||||
final protected function lock() {
|
final protected function lock(PhabricatorStorageManagementAPI $api) {
|
||||||
return PhabricatorGlobalLock::newLock(__CLASS__)
|
return PhabricatorGlobalLock::newLock(__CLASS__)
|
||||||
->useSpecificConnection($this->getApi()->getConn(null))
|
->useSpecificConnection($api->getConn(null))
|
||||||
->lock();
|
->lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue