1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-25 16:22:43 +01:00

(stable) Promote 2016 Week 8

This commit is contained in:
epriestley 2016-02-19 15:46:30 -08:00
commit ad53db0979
155 changed files with 2077 additions and 945 deletions

View file

@ -7,8 +7,8 @@
*/ */
return array( return array(
'names' => array( 'names' => array(
'core.pkg.css' => 'b59766ad', 'core.pkg.css' => '7935f211',
'core.pkg.js' => 'd7daa6d8', 'core.pkg.js' => '7d8faf57',
'darkconsole.pkg.js' => 'e7393ebb', 'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => '2de124c9', 'differential.pkg.css' => '2de124c9',
'differential.pkg.js' => 'd0cd0df6', 'differential.pkg.js' => 'd0cd0df6',
@ -17,19 +17,19 @@ return array(
'maniphest.pkg.css' => '4845691a', 'maniphest.pkg.css' => '4845691a',
'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' => '6378ef3d', 'rsrc/css/aphront/dark-console.css' => 'f54bf286',
'rsrc/css/aphront/dialog-view.css' => 'b4334e08', 'rsrc/css/aphront/dialog-view.css' => 'b4334e08',
'rsrc/css/aphront/lightbox-attachment.css' => '7acac05d', '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' => 'fd18389d', 'rsrc/css/aphront/multi-column.css' => 'fd18389d',
'rsrc/css/aphront/notification.css' => '9c279160', 'rsrc/css/aphront/notification.css' => '7f684b62',
'rsrc/css/aphront/panel-view.css' => '8427b78d', 'rsrc/css/aphront/panel-view.css' => '8427b78d',
'rsrc/css/aphront/phabricator-nav-view.css' => 'a24cb589', 'rsrc/css/aphront/phabricator-nav-view.css' => 'ac79a758',
'rsrc/css/aphront/table-view.css' => '6d01d468', 'rsrc/css/aphront/table-view.css' => '6d01d468',
'rsrc/css/aphront/tokenizer.css' => '056da01b', 'rsrc/css/aphront/tokenizer.css' => '056da01b',
'rsrc/css/aphront/tooltip.css' => '7672b60f', 'rsrc/css/aphront/tooltip.css' => '1a07aea8',
'rsrc/css/aphront/typeahead-browse.css' => 'd8581d2c', 'rsrc/css/aphront/typeahead-browse.css' => 'd8581d2c',
'rsrc/css/aphront/typeahead.css' => '0e403212', 'rsrc/css/aphront/typeahead.css' => 'd4f16145',
'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af', 'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af',
'rsrc/css/application/auth/auth.css' => '0877ed6e', 'rsrc/css/application/auth/auth.css' => '0877ed6e',
'rsrc/css/application/base/main-menu-view.css' => 'd00a795a', 'rsrc/css/application/base/main-menu-view.css' => 'd00a795a',
@ -65,10 +65,10 @@ return array(
'rsrc/css/application/differential/revision-list.css' => 'f3c47d33', 'rsrc/css/application/differential/revision-list.css' => 'f3c47d33',
'rsrc/css/application/differential/table-of-contents.css' => 'ae4b7a55', 'rsrc/css/application/differential/table-of-contents.css' => 'ae4b7a55',
'rsrc/css/application/diffusion/diffusion-icons.css' => '2941baf1', 'rsrc/css/application/diffusion/diffusion-icons.css' => '2941baf1',
'rsrc/css/application/diffusion/diffusion-readme.css' => '2106ea08', 'rsrc/css/application/diffusion/diffusion-readme.css' => '356a4f3c',
'rsrc/css/application/diffusion/diffusion-source.css' => '075ba788', 'rsrc/css/application/diffusion/diffusion-source.css' => '075ba788',
'rsrc/css/application/feed/feed.css' => 'ecd4ec57', 'rsrc/css/application/feed/feed.css' => 'ecd4ec57',
'rsrc/css/application/files/global-drag-and-drop.css' => '697324ad', 'rsrc/css/application/files/global-drag-and-drop.css' => '5c1b47c2',
'rsrc/css/application/flag/flag.css' => '5337623f', 'rsrc/css/application/flag/flag.css' => '5337623f',
'rsrc/css/application/harbormaster/harbormaster.css' => 'b0758ca5', 'rsrc/css/application/harbormaster/harbormaster.css' => 'b0758ca5',
'rsrc/css/application/herald/herald-test.css' => 'a52e323e', 'rsrc/css/application/herald/herald-test.css' => 'a52e323e',
@ -81,10 +81,10 @@ return array(
'rsrc/css/application/owners/owners-path-editor.css' => '2f00933b', 'rsrc/css/application/owners/owners-path-editor.css' => '2f00933b',
'rsrc/css/application/paste/paste.css' => 'a5157c48', 'rsrc/css/application/paste/paste.css' => 'a5157c48',
'rsrc/css/application/people/people-profile.css' => '2473d929', 'rsrc/css/application/people/people-profile.css' => '2473d929',
'rsrc/css/application/phame/phame.css' => '1dbbacf9', 'rsrc/css/application/phame/phame.css' => '4ca6fd6c',
'rsrc/css/application/pholio/pholio-edit.css' => '3ad9d1ee', 'rsrc/css/application/pholio/pholio-edit.css' => '3ad9d1ee',
'rsrc/css/application/pholio/pholio-inline-comments.css' => '8e545e49', 'rsrc/css/application/pholio/pholio-inline-comments.css' => '8e545e49',
'rsrc/css/application/pholio/pholio.css' => '95174bdd', 'rsrc/css/application/pholio/pholio.css' => 'ca89d380',
'rsrc/css/application/phortune/phortune-credit-card-form.css' => '8391eb02', 'rsrc/css/application/phortune/phortune-credit-card-form.css' => '8391eb02',
'rsrc/css/application/phortune/phortune.css' => '9149f103', 'rsrc/css/application/phortune/phortune.css' => '9149f103',
'rsrc/css/application/phrequent/phrequent.css' => 'ffc185ad', 'rsrc/css/application/phrequent/phrequent.css' => 'ffc185ad',
@ -92,9 +92,9 @@ return array(
'rsrc/css/application/policy/policy-edit.css' => '815c66f7', 'rsrc/css/application/policy/policy-edit.css' => '815c66f7',
'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43', 'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43',
'rsrc/css/application/policy/policy.css' => '957ea14c', 'rsrc/css/application/policy/policy.css' => '957ea14c',
'rsrc/css/application/ponder/ponder-view.css' => '7b0df4da', 'rsrc/css/application/ponder/ponder-view.css' => 'b40dc156',
'rsrc/css/application/project/project-card-view.css' => '9418c97d', 'rsrc/css/application/project/project-card-view.css' => '9418c97d',
'rsrc/css/application/project/project-view.css' => '4693497c', 'rsrc/css/application/project/project-view.css' => '83bb6654',
'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733', 'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733',
'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5', 'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5',
'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '8d8b92cd', 'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '8d8b92cd',
@ -104,7 +104,7 @@ return array(
'rsrc/css/application/tokens/tokens.css' => '3d0f239e', 'rsrc/css/application/tokens/tokens.css' => '3d0f239e',
'rsrc/css/application/uiexample/example.css' => '528b19de', 'rsrc/css/application/uiexample/example.css' => '528b19de',
'rsrc/css/core/core.css' => '5b3563c8', 'rsrc/css/core/core.css' => '5b3563c8',
'rsrc/css/core/remarkup.css' => 'e1c8b32f', 'rsrc/css/core/remarkup.css' => 'fc228f08',
'rsrc/css/core/syntax.css' => '9fd11da8', 'rsrc/css/core/syntax.css' => '9fd11da8',
'rsrc/css/core/z-index.css' => '5b6fcf3f', 'rsrc/css/core/z-index.css' => '5b6fcf3f',
'rsrc/css/diviner/diviner-shared.css' => 'aa3656aa', 'rsrc/css/diviner/diviner-shared.css' => 'aa3656aa',
@ -123,30 +123,30 @@ return array(
'rsrc/css/phui/phui-action-panel.css' => '91c7b835', 'rsrc/css/phui/phui-action-panel.css' => '91c7b835',
'rsrc/css/phui/phui-badge.css' => 'f25c3476', 'rsrc/css/phui/phui-badge.css' => 'f25c3476',
'rsrc/css/phui/phui-big-info-view.css' => 'bd903741', 'rsrc/css/phui/phui-big-info-view.css' => 'bd903741',
'rsrc/css/phui/phui-box.css' => '6e8ac7fd', 'rsrc/css/phui/phui-box.css' => 'dd1294d3',
'rsrc/css/phui/phui-button.css' => 'd6ac72db', 'rsrc/css/phui/phui-button.css' => 'edf464e9',
'rsrc/css/phui/phui-chart.css' => '6bf6f78e', 'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
'rsrc/css/phui/phui-crumbs-view.css' => '79d536e5', 'rsrc/css/phui/phui-crumbs-view.css' => '79d536e5',
'rsrc/css/phui/phui-document-pro.css' => '8799acf7', 'rsrc/css/phui/phui-document-pro.css' => 'a8872307',
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf', 'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
'rsrc/css/phui/phui-document.css' => '9c71d2bf', 'rsrc/css/phui/phui-document.css' => '9c71d2bf',
'rsrc/css/phui/phui-feed-story.css' => '04aec08f', 'rsrc/css/phui/phui-feed-story.css' => '04aec08f',
'rsrc/css/phui/phui-fontkit.css' => '9cda225e', 'rsrc/css/phui/phui-fontkit.css' => '9cda225e',
'rsrc/css/phui/phui-form-view.css' => '4a1a0f5e', 'rsrc/css/phui/phui-form-view.css' => '4a1a0f5e',
'rsrc/css/phui/phui-form.css' => '0b98e572', 'rsrc/css/phui/phui-form.css' => 'aac1d51d',
'rsrc/css/phui/phui-header-view.css' => 'd53cc835', 'rsrc/css/phui/phui-header-view.css' => '50c5cb6a',
'rsrc/css/phui/phui-hovercard.css' => 'de1a2119', 'rsrc/css/phui/phui-hovercard.css' => 'de1a2119',
'rsrc/css/phui/phui-icon-set-selector.css' => '1ab67aad', 'rsrc/css/phui/phui-icon-set-selector.css' => '1ab67aad',
'rsrc/css/phui/phui-icon.css' => '3f33ab57', 'rsrc/css/phui/phui-icon.css' => '3f33ab57',
'rsrc/css/phui/phui-image-mask.css' => '5a8b09c8', 'rsrc/css/phui/phui-image-mask.css' => 'a8498f9c',
'rsrc/css/phui/phui-info-panel.css' => '27ea50a1', 'rsrc/css/phui/phui-info-panel.css' => '27ea50a1',
'rsrc/css/phui/phui-info-view.css' => '6d7c3509', 'rsrc/css/phui/phui-info-view.css' => '6d7c3509',
'rsrc/css/phui/phui-list.css' => '9da2aa00', 'rsrc/css/phui/phui-list.css' => '9da2aa00',
'rsrc/css/phui/phui-object-box.css' => '407eaf5a', 'rsrc/css/phui/phui-object-box.css' => '407eaf5a',
'rsrc/css/phui/phui-object-item-list-view.css' => 'be31c3a7', 'rsrc/css/phui/phui-object-item-list-view.css' => '18b2ce8e',
'rsrc/css/phui/phui-pager.css' => 'bea33d23', 'rsrc/css/phui/phui-pager.css' => 'bea33d23',
'rsrc/css/phui/phui-pinboard-view.css' => '2495140e', 'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
'rsrc/css/phui/phui-profile-menu.css' => 'f709256c', 'rsrc/css/phui/phui-profile-menu.css' => '7e92a89a',
'rsrc/css/phui/phui-property-list-view.css' => '27b2849e', 'rsrc/css/phui/phui-property-list-view.css' => '27b2849e',
'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591', 'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591',
'rsrc/css/phui/phui-segment-bar-view.css' => '46342871', 'rsrc/css/phui/phui-segment-bar-view.css' => '46342871',
@ -154,10 +154,11 @@ return array(
'rsrc/css/phui/phui-status.css' => '888cedb8', 'rsrc/css/phui/phui-status.css' => '888cedb8',
'rsrc/css/phui/phui-tag-view.css' => '9d5d4400', 'rsrc/css/phui/phui-tag-view.css' => '9d5d4400',
'rsrc/css/phui/phui-timeline-view.css' => '2efceff8', 'rsrc/css/phui/phui-timeline-view.css' => '2efceff8',
'rsrc/css/phui/phui-two-column-view.css' => 'c75bfc5b', 'rsrc/css/phui/phui-two-column-view.css' => '0763177e',
'rsrc/css/phui/workboards/phui-workboard.css' => 'e9e56029', 'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7',
'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647',
'rsrc/css/phui/workboards/phui-workcard.css' => '3646fb96', 'rsrc/css/phui/workboards/phui-workcard.css' => '3646fb96',
'rsrc/css/phui/workboards/phui-workpanel.css' => 'a78c0661', 'rsrc/css/phui/workboards/phui-workpanel.css' => '92197373',
'rsrc/css/sprite-login.css' => '60e8560e', 'rsrc/css/sprite-login.css' => '60e8560e',
'rsrc/css/sprite-menu.css' => '9dd65b92', 'rsrc/css/sprite-menu.css' => '9dd65b92',
'rsrc/css/sprite-tokens.css' => '4f399012', 'rsrc/css/sprite-tokens.css' => '4f399012',
@ -461,7 +462,7 @@ return array(
'rsrc/js/core/Notification.js' => 'ccf1cbf8', 'rsrc/js/core/Notification.js' => 'ccf1cbf8',
'rsrc/js/core/Prefab.js' => 'e67df814', 'rsrc/js/core/Prefab.js' => 'e67df814',
'rsrc/js/core/ShapedRequest.js' => '7cbe244b', 'rsrc/js/core/ShapedRequest.js' => '7cbe244b',
'rsrc/js/core/TextAreaUtils.js' => '9e54692d', 'rsrc/js/core/TextAreaUtils.js' => '5813016a',
'rsrc/js/core/Title.js' => 'df5e11d2', 'rsrc/js/core/Title.js' => 'df5e11d2',
'rsrc/js/core/ToolTip.js' => '6323f942', 'rsrc/js/core/ToolTip.js' => '6323f942',
'rsrc/js/core/behavior-active-nav.js' => 'e379b58e', 'rsrc/js/core/behavior-active-nav.js' => 'e379b58e',
@ -500,7 +501,7 @@ return array(
'rsrc/js/core/behavior-time-typeahead.js' => 'f80d6bf0', 'rsrc/js/core/behavior-time-typeahead.js' => 'f80d6bf0',
'rsrc/js/core/behavior-toggle-class.js' => '5d7c9f33', 'rsrc/js/core/behavior-toggle-class.js' => '5d7c9f33',
'rsrc/js/core/behavior-tokenizer.js' => 'b3a4b884', 'rsrc/js/core/behavior-tokenizer.js' => 'b3a4b884',
'rsrc/js/core/behavior-tooltip.js' => '3ee3408b', 'rsrc/js/core/behavior-tooltip.js' => '42fcb747',
'rsrc/js/core/behavior-watch-anchor.js' => '9f36c42d', 'rsrc/js/core/behavior-watch-anchor.js' => '9f36c42d',
'rsrc/js/core/behavior-workflow.js' => '0a3f3021', 'rsrc/js/core/behavior-workflow.js' => '0a3f3021',
'rsrc/js/core/phtize.js' => 'd254d646', 'rsrc/js/core/phtize.js' => 'd254d646',
@ -517,15 +518,15 @@ return array(
'symbols' => array( 'symbols' => array(
'almanac-css' => 'dbb9b3af', 'almanac-css' => 'dbb9b3af',
'aphront-bars' => '231ac33c', 'aphront-bars' => '231ac33c',
'aphront-dark-console-css' => '6378ef3d', 'aphront-dark-console-css' => 'f54bf286',
'aphront-dialog-view-css' => 'b4334e08', 'aphront-dialog-view-css' => 'b4334e08',
'aphront-list-filter-view-css' => '5d6f0526', 'aphront-list-filter-view-css' => '5d6f0526',
'aphront-multi-column-view-css' => 'fd18389d', 'aphront-multi-column-view-css' => 'fd18389d',
'aphront-panel-view-css' => '8427b78d', 'aphront-panel-view-css' => '8427b78d',
'aphront-table-view-css' => '6d01d468', 'aphront-table-view-css' => '6d01d468',
'aphront-tokenizer-control-css' => '056da01b', 'aphront-tokenizer-control-css' => '056da01b',
'aphront-tooltip-css' => '7672b60f', 'aphront-tooltip-css' => '1a07aea8',
'aphront-typeahead-control-css' => '0e403212', 'aphront-typeahead-control-css' => 'd4f16145',
'auth-css' => '0877ed6e', 'auth-css' => '0877ed6e',
'bulk-job-css' => 'df9c1d4a', 'bulk-job-css' => 'df9c1d4a',
'changeset-view-manager' => 'a2828756', 'changeset-view-manager' => 'a2828756',
@ -550,13 +551,13 @@ return array(
'differential-revision-list-css' => 'f3c47d33', 'differential-revision-list-css' => 'f3c47d33',
'differential-table-of-contents-css' => 'ae4b7a55', 'differential-table-of-contents-css' => 'ae4b7a55',
'diffusion-icons-css' => '2941baf1', 'diffusion-icons-css' => '2941baf1',
'diffusion-readme-css' => '2106ea08', 'diffusion-readme-css' => '356a4f3c',
'diffusion-source-css' => '075ba788', 'diffusion-source-css' => '075ba788',
'diviner-shared-css' => 'aa3656aa', 'diviner-shared-css' => 'aa3656aa',
'font-aleo' => '8bdb2835', 'font-aleo' => '8bdb2835',
'font-fontawesome' => 'c43323c5', 'font-fontawesome' => 'c43323c5',
'font-lato' => 'c7ccd872', 'font-lato' => 'c7ccd872',
'global-drag-and-drop-css' => '697324ad', 'global-drag-and-drop-css' => '5c1b47c2',
'harbormaster-css' => 'b0758ca5', 'harbormaster-css' => 'b0758ca5',
'herald-css' => '826075fa', 'herald-css' => '826075fa',
'herald-rule-editor' => '746ca158', 'herald-rule-editor' => '746ca158',
@ -647,7 +648,7 @@ return array(
'javelin-behavior-phabricator-reveal-content' => '60821bc7', 'javelin-behavior-phabricator-reveal-content' => '60821bc7',
'javelin-behavior-phabricator-search-typeahead' => '06c32383', 'javelin-behavior-phabricator-search-typeahead' => '06c32383',
'javelin-behavior-phabricator-show-older-transactions' => 'dbbf48b6', 'javelin-behavior-phabricator-show-older-transactions' => 'dbbf48b6',
'javelin-behavior-phabricator-tooltips' => '3ee3408b', 'javelin-behavior-phabricator-tooltips' => '42fcb747',
'javelin-behavior-phabricator-transaction-comment-form' => 'b23b49e6', 'javelin-behavior-phabricator-transaction-comment-form' => 'b23b49e6',
'javelin-behavior-phabricator-transaction-list' => '13c739ea', 'javelin-behavior-phabricator-transaction-list' => '13c739ea',
'javelin-behavior-phabricator-watch-anchor' => '9f36c42d', 'javelin-behavior-phabricator-watch-anchor' => '9f36c42d',
@ -760,21 +761,21 @@ return array(
'phabricator-keyboard-shortcut' => '1ae869f2', 'phabricator-keyboard-shortcut' => '1ae869f2',
'phabricator-keyboard-shortcut-manager' => 'c1700f6f', 'phabricator-keyboard-shortcut-manager' => 'c1700f6f',
'phabricator-main-menu-view' => 'd00a795a', 'phabricator-main-menu-view' => 'd00a795a',
'phabricator-nav-view-css' => 'a24cb589', 'phabricator-nav-view-css' => 'ac79a758',
'phabricator-notification' => 'ccf1cbf8', 'phabricator-notification' => 'ccf1cbf8',
'phabricator-notification-css' => '9c279160', 'phabricator-notification-css' => '7f684b62',
'phabricator-notification-menu-css' => 'f31c0bde', 'phabricator-notification-menu-css' => 'f31c0bde',
'phabricator-object-selector-css' => '85ee8ce6', 'phabricator-object-selector-css' => '85ee8ce6',
'phabricator-phtize' => 'd254d646', 'phabricator-phtize' => 'd254d646',
'phabricator-prefab' => 'e67df814', 'phabricator-prefab' => 'e67df814',
'phabricator-remarkup-css' => 'e1c8b32f', 'phabricator-remarkup-css' => 'fc228f08',
'phabricator-search-results-css' => '7dea472c', 'phabricator-search-results-css' => '7dea472c',
'phabricator-shaped-request' => '7cbe244b', 'phabricator-shaped-request' => '7cbe244b',
'phabricator-side-menu-view-css' => '3a3d9f41', 'phabricator-side-menu-view-css' => '3a3d9f41',
'phabricator-slowvote-css' => 'da0afb1b', 'phabricator-slowvote-css' => 'da0afb1b',
'phabricator-source-code-view-css' => 'cbeef983', 'phabricator-source-code-view-css' => 'cbeef983',
'phabricator-standard-page-view' => 'e709f6d0', 'phabricator-standard-page-view' => 'e709f6d0',
'phabricator-textareautils' => '9e54692d', 'phabricator-textareautils' => '5813016a',
'phabricator-title' => 'df5e11d2', 'phabricator-title' => 'df5e11d2',
'phabricator-tooltip' => '6323f942', 'phabricator-tooltip' => '6323f942',
'phabricator-ui-example-css' => '528b19de', 'phabricator-ui-example-css' => '528b19de',
@ -789,8 +790,8 @@ 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' => '5b6fcf3f', 'phabricator-zindex-css' => '5b6fcf3f',
'phame-css' => '1dbbacf9', 'phame-css' => '4ca6fd6c',
'pholio-css' => '95174bdd', 'pholio-css' => 'ca89d380',
'pholio-edit-css' => '3ad9d1ee', 'pholio-edit-css' => '3ad9d1ee',
'pholio-inline-comments-css' => '8e545e49', 'pholio-inline-comments-css' => '8e545e49',
'phortune-credit-card-form' => '2290aeef', 'phortune-credit-card-form' => '2290aeef',
@ -801,8 +802,8 @@ return array(
'phui-action-panel-css' => '91c7b835', 'phui-action-panel-css' => '91c7b835',
'phui-badge-view-css' => 'f25c3476', 'phui-badge-view-css' => 'f25c3476',
'phui-big-info-view-css' => 'bd903741', 'phui-big-info-view-css' => 'bd903741',
'phui-box-css' => '6e8ac7fd', 'phui-box-css' => 'dd1294d3',
'phui-button-css' => 'd6ac72db', 'phui-button-css' => 'edf464e9',
'phui-calendar-css' => 'ccabe893', 'phui-calendar-css' => 'ccabe893',
'phui-calendar-day-css' => 'd1cf6f93', 'phui-calendar-day-css' => 'd1cf6f93',
'phui-calendar-list-css' => 'c1c7f338', 'phui-calendar-list-css' => 'c1c7f338',
@ -811,27 +812,27 @@ return array(
'phui-crumbs-view-css' => '79d536e5', 'phui-crumbs-view-css' => '79d536e5',
'phui-document-summary-view-css' => '9ca48bdf', 'phui-document-summary-view-css' => '9ca48bdf',
'phui-document-view-css' => '9c71d2bf', 'phui-document-view-css' => '9c71d2bf',
'phui-document-view-pro-css' => '8799acf7', 'phui-document-view-pro-css' => 'a8872307',
'phui-feed-story-css' => '04aec08f', 'phui-feed-story-css' => '04aec08f',
'phui-font-icon-base-css' => 'ecbbb4c2', 'phui-font-icon-base-css' => 'ecbbb4c2',
'phui-fontkit-css' => '9cda225e', 'phui-fontkit-css' => '9cda225e',
'phui-form-css' => '0b98e572', 'phui-form-css' => 'aac1d51d',
'phui-form-view-css' => '4a1a0f5e', 'phui-form-view-css' => '4a1a0f5e',
'phui-header-view-css' => 'd53cc835', 'phui-header-view-css' => '50c5cb6a',
'phui-hovercard' => '1bd28176', 'phui-hovercard' => '1bd28176',
'phui-hovercard-view-css' => 'de1a2119', 'phui-hovercard-view-css' => 'de1a2119',
'phui-icon-set-selector-css' => '1ab67aad', 'phui-icon-set-selector-css' => '1ab67aad',
'phui-icon-view-css' => '3f33ab57', 'phui-icon-view-css' => '3f33ab57',
'phui-image-mask-css' => '5a8b09c8', 'phui-image-mask-css' => 'a8498f9c',
'phui-info-panel-css' => '27ea50a1', 'phui-info-panel-css' => '27ea50a1',
'phui-info-view-css' => '6d7c3509', 'phui-info-view-css' => '6d7c3509',
'phui-inline-comment-view-css' => '0fdb3667', 'phui-inline-comment-view-css' => '0fdb3667',
'phui-list-view-css' => '9da2aa00', 'phui-list-view-css' => '9da2aa00',
'phui-object-box-css' => '407eaf5a', 'phui-object-box-css' => '407eaf5a',
'phui-object-item-list-view-css' => 'be31c3a7', 'phui-object-item-list-view-css' => '18b2ce8e',
'phui-pager-css' => 'bea33d23', 'phui-pager-css' => 'bea33d23',
'phui-pinboard-view-css' => '2495140e', 'phui-pinboard-view-css' => '2495140e',
'phui-profile-menu-css' => 'f709256c', 'phui-profile-menu-css' => '7e92a89a',
'phui-property-list-view-css' => '27b2849e', 'phui-property-list-view-css' => '27b2849e',
'phui-remarkup-preview-css' => '1a8f2591', 'phui-remarkup-preview-css' => '1a8f2591',
'phui-segment-bar-view-css' => '46342871', 'phui-segment-bar-view-css' => '46342871',
@ -840,10 +841,11 @@ return array(
'phui-tag-view-css' => '9d5d4400', 'phui-tag-view-css' => '9d5d4400',
'phui-theme-css' => 'ab7b848c', 'phui-theme-css' => 'ab7b848c',
'phui-timeline-view-css' => '2efceff8', 'phui-timeline-view-css' => '2efceff8',
'phui-two-column-view-css' => 'c75bfc5b', 'phui-two-column-view-css' => '0763177e',
'phui-workboard-view-css' => 'e9e56029', 'phui-workboard-color-css' => 'ac6fe6a7',
'phui-workboard-view-css' => 'e6d89647',
'phui-workcard-view-css' => '3646fb96', 'phui-workcard-view-css' => '3646fb96',
'phui-workpanel-view-css' => 'a78c0661', 'phui-workpanel-view-css' => '92197373',
'phuix-action-list-view' => 'b5c256b8', 'phuix-action-list-view' => 'b5c256b8',
'phuix-action-view' => '8cf6d262', 'phuix-action-view' => '8cf6d262',
'phuix-autocomplete' => '9196fb06', 'phuix-autocomplete' => '9196fb06',
@ -853,9 +855,9 @@ return array(
'policy-css' => '957ea14c', 'policy-css' => '957ea14c',
'policy-edit-css' => '815c66f7', 'policy-edit-css' => '815c66f7',
'policy-transaction-detail-css' => '82100a43', 'policy-transaction-detail-css' => '82100a43',
'ponder-view-css' => '7b0df4da', 'ponder-view-css' => 'b40dc156',
'project-card-view-css' => '9418c97d', 'project-card-view-css' => '9418c97d',
'project-view-css' => '4693497c', 'project-view-css' => '83bb6654',
'releeph-core' => '9b3c5733', 'releeph-core' => '9b3c5733',
'releeph-preview-branch' => 'b7a6f4a5', 'releeph-preview-branch' => 'b7a6f4a5',
'releeph-request-differential-create-dialog' => '8d8b92cd', 'releeph-request-differential-create-dialog' => '8d8b92cd',
@ -1117,12 +1119,6 @@ return array(
'javelin-util', 'javelin-util',
'javelin-uri', 'javelin-uri',
), ),
'3ee3408b' => array(
'javelin-behavior',
'javelin-behavior-device',
'javelin-stratcom',
'phabricator-tooltip',
),
'3f5d6dbf' => array( '3f5d6dbf' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-dom', 'javelin-dom',
@ -1138,6 +1134,12 @@ return array(
'javelin-dom', 'javelin-dom',
'javelin-request', 'javelin-request',
), ),
'42fcb747' => array(
'javelin-behavior',
'javelin-behavior-device',
'javelin-stratcom',
'phabricator-tooltip',
),
'44959b73' => array( '44959b73' => array(
'javelin-util', 'javelin-util',
'javelin-uri', 'javelin-uri',
@ -1273,6 +1275,11 @@ return array(
'javelin-request', 'javelin-request',
'javelin-util', 'javelin-util',
), ),
'5813016a' => array(
'javelin-install',
'javelin-dom',
'javelin-vector',
),
'59a7976a' => array( '59a7976a' => array(
'javelin-install', 'javelin-install',
'javelin-dom', 'javelin-dom',
@ -1573,6 +1580,9 @@ return array(
'phuix-icon-view', 'phuix-icon-view',
'phabricator-prefab', 'phabricator-prefab',
), ),
92197373 => array(
'phui-workcard-view-css',
),
'93d0c9e3' => array( '93d0c9e3' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-stratcom', 'javelin-stratcom',
@ -1616,11 +1626,6 @@ return array(
'phabricator-phtize', 'phabricator-phtize',
'changeset-view-manager', 'changeset-view-manager',
), ),
'9e54692d' => array(
'javelin-install',
'javelin-dom',
'javelin-vector',
),
'9f36c42d' => array( '9f36c42d' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-stratcom', 'javelin-stratcom',
@ -1659,9 +1664,6 @@ return array(
'javelin-install', 'javelin-install',
'javelin-dom', 'javelin-dom',
), ),
'a78c0661' => array(
'phui-workcard-view-css',
),
'a80d0378' => array( 'a80d0378' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-stratcom', 'javelin-stratcom',

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_owners.owners_package
ADD viewPolicy VARBINARY(64) NOT NULL;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_owners.owners_package
ADD editPolicy VARBINARY(64) NOT NULL;

View file

@ -0,0 +1,2 @@
UPDATE {$NAMESPACE}_owners.owners_package
SET viewPolicy = 'users' WHERE viewPolicy = '';

View file

@ -0,0 +1,2 @@
UPDATE {$NAMESPACE}_owners.owners_package
SET editPolicy = 'users' WHERE editPolicy = '';

View file

@ -0,0 +1,4 @@
/* Make callsigns nullable, and thus optional. */
ALTER TABLE {$NAMESPACE}_repository.repository
CHANGE callsign callsign VARCHAR(32) COLLATE {$COLLATE_SORT};

View file

@ -32,14 +32,14 @@ $root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/__init_script__.php'; require_once $root.'/scripts/__init_script__.php';
if ($argc < 2) { if ($argc < 2) {
throw new Exception(pht('usage: commit-hook <callsign>')); throw new Exception(pht('usage: commit-hook <repository>'));
} }
$engine = new DiffusionCommitHookEngine(); $engine = new DiffusionCommitHookEngine();
$repository = id(new PhabricatorRepositoryQuery()) $repository = id(new PhabricatorRepositoryQuery())
->setViewer(PhabricatorUser::getOmnipotentUser()) ->setViewer(PhabricatorUser::getOmnipotentUser())
->withCallsigns(array($argv[1])) ->withIdentifiers(array($argv[1]))
->needProjectPHIDs(true) ->needProjectPHIDs(true)
->executeOne(); ->executeOne();
@ -62,8 +62,9 @@ if ($repository->isGit() || $repository->isHg()) {
if (!strlen($username)) { if (!strlen($username)) {
throw new Exception( throw new Exception(
pht( pht(
'Usage: %s should be defined!', 'No Direct Pushes: You are pushing directly to a repository hosted '.
DiffusionCommitHookEngine::ENV_USER)); 'by Phabricator. This will not work. See "No Direct Pushes" in the '.
'documentation for more information.'));
} }
if ($repository->isHg()) { if ($repository->isHg()) {
@ -77,7 +78,7 @@ if ($repository->isGit() || $repository->isHg()) {
// specify the correct user; read this user out of the commit log. // specify the correct user; read this user out of the commit log.
if ($argc < 4) { if ($argc < 4) {
throw new Exception(pht('usage: commit-hook <callsign> <repo> <txn>')); throw new Exception(pht('usage: commit-hook <repository> <repo> <txn>'));
} }
$svn_repo = $argv[2]; $svn_repo = $argv[2];

View file

@ -6,7 +6,7 @@ require_once $root.'/scripts/__init_script__.php';
$args = new PhutilArgumentParser($argv); $args = new PhutilArgumentParser($argv);
$args->setSynopsis(<<<EOSYNOPSIS $args->setSynopsis(<<<EOSYNOPSIS
**clear_repository_symbols.php** [__options__] __callsign__ **clear_repository_symbols.php** [__options__] __repository__
Clear repository symbols. Clear repository symbols.
EOSYNOPSIS EOSYNOPSIS
@ -15,24 +15,26 @@ $args->parseStandardArguments();
$args->parse( $args->parse(
array( array(
array( array(
'name' => 'callsign', 'name' => 'repository',
'wildcard' => true, 'wildcard' => true,
), ),
)); ));
$callsigns = $args->getArg('callsign'); $identifiers = $args->getArg('repository');
if (count($callsigns) !== 1) { if (count($identifiers) !== 1) {
$args->printHelpAndExit(); $args->printHelpAndExit();
} }
$callsign = head($callsigns); $identifier = head($identifiers);
$repository = id(new PhabricatorRepositoryQuery()) $repository = id(new PhabricatorRepositoryQuery())
->setViewer(PhabricatorUser::getOmnipotentUser()) ->setViewer(PhabricatorUser::getOmnipotentUser())
->withCallsigns($callsigns) ->withIdentifiers($identifiers)
->executeOne(); ->executeOne();
if (!$repository) { if (!$repository) {
echo pht("Repository '%s' does not exist.", $callsign); echo tsprintf(
"%s\n",
pht('Repository "%s" does not exist.', $identifier));
exit(1); exit(1);
} }

View file

@ -6,7 +6,7 @@ require_once $root.'/scripts/__init_script__.php';
$args = new PhutilArgumentParser($argv); $args = new PhutilArgumentParser($argv);
$args->setSynopsis(<<<EOSYNOPSIS $args->setSynopsis(<<<EOSYNOPSIS
**import_repository_symbols.php** [__options__] __callsign__ < symbols **import_repository_symbols.php** [__options__] __repository__ < symbols
Import repository symbols (symbols are read from stdin). Import repository symbols (symbols are read from stdin).
EOSYNOPSIS EOSYNOPSIS
@ -35,24 +35,26 @@ $args->parse(
'be part of a single transaction.'), 'be part of a single transaction.'),
), ),
array( array(
'name' => 'callsign', 'name' => 'repository',
'wildcard' => true, 'wildcard' => true,
), ),
)); ));
$callsigns = $args->getArg('callsign'); $identifiers = $args->getArg('repository');
if (count($callsigns) !== 1) { if (count($identifiers) !== 1) {
$args->printHelpAndExit(); $args->printHelpAndExit();
} }
$callsign = head($callsigns); $identifier = head($identifiers);
$repository = id(new PhabricatorRepositoryQuery()) $repository = id(new PhabricatorRepositoryQuery())
->setViewer(PhabricatorUser::getOmnipotentUser()) ->setViewer(PhabricatorUser::getOmnipotentUser())
->withCallsigns($callsigns) ->withIdentifiers($identifiers)
->executeOne(); ->executeOne();
if (!$repository) { if (!$repository) {
echo pht("Repository '%s' does not exist.", $callsign); echo tsprintf(
"%s\n",
pht('Repository "%s" does not exist.', $identifier));
exit(1); exit(1);
} }

View file

@ -689,7 +689,7 @@ phutil_register_library_map(array(
'DiffusionPreCommitRefRepositoryHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefRepositoryHeraldField.php', 'DiffusionPreCommitRefRepositoryHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefRepositoryHeraldField.php',
'DiffusionPreCommitRefRepositoryProjectsHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefRepositoryProjectsHeraldField.php', 'DiffusionPreCommitRefRepositoryProjectsHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefRepositoryProjectsHeraldField.php',
'DiffusionPreCommitRefTypeHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefTypeHeraldField.php', 'DiffusionPreCommitRefTypeHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefTypeHeraldField.php',
'DiffusionPullEventGarbageCollector' => 'applications/diffusion/DiffusionPullEventGarbageCollector.php', 'DiffusionPullEventGarbageCollector' => 'applications/diffusion/garbagecollector/DiffusionPullEventGarbageCollector.php',
'DiffusionPushCapability' => 'applications/diffusion/capability/DiffusionPushCapability.php', 'DiffusionPushCapability' => 'applications/diffusion/capability/DiffusionPushCapability.php',
'DiffusionPushEventViewController' => 'applications/diffusion/controller/DiffusionPushEventViewController.php', 'DiffusionPushEventViewController' => 'applications/diffusion/controller/DiffusionPushEventViewController.php',
'DiffusionPushLogController' => 'applications/diffusion/controller/DiffusionPushLogController.php', 'DiffusionPushLogController' => 'applications/diffusion/controller/DiffusionPushLogController.php',
@ -2665,6 +2665,8 @@ phutil_register_library_map(array(
'PhabricatorOwnersCustomFieldStorage' => 'applications/owners/storage/PhabricatorOwnersCustomFieldStorage.php', 'PhabricatorOwnersCustomFieldStorage' => 'applications/owners/storage/PhabricatorOwnersCustomFieldStorage.php',
'PhabricatorOwnersCustomFieldStringIndex' => 'applications/owners/storage/PhabricatorOwnersCustomFieldStringIndex.php', 'PhabricatorOwnersCustomFieldStringIndex' => 'applications/owners/storage/PhabricatorOwnersCustomFieldStringIndex.php',
'PhabricatorOwnersDAO' => 'applications/owners/storage/PhabricatorOwnersDAO.php', 'PhabricatorOwnersDAO' => 'applications/owners/storage/PhabricatorOwnersDAO.php',
'PhabricatorOwnersDefaultEditCapability' => 'applications/owners/capability/PhabricatorOwnersDefaultEditCapability.php',
'PhabricatorOwnersDefaultViewCapability' => 'applications/owners/capability/PhabricatorOwnersDefaultViewCapability.php',
'PhabricatorOwnersDetailController' => 'applications/owners/controller/PhabricatorOwnersDetailController.php', 'PhabricatorOwnersDetailController' => 'applications/owners/controller/PhabricatorOwnersDetailController.php',
'PhabricatorOwnersEditController' => 'applications/owners/controller/PhabricatorOwnersEditController.php', 'PhabricatorOwnersEditController' => 'applications/owners/controller/PhabricatorOwnersEditController.php',
'PhabricatorOwnersListController' => 'applications/owners/controller/PhabricatorOwnersListController.php', 'PhabricatorOwnersListController' => 'applications/owners/controller/PhabricatorOwnersListController.php',
@ -2867,9 +2869,11 @@ phutil_register_library_map(array(
'PhabricatorProjectAddHeraldAction' => 'applications/project/herald/PhabricatorProjectAddHeraldAction.php', 'PhabricatorProjectAddHeraldAction' => 'applications/project/herald/PhabricatorProjectAddHeraldAction.php',
'PhabricatorProjectApplication' => 'applications/project/application/PhabricatorProjectApplication.php', 'PhabricatorProjectApplication' => 'applications/project/application/PhabricatorProjectApplication.php',
'PhabricatorProjectArchiveController' => 'applications/project/controller/PhabricatorProjectArchiveController.php', 'PhabricatorProjectArchiveController' => 'applications/project/controller/PhabricatorProjectArchiveController.php',
'PhabricatorProjectBoardBackgroundController' => 'applications/project/controller/PhabricatorProjectBoardBackgroundController.php',
'PhabricatorProjectBoardController' => 'applications/project/controller/PhabricatorProjectBoardController.php', 'PhabricatorProjectBoardController' => 'applications/project/controller/PhabricatorProjectBoardController.php',
'PhabricatorProjectBoardDisableController' => 'applications/project/controller/PhabricatorProjectBoardDisableController.php', 'PhabricatorProjectBoardDisableController' => 'applications/project/controller/PhabricatorProjectBoardDisableController.php',
'PhabricatorProjectBoardImportController' => 'applications/project/controller/PhabricatorProjectBoardImportController.php', 'PhabricatorProjectBoardImportController' => 'applications/project/controller/PhabricatorProjectBoardImportController.php',
'PhabricatorProjectBoardManageController' => 'applications/project/controller/PhabricatorProjectBoardManageController.php',
'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php', 'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php',
'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php', 'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php',
'PhabricatorProjectCardView' => 'applications/project/view/PhabricatorProjectCardView.php', 'PhabricatorProjectCardView' => 'applications/project/view/PhabricatorProjectCardView.php',
@ -2967,6 +2971,7 @@ phutil_register_library_map(array(
'PhabricatorProjectViewController' => 'applications/project/controller/PhabricatorProjectViewController.php', 'PhabricatorProjectViewController' => 'applications/project/controller/PhabricatorProjectViewController.php',
'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php', 'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php',
'PhabricatorProjectWatcherListView' => 'applications/project/view/PhabricatorProjectWatcherListView.php', 'PhabricatorProjectWatcherListView' => 'applications/project/view/PhabricatorProjectWatcherListView.php',
'PhabricatorProjectWorkboardBackgroundColor' => 'applications/project/constants/PhabricatorProjectWorkboardBackgroundColor.php',
'PhabricatorProjectWorkboardProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectWorkboardProfilePanel.php', 'PhabricatorProjectWorkboardProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectWorkboardProfilePanel.php',
'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php', 'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php',
'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php', 'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php',
@ -5706,7 +5711,7 @@ phutil_register_library_map(array(
'PHUIListView' => 'AphrontTagView', 'PHUIListView' => 'AphrontTagView',
'PHUIListViewTestCase' => 'PhabricatorTestCase', 'PHUIListViewTestCase' => 'PhabricatorTestCase',
'PHUIMainMenuView' => 'AphrontView', 'PHUIMainMenuView' => 'AphrontView',
'PHUIObjectBoxView' => 'AphrontView', 'PHUIObjectBoxView' => 'AphrontTagView',
'PHUIObjectItemListExample' => 'PhabricatorUIExample', 'PHUIObjectItemListExample' => 'PhabricatorUIExample',
'PHUIObjectItemListView' => 'AphrontTagView', 'PHUIObjectItemListView' => 'AphrontTagView',
'PHUIObjectItemView' => 'AphrontTagView', 'PHUIObjectItemView' => 'AphrontTagView',
@ -7036,6 +7041,8 @@ phutil_register_library_map(array(
'PhabricatorOwnersCustomFieldStorage' => 'PhabricatorCustomFieldStorage', 'PhabricatorOwnersCustomFieldStorage' => 'PhabricatorCustomFieldStorage',
'PhabricatorOwnersCustomFieldStringIndex' => 'PhabricatorCustomFieldStringIndexStorage', 'PhabricatorOwnersCustomFieldStringIndex' => 'PhabricatorCustomFieldStringIndexStorage',
'PhabricatorOwnersDAO' => 'PhabricatorLiskDAO', 'PhabricatorOwnersDAO' => 'PhabricatorLiskDAO',
'PhabricatorOwnersDefaultEditCapability' => 'PhabricatorPolicyCapability',
'PhabricatorOwnersDefaultViewCapability' => 'PhabricatorPolicyCapability',
'PhabricatorOwnersDetailController' => 'PhabricatorOwnersController', 'PhabricatorOwnersDetailController' => 'PhabricatorOwnersController',
'PhabricatorOwnersEditController' => 'PhabricatorOwnersController', 'PhabricatorOwnersEditController' => 'PhabricatorOwnersController',
'PhabricatorOwnersListController' => 'PhabricatorOwnersController', 'PhabricatorOwnersListController' => 'PhabricatorOwnersController',
@ -7293,9 +7300,11 @@ phutil_register_library_map(array(
'PhabricatorProjectAddHeraldAction' => 'PhabricatorProjectHeraldAction', 'PhabricatorProjectAddHeraldAction' => 'PhabricatorProjectHeraldAction',
'PhabricatorProjectApplication' => 'PhabricatorApplication', 'PhabricatorProjectApplication' => 'PhabricatorApplication',
'PhabricatorProjectArchiveController' => 'PhabricatorProjectController', 'PhabricatorProjectArchiveController' => 'PhabricatorProjectController',
'PhabricatorProjectBoardBackgroundController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectBoardController' => 'PhabricatorProjectController', 'PhabricatorProjectBoardController' => 'PhabricatorProjectController',
'PhabricatorProjectBoardDisableController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardDisableController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectBoardImportController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardImportController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectBoardManageController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectCardView' => 'AphrontTagView', 'PhabricatorProjectCardView' => 'AphrontTagView',
@ -7305,6 +7314,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionInterface', 'PhabricatorApplicationTransactionInterface',
'PhabricatorPolicyInterface', 'PhabricatorPolicyInterface',
'PhabricatorDestructibleInterface', 'PhabricatorDestructibleInterface',
'PhabricatorExtendedPolicyInterface',
), ),
'PhabricatorProjectColumnDetailController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectColumnDetailController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectColumnEditController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectColumnEditController' => 'PhabricatorProjectBoardController',
@ -7406,6 +7416,7 @@ phutil_register_library_map(array(
'PhabricatorProjectViewController' => 'PhabricatorProjectController', 'PhabricatorProjectViewController' => 'PhabricatorProjectController',
'PhabricatorProjectWatchController' => 'PhabricatorProjectController', 'PhabricatorProjectWatchController' => 'PhabricatorProjectController',
'PhabricatorProjectWatcherListView' => 'PhabricatorProjectUserListView', 'PhabricatorProjectWatcherListView' => 'PhabricatorProjectUserListView',
'PhabricatorProjectWorkboardBackgroundColor' => 'Phobject',
'PhabricatorProjectWorkboardProfilePanel' => 'PhabricatorProfilePanel', 'PhabricatorProjectWorkboardProfilePanel' => 'PhabricatorProfilePanel',
'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension', 'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension',
'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField', 'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField',

View file

@ -363,11 +363,7 @@ final class PhabricatorCalendarEventViewController
->getIconLabel($event->getIcon())); ->getIconLabel($event->getIcon()));
if (strlen($event->getDescription())) { if (strlen($event->getDescription())) {
$description = PhabricatorMarkupEngine::renderOneObject( $description = new PHUIRemarkupView($viewer, $event->getDescription());
id(new PhabricatorMarkupOneOff())->setContent($event->getDescription()),
'default',
$viewer);
$properties->addSectionHeader( $properties->addSectionHeader(
pht('Description'), pht('Description'),
PHUIPropertyListView::ICON_SUMMARY); PHUIPropertyListView::ICON_SUMMARY);

View file

@ -72,6 +72,12 @@ final class CelerityDefaultPostprocessor
'hoverselectedblue' => '#e6e9ee', 'hoverselectedblue' => '#e6e9ee',
'borderinset' => 'inset 0 0 0 1px rgba(55,55,55,.15)', 'borderinset' => 'inset 0 0 0 1px rgba(55,55,55,.15)',
// Alphas
'alphawhite' => '255,255,255',
'alphagrey' => '55,55,55',
'alphablue' => '71,87,120',
'alphablack' => '0,0,0',
// Base Greys // Base Greys
'lightgreyborder' => '#C7CCD9', 'lightgreyborder' => '#C7CCD9',
'greyborder' => '#A1A6B0', 'greyborder' => '#A1A6B0',

View file

@ -37,7 +37,7 @@ final class PhabricatorConduitLogSearchEngine
return array( return array(
id(new PhabricatorUsersSearchField()) id(new PhabricatorUsersSearchField())
->setKey('callerPHIDs') ->setKey('callerPHIDs')
->setLabel(pht('Methods')) ->setLabel(pht('Callers'))
->setAliases(array('caller', 'callers')) ->setAliases(array('caller', 'callers'))
->setDescription(pht('Find calls by specific users.')), ->setDescription(pht('Find calls by specific users.')),
id(new PhabricatorSearchStringListField()) id(new PhabricatorSearchStringListField())

View file

@ -341,23 +341,14 @@ final class PhabricatorConfigWelcomeController
$header = id(new PHUIHeaderView()) $header = id(new PHUIHeaderView())
->setHeader(pht('Welcome to Phabricator')); ->setHeader(pht('Welcome to Phabricator'));
$setup_header = PhabricatorMarkupEngine::renderOneObject( $setup_header = new PHUIRemarkupView(
id(new PhabricatorMarkupOneOff()) $viewer, pht('=Setup and Configuration'));
->setContent(pht('=Setup and Configuration')),
'default',
$viewer);
$explore_header = PhabricatorMarkupEngine::renderOneObject( $explore_header = new PHUIRemarkupView(
id(new PhabricatorMarkupOneOff()) $viewer, pht('=Explore Phabricator'));
->setContent(pht('=Explore Phabricator')),
'default',
$viewer);
$quick_header = PhabricatorMarkupEngine::renderOneObject( $quick_header = new PHUIRemarkupView(
id(new PhabricatorMarkupOneOff()) $viewer, pht('=Quick Start Guide'));
->setContent(pht('=Quick Start Guides')),
'default',
$viewer);
return id(new PHUIDocumentView()) return id(new PHUIDocumentView())
->setHeader($header) ->setHeader($header)
@ -376,10 +367,7 @@ final class PhabricatorConfigWelcomeController
$icon = id(new PHUIIconView()) $icon = id(new PHUIIconView())
->setIcon($icon.' fa-2x'); ->setIcon($icon.' fa-2x');
$content = PhabricatorMarkupEngine::renderOneObject( $content = new PHUIRemarkupView($viewer, $content);
id(new PhabricatorMarkupOneOff())->setContent($content),
'default',
$viewer);
$icon = phutil_tag( $icon = phutil_tag(
'div', 'div',

View file

@ -132,11 +132,7 @@ final class PhabricatorCountdownViewController
$description = $countdown->getDescription(); $description = $countdown->getDescription();
if (strlen($description)) { if (strlen($description)) {
$description = PhabricatorMarkupEngine::renderOneObject( $description = new PHUIRemarkupView($viewer, $description);
id(new PhabricatorMarkupOneOff())->setContent($description),
'default',
$viewer);
$view->addSectionHeader( $view->addSectionHeader(
pht('Description'), pht('Description'),
PHUIPropertyListView::ICON_SUMMARY); PHUIPropertyListView::ICON_SUMMARY);

View file

@ -44,12 +44,7 @@ final class DifferentialRevertPlanField
return null; return null;
} }
return PhabricatorMarkupEngine::renderOneObject( return new PHUIRemarkupView($this->getViewer(), $this->getValue());
id(new PhabricatorMarkupOneOff())
->setPreserveLinebreaks(true)
->setContent($this->getValue()),
'default',
$this->getViewer());
} }
public function shouldAppearInGlobalSearch() { public function shouldAppearInGlobalSearch() {

View file

@ -122,12 +122,7 @@ final class DifferentialSummaryField
return null; return null;
} }
return PhabricatorMarkupEngine::renderOneObject( return new PHUIRemarkupView($this->getViewer(), $this->getValue());
id(new PhabricatorMarkupOneOff())
->setPreserveLinebreaks(true)
->setContent($this->getValue()),
'default',
$this->getViewer());
} }
public function getApplicationTransactionRemarkupBlocks( public function getApplicationTransactionRemarkupBlocks(

View file

@ -136,12 +136,7 @@ final class DifferentialTestPlanField
return null; return null;
} }
return PhabricatorMarkupEngine::renderOneObject( return new PHUIRemarkupView($this->getViewer(), $this->getValue());
id(new PhabricatorMarkupOneOff())
->setPreserveLinebreaks(true)
->setContent($this->getValue()),
'default',
$this->getViewer());
} }
public function getApplicationTransactionRemarkupBlocks( public function getApplicationTransactionRemarkupBlocks(

View file

@ -64,7 +64,11 @@ 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]+)/' => array( '(?:'.
'(?P<repositoryCallsign>[A-Z]+)'.
'|'.
'(?P<repositoryID>[1-9]\d*)'.
')/' => array(
'' => 'DiffusionRepositoryController', '' => 'DiffusionRepositoryController',
'repository/(?P<dblob>.*)' => 'DiffusionRepositoryController', 'repository/(?P<dblob>.*)' => 'DiffusionRepositoryController',
@ -115,8 +119,9 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
// catch-all for serving repositories over HTTP. We must accept // catch-all for serving repositories over HTTP. We must accept
// requests without the trailing "/" because SVN commands don't // requests without the trailing "/" because SVN commands don't
// necessarily include it. // necessarily include it.
'(?P<repositoryCallsign>[A-Z]+)(?:/.*)?' => '(?:(?P<repositoryCallsign>[A-Z]+)|(?P<repositoryID>[1-9]\d*))'.
'DiffusionRepositoryDefaultController', '(?:/.*)?'
=> 'DiffusionRepositoryDefaultController',
'inline/' => array( 'inline/' => array(
'edit/(?P<phid>[^/]+)/' => 'DiffusionInlineCommentController', 'edit/(?P<phid>[^/]+)/' => 'DiffusionInlineCommentController',

View file

@ -246,7 +246,16 @@ final class DiffusionBrowseQueryConduitAPIMethod
DiffusionBrowseResultSet::REASON_IS_FILE); DiffusionBrowseResultSet::REASON_IS_FILE);
return $result; return $result;
} }
$parts = explode('/', $remainder); $parts = explode('/', $remainder);
$name = reset($parts);
// If we've already seen this path component, we're looking at a file
// inside a directory we already processed. Just move on.
if (isset($results[$name])) {
continue;
}
if (count($parts) == 1) { if (count($parts) == 1) {
$type = DifferentialChangeType::FILE_NORMAL; $type = DifferentialChangeType::FILE_NORMAL;
} else { } else {
@ -254,7 +263,7 @@ final class DiffusionBrowseQueryConduitAPIMethod
} }
if ($count >= $offset) { if ($count >= $offset) {
$results[reset($parts)] = $type; $results[$name] = $type;
} }
$count++; $count++;

View file

@ -19,13 +19,19 @@ final class DiffusionBranchTableController extends DiffusionController {
$pager = id(new PHUIPagerView()) $pager = id(new PHUIPagerView())
->readFromRequest($request); ->readFromRequest($request);
// TODO: Add support for branches that contain commit $params = array(
$branches = $this->callConduitWithDiffusionRequest(
'diffusion.branchquery',
array(
'offset' => $pager->getOffset(), 'offset' => $pager->getOffset(),
'limit' => $pager->getPageSize() + 1, 'limit' => $pager->getPageSize() + 1,
)); );
$contains = $drequest->getSymbolicCommit();
if (strlen($contains)) {
$params['contains'] = $contains;
}
$branches = $this->callConduitWithDiffusionRequest(
'diffusion.branchquery',
$params);
$branches = $pager->sliceResults($branches); $branches = $pager->sliceResults($branches);
$branches = DiffusionRepositoryRef::loadAllFromDictionaries($branches); $branches = DiffusionRepositoryRef::loadAllFromDictionaries($branches);

View file

@ -15,20 +15,18 @@ final class DiffusionCommitBranchesController extends DiffusionController {
$drequest = $this->getDiffusionRequest(); $drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository(); $repository = $drequest->getRepository();
switch ($repository->getVersionControlSystem()) { $branch_limit = 10;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $branches = DiffusionRepositoryRef::loadAllFromDictionaries(
$branches = array(); $this->callConduitWithDiffusionRequest(
break;
default:
$branches = $this->callConduitWithDiffusionRequest(
'diffusion.branchquery', 'diffusion.branchquery',
array( array(
'contains' => $drequest->getCommit(), 'contains' => $drequest->getCommit(),
)); 'limit' => $branch_limit + 1,
break; )));
}
$has_more_branches = (count($branches) > $branch_limit);
$branches = array_slice($branches, 0, $branch_limit);
$branches = DiffusionRepositoryRef::loadAllFromDictionaries($branches);
$branch_links = array(); $branch_links = array();
foreach ($branches as $branch) { foreach ($branches as $branch) {
$branch_links[] = phutil_tag( $branch_links[] = phutil_tag(
@ -43,6 +41,18 @@ final class DiffusionCommitBranchesController extends DiffusionController {
$branch->getShortName()); $branch->getShortName());
} }
if ($has_more_branches) {
$branch_links[] = phutil_tag(
'a',
array(
'href' => $drequest->generateURI(
array(
'action' => 'branches',
)),
),
pht("More Branches\xE2\x80\xA6"));
}
return id(new AphrontAjaxResponse()) return id(new AphrontAjaxResponse())
->setContent($branch_links ? implode(', ', $branch_links) : pht('None')); ->setContent($branch_links ? implode(', ', $branch_links) : pht('None'));
} }

View file

@ -16,11 +16,6 @@ final class DiffusionCommitTagsController extends DiffusionController {
$repository = $drequest->getRepository(); $repository = $drequest->getRepository();
$tag_limit = 10; $tag_limit = 10;
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$tags = array();
break;
default:
$tags = DiffusionRepositoryTag::newFromConduit( $tags = DiffusionRepositoryTag::newFromConduit(
$this->callConduitWithDiffusionRequest( $this->callConduitWithDiffusionRequest(
'diffusion.tagsquery', 'diffusion.tagsquery',
@ -28,8 +23,7 @@ final class DiffusionCommitTagsController extends DiffusionController {
'commit' => $drequest->getCommit(), 'commit' => $drequest->getCommit(),
'limit' => $tag_limit + 1, 'limit' => $tag_limit + 1,
))); )));
break;
}
$has_more_tags = (count($tags) > $tag_limit); $has_more_tags = (count($tags) > $tag_limit);
$tags = array_slice($tags, 0, $tag_limit); $tags = array_slice($tags, 0, $tag_limit);

View file

@ -64,6 +64,20 @@ abstract class DiffusionController extends PhabricatorController {
return new Aphront404Response(); return new Aphront404Response();
} }
// If the client is making a request like "/diffusion/1/...", but the
// repository has a different canonical path like "/diffusion/XYZ/...",
// redirect them to the canonical path.
$request_path = $request->getPath();
$repository = $drequest->getRepository();
$canonical_path = $repository->getCanonicalPath($request_path);
if ($canonical_path !== null) {
if ($canonical_path != $request_path) {
return id(new AphrontRedirectResponse())->setURI($canonical_path);
}
}
$this->diffusionRequest = $drequest; $this->diffusionRequest = $drequest;
return null; return null;

View file

@ -290,10 +290,7 @@ final class DiffusionRepositoryController extends DiffusionController {
$description = $repository->getDetail('description'); $description = $repository->getDetail('description');
if (strlen($description)) { if (strlen($description)) {
$description = PhabricatorMarkupEngine::renderOneObject( $description = new PHUIRemarkupView($user, $description);
$repository,
'description',
$user);
$view->addSectionHeader( $view->addSectionHeader(
pht('Description'), PHUIPropertyListView::ICON_SUMMARY); pht('Description'), PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent($description); $view->addTextContent($description);

View file

@ -134,7 +134,6 @@ final class DiffusionRepositoryCreateController
$type_name = PhabricatorRepositoryTransaction::TYPE_NAME; $type_name = PhabricatorRepositoryTransaction::TYPE_NAME;
$type_vcs = PhabricatorRepositoryTransaction::TYPE_VCS; $type_vcs = PhabricatorRepositoryTransaction::TYPE_VCS;
$type_activate = PhabricatorRepositoryTransaction::TYPE_ACTIVATE; $type_activate = PhabricatorRepositoryTransaction::TYPE_ACTIVATE;
$type_local_path = PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH;
$type_remote_uri = PhabricatorRepositoryTransaction::TYPE_REMOTE_URI; $type_remote_uri = PhabricatorRepositoryTransaction::TYPE_REMOTE_URI;
$type_hosting = PhabricatorRepositoryTransaction::TYPE_HOSTING; $type_hosting = PhabricatorRepositoryTransaction::TYPE_HOSTING;
$type_http = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP; $type_http = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP;
@ -150,14 +149,6 @@ final class DiffusionRepositoryCreateController
// If we're creating a new repository, set all this core stuff. // If we're creating a new repository, set all this core stuff.
if ($is_create) { if ($is_create) {
$callsign = $form->getPage('name')
->getControl('callsign')->getValue();
// We must set this to a unique value to save the repository
// initially, and it's immutable, so we don't bother using
// transactions to apply this change.
$repository->setCallsign($callsign);
$xactions[] = id(clone $template) $xactions[] = id(clone $template)
->setTransactionType($type_name) ->setTransactionType($type_name)
->setNewValue( ->setNewValue(
@ -179,16 +170,6 @@ final class DiffusionRepositoryCreateController
->setTransactionType($type_service) ->setTransactionType($type_service)
->setNewValue($service->getPHID()); ->setNewValue($service->getPHID());
} }
$default_local_path = PhabricatorEnv::getEnvConfig(
'repository.default-local-path');
$default_local_path = rtrim($default_local_path, '/');
$default_local_path = $default_local_path.'/'.$callsign.'/';
$xactions[] = id(clone $template)
->setTransactionType($type_local_path)
->setNewValue($default_local_path);
} }
if ($is_init) { if ($is_init) {
@ -354,7 +335,7 @@ final class DiffusionRepositoryCreateController
} }
/* -( Page: Name and Callsign )-------------------------------------------- */ /* -( Page: Name )--------------------------------------------------------- */
private function buildNamePage() { private function buildNamePage() {
@ -370,23 +351,7 @@ final class DiffusionRepositoryCreateController
->addControl( ->addControl(
id(new AphrontFormTextControl()) id(new AphrontFormTextControl())
->setName('name') ->setName('name')
->setLabel(pht('Name')) ->setLabel(pht('Name')));
->setCaption(pht('Human-readable repository name.')))
->addRemarkupInstructions(
pht(
'**Choose a "Callsign" for the repository.** This is a short, '.
'unique string which identifies commits elsewhere in Phabricator. '.
'For example, you might use `M` for your mobile app repository '.
'and `B` for your backend repository.'.
"\n\n".
'**Callsigns must be UPPERCASE**, and can not be edited after the '.
'repository is created. Generally, you should choose short '.
'callsigns.'))
->addControl(
id(new AphrontFormTextControl())
->setName('callsign')
->setLabel(pht('Callsign'))
->setCaption(pht('Short UPPERCASE identifier.')));
} }
public function validateNamePage(PHUIFormPageView $page) { public function validateNamePage(PHUIFormPageView $page) {
@ -398,38 +363,7 @@ final class DiffusionRepositoryCreateController
pht('You must choose a name for this repository.')); pht('You must choose a name for this repository.'));
} }
$c_call = $page->getControl('callsign'); return $c_name->isValid();
$v_call = $c_call->getValue();
if (!strlen($v_call)) {
$c_call->setError(pht('Required'));
$page->addPageError(
pht('You must choose a callsign for this repository.'));
} else if (!preg_match('/^[A-Z]+\z/', $v_call)) {
$c_call->setError(pht('Invalid'));
$page->addPageError(
pht('The callsign must contain only UPPERCASE letters.'));
} else {
$exists = false;
try {
$repo = id(new PhabricatorRepositoryQuery())
->setViewer($this->getRequest()->getUser())
->withCallsigns(array($v_call))
->executeOne();
$exists = (bool)$repo;
} catch (PhabricatorPolicyException $ex) {
$exists = true;
}
if ($exists) {
$c_call->setError(pht('Not Unique'));
$page->addPageError(
pht(
'Another repository already uses that callsign. You must choose '.
'a unique callsign.'));
}
}
return $c_name->isValid() &&
$c_call->isValid();
} }

View file

@ -18,11 +18,13 @@ final class DiffusionRepositoryEditBasicController
$v_name = $repository->getName(); $v_name = $repository->getName();
$v_desc = $repository->getDetail('description'); $v_desc = $repository->getDetail('description');
$v_slug = $repository->getRepositorySlug(); $v_slug = $repository->getRepositorySlug();
$v_callsign = $repository->getCallsign();
$v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs( $v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
$repository->getPHID(), $repository->getPHID(),
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
$e_name = true; $e_name = true;
$e_slug = null; $e_slug = null;
$e_callsign = null;
$errors = array(); $errors = array();
$validation_exception = null; $validation_exception = null;
@ -31,6 +33,7 @@ final class DiffusionRepositoryEditBasicController
$v_desc = $request->getStr('description'); $v_desc = $request->getStr('description');
$v_projects = $request->getArr('projectPHIDs'); $v_projects = $request->getArr('projectPHIDs');
$v_slug = $request->getStr('slug'); $v_slug = $request->getStr('slug');
$v_callsign = $request->getStr('callsign');
if (!strlen($v_name)) { if (!strlen($v_name)) {
$e_name = pht('Required'); $e_name = pht('Required');
@ -47,6 +50,7 @@ final class DiffusionRepositoryEditBasicController
$type_desc = PhabricatorRepositoryTransaction::TYPE_DESCRIPTION; $type_desc = PhabricatorRepositoryTransaction::TYPE_DESCRIPTION;
$type_edge = PhabricatorTransactions::TYPE_EDGE; $type_edge = PhabricatorTransactions::TYPE_EDGE;
$type_slug = PhabricatorRepositoryTransaction::TYPE_SLUG; $type_slug = PhabricatorRepositoryTransaction::TYPE_SLUG;
$type_callsign = PhabricatorRepositoryTransaction::TYPE_CALLSIGN;
$xactions[] = id(clone $template) $xactions[] = id(clone $template)
->setTransactionType($type_name) ->setTransactionType($type_name)
@ -60,6 +64,10 @@ final class DiffusionRepositoryEditBasicController
->setTransactionType($type_slug) ->setTransactionType($type_slug)
->setNewValue($v_slug); ->setNewValue($v_slug);
$xactions[] = id(clone $template)
->setTransactionType($type_callsign)
->setNewValue($v_callsign);
$xactions[] = id(clone $template) $xactions[] = id(clone $template)
->setTransactionType($type_edge) ->setTransactionType($type_edge)
->setMetadataValue( ->setMetadataValue(
@ -78,11 +86,16 @@ final class DiffusionRepositoryEditBasicController
try { try {
$editor->applyTransactions($repository, $xactions); $editor->applyTransactions($repository, $xactions);
// The preferred edit URI may have changed if the callsign or slug
// were adjusted, so grab a fresh copy.
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
return id(new AphrontRedirectResponse())->setURI($edit_uri); return id(new AphrontRedirectResponse())->setURI($edit_uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) { } catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex; $validation_exception = $ex;
$e_slug = $ex->getShortMessage($type_slug); $e_slug = $ex->getShortMessage($type_slug);
$e_callsign = $ex->getShortMessage($type_callsign);
} }
} }
} }
@ -106,6 +119,12 @@ final class DiffusionRepositoryEditBasicController
->setLabel(pht('Short Name')) ->setLabel(pht('Short Name'))
->setValue($v_slug) ->setValue($v_slug)
->setError($e_slug)) ->setError($e_slug))
->appendChild(
id(new AphrontFormTextControl())
->setName('callsign')
->setLabel(pht('Callsign'))
->setValue($v_callsign)
->setError($e_callsign))
->appendChild( ->appendChild(
id(new PhabricatorRemarkupControl()) id(new PhabricatorRemarkupControl())
->setUser($viewer) ->setUser($viewer)

View file

@ -284,7 +284,12 @@ final class DiffusionRepositoryEditMainController
$repository->getVersionControlSystem()); $repository->getVersionControlSystem());
$view->addProperty(pht('Type'), $type); $view->addProperty(pht('Type'), $type);
$view->addProperty(pht('Callsign'), $repository->getCallsign());
$callsign = $repository->getCallsign();
if (!strlen($callsign)) {
$callsign = phutil_tag('em', array(), pht('No Callsign'));
}
$view->addProperty(pht('Callsign'), $callsign);
$short_name = $repository->getRepositorySlug(); $short_name = $repository->getRepositorySlug();
if ($short_name === null) { if ($short_name === null) {
@ -309,10 +314,7 @@ final class DiffusionRepositoryEditMainController
if (!strlen($description)) { if (!strlen($description)) {
$description = phutil_tag('em', array(), pht('No description provided.')); $description = phutil_tag('em', array(), pht('No description provided.'));
} else { } else {
$description = PhabricatorMarkupEngine::renderOneObject( $description = new PHUIRemarkupView($viewer, $description);
$repository,
'description',
$viewer);
} }
$view->addTextContent($description); $view->addTextContent($description);

View file

@ -262,17 +262,23 @@ final class DiffusionServeController extends DiffusionController {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$result = new PhabricatorVCSResponse( $result = new PhabricatorVCSResponse(
500, 500,
pht('This is not a Git repository.')); pht(
'This repository ("%s") is not a Git repository.',
$repository->getDisplayName()));
break; break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$result = new PhabricatorVCSResponse( $result = new PhabricatorVCSResponse(
500, 500,
pht('This is not a Mercurial repository.')); pht(
'This repository ("%s") is not a Mercurial repository.',
$repository->getDisplayName()));
break; break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$result = new PhabricatorVCSResponse( $result = new PhabricatorVCSResponse(
500, 500,
pht('This is not a Subversion repository.')); pht(
'This repository ("%s") is not a Subversion repository.',
$repository->getDisplayName()));
break; break;
default: default:
$result = new PhabricatorVCSResponse( $result = new PhabricatorVCSResponse(
@ -480,7 +486,9 @@ final class DiffusionServeController extends DiffusionController {
private function getRequestDirectoryPath(PhabricatorRepository $repository) { private function getRequestDirectoryPath(PhabricatorRepository $repository) {
$request = $this->getRequest(); $request = $this->getRequest();
$request_path = $request->getRequestURI()->getPath(); $request_path = $request->getRequestURI()->getPath();
$base_path = preg_replace('@^/diffusion/[A-Z]+@', '', $request_path);
$info = PhabricatorRepository::parseRepositoryServicePath($request_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
// isn't the name of a known Git resource. This allows users to clone // isn't the name of a known Git resource. This allows users to clone

View file

@ -10,6 +10,7 @@
*/ */
final class DiffusionCommitHookEngine extends Phobject { final class DiffusionCommitHookEngine extends Phobject {
const ENV_REPOSITORY = 'PHABRICATOR_REPOSITORY';
const ENV_USER = 'PHABRICATOR_USER'; const ENV_USER = 'PHABRICATOR_USER';
const ENV_REMOTE_ADDRESS = 'PHABRICATOR_REMOTE_ADDRESS'; const ENV_REMOTE_ADDRESS = 'PHABRICATOR_REMOTE_ADDRESS';
const ENV_REMOTE_PROTOCOL = 'PHABRICATOR_REMOTE_PROTOCOL'; const ENV_REMOTE_PROTOCOL = 'PHABRICATOR_REMOTE_PROTOCOL';
@ -610,7 +611,7 @@ final class DiffusionCommitHookEngine extends Phobject {
$console = PhutilConsole::getConsole(); $console = PhutilConsole::getConsole();
$env = array( $env = array(
'PHABRICATOR_REPOSITORY' => $this->getRepository()->getCallsign(), self::ENV_REPOSITORY => $this->getRepository()->getPHID(),
self::ENV_USER => $this->getViewer()->getUsername(), self::ENV_USER => $this->getViewer()->getUsername(),
self::ENV_REMOTE_PROTOCOL => $this->getRemoteProtocol(), self::ENV_REMOTE_PROTOCOL => $this->getRemoteProtocol(),
self::ENV_REMOTE_ADDRESS => $this->getRemoteAddress(), self::ENV_REMOTE_ADDRESS => $this->getRemoteAddress(),

View file

@ -243,10 +243,6 @@ abstract class DiffusionRequest extends Phobject {
return $this->repository; return $this->repository;
} }
public function getCallsign() {
return $this->getRepository()->getCallsign();
}
public function setPath($path) { public function setPath($path) {
$this->path = $path; $this->path = $path;
return $this; return $this;

View file

@ -6,6 +6,7 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
private $repository; private $repository;
private $hasWriteAccess; private $hasWriteAccess;
private $proxyURI; private $proxyURI;
private $baseRequestPath;
public function getRepository() { public function getRepository() {
if (!$this->repository) { if (!$this->repository) {
@ -45,6 +46,10 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
abstract protected function identifyRepository(); abstract protected function identifyRepository();
abstract protected function executeRepositoryOperations(); abstract protected function executeRepositoryOperations();
protected function getBaseRequestPath() {
return $this->baseRequestPath;
}
protected function writeError($message) { protected function writeError($message) {
$this->getErrorChannel()->write($message); $this->getErrorChannel()->write($message);
return $this; return $this;
@ -149,25 +154,29 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
protected function loadRepositoryWithPath($path) { protected function loadRepositoryWithPath($path) {
$viewer = $this->getUser(); $viewer = $this->getUser();
$regex = '@^/?diffusion/(?P<callsign>[A-Z]+)(?:/|\z)@'; $info = PhabricatorRepository::parseRepositoryServicePath($path);
$matches = null; if ($info === null) {
if (!preg_match($regex, $path, $matches)) {
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".',
$path, $path,
'/diffusion/X/')); '/diffusion/X/',
'/diffusion/123/'));
} }
$callsign = $matches[1]; $identifier = $info['identifier'];
$base = $info['base'];
$this->baseRequestPath = $base;
$repository = id(new PhabricatorRepositoryQuery()) $repository = id(new PhabricatorRepositoryQuery())
->setViewer($viewer) ->setViewer($viewer)
->withCallsigns(array($callsign)) ->withIdentifiers(array($identifier))
->executeOne(); ->executeOne();
if (!$repository) { if (!$repository) {
throw new Exception( throw new Exception(
pht('No repository "%s" exists!', $callsign)); pht('No repository "%s" exists!', $identifier));
} }
switch ($repository->getServeOverSSH()) { switch ($repository->getServeOverSSH()) {
@ -179,7 +188,9 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
case PhabricatorRepository::SERVE_OFF: case PhabricatorRepository::SERVE_OFF:
default: default:
throw new Exception( throw new Exception(
pht('This repository is not available over SSH.')); pht(
'This repository ("%s") is not available over SSH.',
$repository->getDisplayName()));
} }
return $repository; return $repository;

View file

@ -377,14 +377,12 @@ final class DiffusionSubversionServeSSHWorkflow
$repository = $this->getRepository(); $repository = $this->getRepository();
$path = $this->getPathFromSubversionURI($uri_string); $path = $this->getPathFromSubversionURI($uri_string);
$path = preg_replace( $external_base = $this->getBaseRequestPath();
'(^/diffusion/[A-Z]+)',
rtrim($repository->getLocalPath(), '/'),
$path);
if (preg_match('(^/diffusion/[A-Z]+/\z)', $path)) { // Replace "/diffusion/X" in the request with the repository local path,
$path = rtrim($path, '/'); // so "/diffusion/X/master/" becomes "/path/to/repository/X/master/".
} $local_path = rtrim($repository->getLocalPath(), '/');
$path = $local_path.substr($path, strlen($external_base));
// NOTE: We are intentionally NOT removing username information from the // NOTE: We are intentionally NOT removing username information from the
// URI. Subversion retains it over the course of the request and considers // URI. Subversion retains it over the course of the request and considers
@ -398,7 +396,7 @@ final class DiffusionSubversionServeSSHWorkflow
if ($this->externalBaseURI === null) { if ($this->externalBaseURI === null) {
$pre = (string)id(clone $uri)->setPath(''); $pre = (string)id(clone $uri)->setPath('');
$external_path = '/diffusion/'.$repository->getCallsign(); $external_path = $external_base;
$external_path = $this->normalizeSVNPath($external_path); $external_path = $this->normalizeSVNPath($external_path);
$this->externalBaseURI = $pre.$external_path; $this->externalBaseURI = $pre.$external_path;

View file

@ -109,11 +109,14 @@ final class DiffusionReadmeView extends DiffusionView {
$header = id(new PHUIHeaderView()) $header = id(new PHUIHeaderView())
->setHeader($readme_name); ->setHeader($readme_name);
return id(new PHUIDocumentView()) $document = id(new PHUIDocumentViewPro())
->setFluid(true) ->setFluid(true)
->appendChild($readme_content) ->appendChild($readme_content);
->addClass('diffusion-readme-view')
->setHeader($header); return id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($document)
->addClass('diffusion-readme-view');
} }
} }

View file

@ -29,20 +29,12 @@ final class DivinerBookController extends DivinerController {
$book->getShortTitle(), $book->getShortTitle(),
'/book/'.$book->getName().'/'); '/book/'.$book->getName().'/');
$action_button = id(new PHUIButtonView())
->setTag('a')
->setText(pht('Actions'))
->setHref('#')
->setIcon('fa-bars')
->addClass('phui-mobile-menu')
->setDropdownMenu($actions);
$header = id(new PHUIHeaderView()) $header = id(new PHUIHeaderView())
->setHeader($book->getTitle()) ->setHeader($book->getTitle())
->setUser($viewer) ->setUser($viewer)
->setPolicyObject($book) ->setPolicyObject($book)
->setEpoch($book->getDateModified()) ->setEpoch($book->getDateModified())
->addActionLink($action_button); ->setActionList($actions);
// TODO: This could probably look better. // TODO: This could probably look better.
if ($book->getRepositoryPHID()) { if ($book->getRepositoryPHID()) {
@ -94,11 +86,7 @@ final class DivinerBookController extends DivinerController {
$preface = $book->getPreface(); $preface = $book->getPreface();
$preface_view = null; $preface_view = null;
if (strlen($preface)) { if (strlen($preface)) {
$preface_view = $preface_view = new PHUIRemarkupView($viewer, $preface);
PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($preface),
'default',
$viewer);
} }
$document->appendChild($preface_view); $document->appendChild($preface_view);

View file

@ -61,11 +61,7 @@ final class DivinerMainController extends DivinerController {
" %s\n\n", " %s\n\n",
'phabricator/ $ ./bin/diviner generate'); 'phabricator/ $ ./bin/diviner generate');
$text = PhabricatorMarkupEngine::renderOneObject( $text = new PHUIRemarkupView($viewer, $text);
id(new PhabricatorMarkupOneOff())->setContent($text),
'default',
$viewer);
$document->appendChild($text); $document->appendChild($text);
} }

View file

@ -27,7 +27,7 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow {
), ),
array( array(
'name' => 'repository', 'name' => 'repository',
'param' => 'callsign', 'param' => 'identifier',
'help' => pht('Repository that the documentation belongs to.'), 'help' => pht('Repository that the documentation belongs to.'),
), ),
)); ));
@ -192,19 +192,19 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow {
} }
$publisher = newv($publisher_class, array()); $publisher = newv($publisher_class, array());
$callsign = $args->getArg('repository'); $identifier = $args->getArg('repository');
$repository = null; $repository = null;
if ($callsign) { if (strlen($identifier)) {
$repository = id(new PhabricatorRepositoryQuery()) $repository = id(new PhabricatorRepositoryQuery())
->setViewer(PhabricatorUser::getOmnipotentUser()) ->setViewer(PhabricatorUser::getOmnipotentUser())
->withCallsigns(array($callsign)) ->withIdentifiers(array($identifier))
->executeOne(); ->executeOne();
if (!$repository) { if (!$repository) {
throw new PhutilArgumentUsageException( throw new PhutilArgumentUsageException(
pht( pht(
"Repository '%s' does not exist.", 'Repository "%s" does not exist.',
$callsign)); $identifier));
} }
$publisher->setRepositoryPHID($repository->getPHID()); $publisher->setRepositoryPHID($repository->getPHID());

View file

@ -111,10 +111,7 @@ final class PhabricatorAsanaConfigOptions
"The Asana Workspaces your linked account has access to are:\n\n%s", "The Asana Workspaces your linked account has access to are:\n\n%s",
$out); $out);
return PhabricatorMarkupEngine::renderOneObject( return new PHUIRemarkupView($viewer, $out);
id(new PhabricatorMarkupOneOff())->setContent($out),
'default',
$viewer);
} }
private function renderContextualProjectDescription( private function renderContextualProjectDescription(
@ -155,10 +152,7 @@ final class PhabricatorAsanaConfigOptions
$out = implode("\n", $out); $out = implode("\n", $out);
return PhabricatorMarkupEngine::renderOneObject( return new PHUIRemarkupView($viewer, $out);
id(new PhabricatorMarkupOneOff())->setContent($out),
'default',
$viewer);
} }
} }

View file

@ -98,11 +98,7 @@ final class FundInitiativeViewController
$description = $initiative->getDescription(); $description = $initiative->getDescription();
if (strlen($description)) { if (strlen($description)) {
$description = PhabricatorMarkupEngine::renderOneObject( $description = new PHUIRemarkupView($viewer, $description);
id(new PhabricatorMarkupOneOff())->setContent($description),
'default',
$viewer);
$view->addSectionHeader( $view->addSectionHeader(
pht('Description'), PHUIPropertyListView::ICON_SUMMARY); pht('Description'), PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent($description); $view->addTextContent($description);
@ -110,11 +106,7 @@ final class FundInitiativeViewController
$risks = $initiative->getRisks(); $risks = $initiative->getRisks();
if (strlen($risks)) { if (strlen($risks)) {
$risks = PhabricatorMarkupEngine::renderOneObject( $risks = new PHUIRemarkupView($viewer, $risks);
id(new PhabricatorMarkupOneOff())->setContent($risks),
'default',
$viewer);
$view->addSectionHeader( $view->addSectionHeader(
pht('Risks/Challenges'), 'fa-ambulance'); pht('Risks/Challenges'), 'fa-ambulance');
$view->addTextContent($risks); $view->addTextContent($risks);

View file

@ -181,16 +181,10 @@ final class HarbormasterBuildViewController
if ($step) { if ($step) {
$description = $step->getDescription(); $description = $step->getDescription();
if ($description) { if ($description) {
$rendered = PhabricatorMarkupEngine::renderOneObject( $description = new PHUIRemarkupView($viewer, $description);
id(new PhabricatorMarkupOneOff())
->setContent($description)
->setPreserveLinebreaks(true),
'default',
$viewer);
$properties->addSectionHeader( $properties->addSectionHeader(
pht('Description'), PHUIPropertyListView::ICON_SUMMARY); pht('Description'), PHUIPropertyListView::ICON_SUMMARY);
$properties->addTextContent($rendered); $properties->addTextContent($description);
} }
} else { } else {
$target_box->setFormErrors( $target_box->setFormErrors(

View file

@ -258,11 +258,7 @@ final class LegalpadDocumentSignController extends LegalpadController {
$preamble_box = null; $preamble_box = null;
if (strlen($document->getPreamble())) { if (strlen($document->getPreamble())) {
$preamble_text = PhabricatorMarkupEngine::renderOneObject( $preamble_text = new PHUIRemarkupView($viewer, $document->getPreamble());
id(new PhabricatorMarkupOneOff())->setContent(
$document->getPreamble()),
'default',
$viewer);
// NOTE: We're avoiding `setObject()` here so we don't pick up extra UI // NOTE: We're avoiding `setObject()` here so we don't pick up extra UI
// elements like "Subscribers". This information is available on the // elements like "Subscribers". This information is available on the

View file

@ -42,13 +42,6 @@ final class PhabricatorMacroApplication extends PhabricatorApplication {
); );
} }
public function getRemarkupRules() {
return array(
new PhabricatorIconRemarkupRule(),
new PhabricatorEmojiRemarkupRule(),
);
}
protected function getCustomCapabilities() { protected function getCustomCapabilities() {
return array( return array(
PhabricatorMacroManageCapability::CAPABILITY => array( PhabricatorMacroManageCapability::CAPABILITY => array(

View file

@ -289,8 +289,14 @@ final class ManiphestTransactionEditor
array($select_phids)) array($select_phids))
->execute(); ->execute();
$object_phids = mpull($board_tasks, 'getPHID'); $board_tasks = mpull($board_tasks, null, 'getPHID');
$object_phids[] = $object_phid; $board_tasks[$object_phid] = $object;
// Make sure tasks are sorted by ID, so we lay out new positions in
// a consistent way.
$board_tasks = msort($board_tasks, 'getID');
$object_phids = array_keys($board_tasks);
$engine = id(new PhabricatorBoardLayoutEngine()) $engine = id(new PhabricatorBoardLayoutEngine())
->setViewer($omnipotent_viewer) ->setViewer($omnipotent_viewer)

View file

@ -104,14 +104,11 @@ final class PhabricatorApplicationDetailViewController
} }
$overview = $application->getOverview(); $overview = $application->getOverview();
if ($overview) { if (strlen($overview)) {
$overview = new PHUIRemarkupView($viewer, $overview);
$properties->addSectionHeader( $properties->addSectionHeader(
pht('Overview'), PHUIPropertyListView::ICON_SUMMARY); pht('Overview'), PHUIPropertyListView::ICON_SUMMARY);
$properties->addTextContent( $properties->addTextContent($overview);
PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($overview),
'default',
$viewer));
} }
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions( $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(

View file

@ -117,10 +117,7 @@ final class PhabricatorApplicationEmailCommandsController
$crumbs->addTextCrumb($title); $crumbs->addTextCrumb($title);
$crumbs->setBorder(true); $crumbs->setBorder(true);
$content_box = PhabricatorMarkupEngine::renderOneObject( $content_box = new PHUIRemarkupView($viewer, $content);
id(new PhabricatorMarkupOneOff())->setContent($content),
'default',
$viewer);
$info_view = null; $info_view = null;
if (!PhabricatorEnv::getEnvConfig('metamta.reply-handler-domain')) { if (!PhabricatorEnv::getEnvConfig('metamta.reply-handler-domain')) {

View file

@ -120,11 +120,7 @@ final class NuancePhabricatorFormSourceDefinition
PHUIPropertyListView $view) { PHUIPropertyListView $view) {
$complaint = $item->getNuanceProperty('complaint'); $complaint = $item->getNuanceProperty('complaint');
$complaint = PhabricatorMarkupEngine::renderOneObject( $complaint = new PHUIRemarkupView($viewer, $complaint);
id(new PhabricatorMarkupOneOff())->setContent($complaint),
'default',
$viewer);
$view->addSectionHeader( $view->addSectionHeader(
pht('Complaint'), 'fa-exclamation-circle'); pht('Complaint'), 'fa-exclamation-circle');
$view->addTextContent($complaint); $view->addTextContent($complaint);

View file

@ -54,4 +54,19 @@ final class PhabricatorOwnersApplication extends PhabricatorApplication {
); );
} }
protected function getCustomCapabilities() {
return array(
PhabricatorOwnersDefaultViewCapability::CAPABILITY => array(
'caption' => pht('Default view policy for newly created packages.'),
'template' => PhabricatorOwnersPackagePHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
),
PhabricatorOwnersDefaultEditCapability::CAPABILITY => array(
'caption' => pht('Default edit policy for newly created packages.'),
'template' => PhabricatorOwnersPackagePHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
),
);
}
} }

View file

@ -0,0 +1,12 @@
<?php
final class PhabricatorOwnersDefaultEditCapability
extends PhabricatorPolicyCapability {
const CAPABILITY = 'owners.default.edit';
public function getCapabilityName() {
return pht('Default Edit Policy');
}
}

View file

@ -0,0 +1,16 @@
<?php
final class PhabricatorOwnersDefaultViewCapability
extends PhabricatorPolicyCapability {
const CAPABILITY = 'owners.default.view';
public function getCapabilityName() {
return pht('Default View Policy');
}
public function shouldAllowPublicPolicySetting() {
return true;
}
}

View file

@ -189,13 +189,10 @@ final class PhabricatorOwnersDetailController
$description = $package->getDescription(); $description = $package->getDescription();
if (strlen($description)) { if (strlen($description)) {
$description = new PHUIRemarkupView($viewer, $description);
$view->addSectionHeader( $view->addSectionHeader(
pht('Description'), PHUIPropertyListView::ICON_SUMMARY); pht('Description'), PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent( $view->addTextContent($description);
$output = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($description),
'default',
$viewer));
} }
$view->invokeWillRenderEvent(); $view->invokeWillRenderEvent();
@ -211,8 +208,10 @@ final class PhabricatorOwnersDetailController
private function buildPackageActionView(PhabricatorOwnersPackage $package) { private function buildPackageActionView(PhabricatorOwnersPackage $package) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();
// TODO: Implement this capability. $can_edit = PhabricatorPolicyFilter::hasCapability(
$can_edit = true; $viewer,
$package,
PhabricatorPolicyCapability::CAN_EDIT);
$id = $package->getID(); $id = $package->getID();
$edit_uri = $this->getApplicationURI("/edit/{$id}/"); $edit_uri = $this->getApplicationURI("/edit/{$id}/");

View file

@ -12,8 +12,7 @@ final class PhabricatorOwnersPathsController
->requireCapabilities( ->requireCapabilities(
array( array(
PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_VIEW,
// TODO: Support this capability. PhabricatorPolicyCapability::CAN_EDIT,
// PhabricatorPolicyCapability::CAN_EDIT,
)) ))
->needPaths(true) ->needPaths(true)
->executeOne(); ->executeOne();

View file

@ -21,6 +21,9 @@ final class PhabricatorOwnersPackageTransactionEditor
$types[] = PhabricatorOwnersPackageTransaction::TYPE_PATHS; $types[] = PhabricatorOwnersPackageTransaction::TYPE_PATHS;
$types[] = PhabricatorOwnersPackageTransaction::TYPE_STATUS; $types[] = PhabricatorOwnersPackageTransaction::TYPE_STATUS;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
return $types; return $types;
} }

View file

@ -18,6 +18,8 @@ final class PhabricatorOwnersPackage
protected $primaryOwnerPHID; protected $primaryOwnerPHID;
protected $mailKey; protected $mailKey;
protected $status; protected $status;
protected $viewPolicy;
protected $editPolicy;
private $paths = self::ATTACHABLE; private $paths = self::ATTACHABLE;
private $owners = self::ATTACHABLE; private $owners = self::ATTACHABLE;
@ -27,8 +29,20 @@ final class PhabricatorOwnersPackage
const STATUS_ARCHIVED = 'archived'; const STATUS_ARCHIVED = 'archived';
public static function initializeNewPackage(PhabricatorUser $actor) { public static function initializeNewPackage(PhabricatorUser $actor) {
$app = id(new PhabricatorApplicationQuery())
->setViewer($actor)
->withClasses(array('PhabricatorOwnersApplication'))
->executeOne();
$view_policy = $app->getPolicy(
PhabricatorOwnersDefaultViewCapability::CAPABILITY);
$edit_policy = $app->getPolicy(
PhabricatorOwnersDefaultEditCapability::CAPABILITY);
return id(new PhabricatorOwnersPackage()) return id(new PhabricatorOwnersPackage())
->setAuditingEnabled(0) ->setAuditingEnabled(0)
->setViewPolicy($view_policy)
->setEditPolicy($edit_policy)
->attachPaths(array()) ->attachPaths(array())
->setStatus(self::STATUS_ACTIVE) ->setStatus(self::STATUS_ACTIVE)
->attachOwners(array()) ->attachOwners(array())
@ -287,8 +301,12 @@ final class PhabricatorOwnersPackage
} }
public function getPolicy($capability) { public function getPolicy($capability) {
// TODO: Implement proper policies. switch ($capability) {
return PhabricatorPolicies::POLICY_USER; case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return $this->getEditPolicy();
}
} }
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {

View file

@ -56,9 +56,11 @@ final class PhabricatorPeopleProfileViewController
$projects = $this->buildProjectsView($user); $projects = $this->buildProjectsView($user);
$badges = $this->buildBadgesView($user); $badges = $this->buildBadgesView($user);
require_celerity_resource('project-view-css');
$columns = id(new PHUITwoColumnView()) $home = id(new PHUITwoColumnView())
->addClass('project-view-badges') ->setHeader($header)
->addClass('project-view-home')
->setMainColumn( ->setMainColumn(
array( array(
$properties, $properties,
@ -76,17 +78,6 @@ final class PhabricatorPeopleProfileViewController
$crumbs = $this->buildApplicationCrumbs(); $crumbs = $this->buildApplicationCrumbs();
$crumbs->setBorder(true); $crumbs->setBorder(true);
require_celerity_resource('project-view-css');
$home = phutil_tag(
'div',
array(
'class' => 'project-view-home',
),
array(
$header,
$columns,
));
return $this->newPage() return $this->newPage()
->setTitle($user->getUsername()) ->setTitle($user->getUsername())
->setNavigation($nav) ->setNavigation($nav)
@ -115,7 +106,7 @@ final class PhabricatorPeopleProfileViewController
} }
$view = id(new PHUIBoxView()) $view = id(new PHUIBoxView())
->setColor(PHUIBoxView::GREY) ->setBorder(true)
->appendChild($view) ->appendChild($view)
->addClass('project-view-properties'); ->addClass('project-view-properties');
@ -174,7 +165,7 @@ final class PhabricatorPeopleProfileViewController
$box = id(new PHUIObjectBoxView()) $box = id(new PHUIObjectBoxView())
->setHeader($header) ->setHeader($header)
->appendChild($list) ->appendChild($list)
->setBackground(PHUIBoxView::GREY); ->setBackground(PHUIObjectBoxView::GREY);
return $box; return $box;
} }
@ -217,8 +208,9 @@ final class PhabricatorPeopleProfileViewController
$box = id(new PHUIObjectBoxView()) $box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Badges')) ->setHeaderText(pht('Badges'))
->addClass('project-view-badges')
->appendChild($flex) ->appendChild($flex)
->setBackground(PHUIBoxView::GREY); ->setBackground(PHUIObjectBoxView::GREY);
return $box; return $box;
} }

View file

@ -118,10 +118,7 @@ final class PhameBlogManageController extends PhameBlogController {
$properties->invokeWillRenderEvent(); $properties->invokeWillRenderEvent();
if (strlen($blog->getDescription())) { if (strlen($blog->getDescription())) {
$description = PhabricatorMarkupEngine::renderOneObject( $description = new PHUIRemarkupView($viewer, $description);
id(new PhabricatorMarkupOneOff())->setContent($blog->getDescription()),
'default',
$viewer);
$properties->addSectionHeader( $properties->addSectionHeader(
pht('Description'), pht('Description'),
PHUIPropertyListView::ICON_SUMMARY); PHUIPropertyListView::ICON_SUMMARY);

View file

@ -44,16 +44,7 @@ final class PhameBlogViewController extends PhameLiveController {
$header->setStatus($header_icon, $header_color, $header_name); $header->setStatus($header_icon, $header_color, $header_name);
$actions = $this->renderActions($blog); $actions = $this->renderActions($blog);
$action_button = id(new PHUIButtonView()) $header->setActionList($actions);
->setTag('a')
->setText(pht('Actions'))
->setHref('#')
->setIcon('fa-bars')
->addClass('phui-mobile-menu')
->setDropdownMenu($actions);
$header->addActionLink($action_button);
$header->setPolicyObject($blog); $header->setPolicyObject($blog);
} }

View file

@ -24,17 +24,8 @@ final class PhamePostViewController
if (!$is_external) { if (!$is_external) {
$actions = $this->renderActions($post); $actions = $this->renderActions($post);
$action_button = id(new PHUIButtonView())
->setTag('a')
->setText(pht('Actions'))
->setHref('#')
->setIcon('fa-bars')
->addClass('phui-mobile-menu')
->setDropdownMenu($actions);
$header->setPolicyObject($post); $header->setPolicyObject($post);
$header->addActionLink($action_button); $header->setActionList($actions);
} }
$document = id(new PHUIDocumentViewPro()) $document = id(new PHUIDocumentViewPro())

View file

@ -52,6 +52,13 @@ final class PHUIHandleTagListView extends AphrontTagView {
protected function getTagContent() { protected function getTagContent() {
$handles = $this->handles; $handles = $this->handles;
// Remove any archived projects from the list.
foreach ($handles as $key => $handle) {
if ($handle->getStatus() == PhabricatorObjectHandle::STATUS_CLOSED) {
unset($handles[$key]);
}
}
// If the list is empty, we may render a "No Projects" tag. // If the list is empty, we may render a "No Projects" tag.
if (!$handles) { if (!$handles) {
if (strlen($this->noDataString)) { if (strlen($this->noDataString)) {

View file

@ -86,16 +86,13 @@ final class PholioInlineController extends PholioController {
), ),
$author_handle->renderLink()); $author_handle->renderLink());
$inline_content = new PHUIRemarkupView($viewer, $inline->getContent());
$comment_body = phutil_tag( $comment_body = phutil_tag(
'div', 'div',
array( array(
'class' => 'pholio-inline-comment-body', 'class' => 'pholio-inline-comment-body',
), ),
PhabricatorMarkupEngine::renderOneObject( $inline_content);
id(new PhabricatorMarkupOneOff())
->setContent($inline->getContent()),
'default',
$viewer));
return $this->newDialog() return $this->newDialog()
->setTitle(pht('Inline Comment')) ->setTitle(pht('Inline Comment'))

View file

@ -48,12 +48,7 @@ abstract class PhortuneCartController
return null; return null;
} }
$output = PhabricatorMarkupEngine::renderOneObject( $output = new PHUIRemarkupView($this->getUser(), $description);
id(new PhabricatorMarkupOneOff())
->setPreserveLinebreaks(true)
->setContent($description),
'default',
$this->getViewer());
$box = id(new PHUIBoxView()) $box = id(new PHUIBoxView())
->addMargin(PHUI::MARGIN_LARGE) ->addMargin(PHUI::MARGIN_LARGE)

View file

@ -136,11 +136,7 @@ final class PhortuneMerchantViewController
$description = $merchant->getDescription(); $description = $merchant->getDescription();
if (strlen($description)) { if (strlen($description)) {
$description = PhabricatorMarkupEngine::renderOneObject( $description = new PHUIRemarkupView($viewer, $description);
id(new PhabricatorMarkupOneOff())->setContent($description),
'default',
$viewer);
$view->addSectionHeader( $view->addSectionHeader(
pht('Description'), PHUIPropertyListView::ICON_SUMMARY); pht('Description'), PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent($description); $view->addTextContent($description);

View file

@ -203,19 +203,11 @@ final class PhrictionDocumentController
$crumbs->addCrumb($view); $crumbs->addCrumb($view);
} }
$action_button = id(new PHUIButtonView())
->setTag('a')
->setText(pht('Actions'))
->setHref('#')
->setIcon('fa-bars')
->addClass('phui-mobile-menu')
->setDropdownMenu($actions);
$header = id(new PHUIHeaderView()) $header = id(new PHUIHeaderView())
->setUser($viewer) ->setUser($viewer)
->setPolicyObject($document) ->setPolicyObject($document)
->setHeader($page_title) ->setHeader($page_title)
->addActionLink($action_button); ->setActionList($actions);
if ($content) { if ($content) {
$header->setEpoch($content->getDateCreated()); $header->setEpoch($content->getDateCreated());

View file

@ -134,16 +134,12 @@ final class PhabricatorPhurlURLViewController
$properties->invokeWillRenderEvent(); $properties->invokeWillRenderEvent();
if (strlen($url->getDescription())) { $description = $url->getDescription();
$description = PhabricatorMarkupEngine::renderOneObject( if (strlen($description)) {
id(new PhabricatorMarkupOneOff())->setContent($url->getDescription()), $description = new PHUIRemarkupView($viewer, $description);
'default',
$viewer);
$properties->addSectionHeader( $properties->addSectionHeader(
pht('Description'), pht('Description'),
PHUIPropertyListView::ICON_SUMMARY); PHUIPropertyListView::ICON_SUMMARY);
$properties->addTextContent($description); $properties->addTextContent($description);
} }

View file

@ -101,6 +101,9 @@ final class PhabricatorPolicyEditEngineExtension
if ($capability == PhabricatorPolicyCapability::CAN_VIEW) { if ($capability == PhabricatorPolicyCapability::CAN_VIEW) {
$type_space = PhabricatorTransactions::TYPE_SPACE; $type_space = PhabricatorTransactions::TYPE_SPACE;
if (isset($types[$type_space])) { if (isset($types[$type_space])) {
$space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID(
$object);
$space_field = id(new PhabricatorSpaceEditField()) $space_field = id(new PhabricatorSpaceEditField())
->setKey('spacePHID') ->setKey('spacePHID')
->setLabel(pht('Space')) ->setLabel(pht('Space'))
@ -114,7 +117,7 @@ final class PhabricatorPolicyEditEngineExtension
->setConduitDescription( ->setConduitDescription(
pht('Shift the object between spaces.')) pht('Shift the object between spaces.'))
->setConduitTypeDescription(pht('New space PHID.')) ->setConduitTypeDescription(pht('New space PHID.'))
->setValue($object->getSpacePHID()); ->setValue($space_phid);
$fields[] = $space_field; $fields[] = $space_field;
$space_field->setPolicyField($policy_field); $space_field->setPolicyField($policy_field);

View file

@ -44,7 +44,6 @@ final class PonderQuestionViewController extends PonderController {
$actions = $this->buildActionListView($question); $actions = $this->buildActionListView($question);
$properties = $this->buildPropertyListView($question, $actions); $properties = $this->buildPropertyListView($question, $actions);
$sidebar = $this->buildSidebar($question);
$content_id = celerity_generate_unique_node_id(); $content_id = celerity_generate_unique_node_id();
$timeline = $this->buildTransactionTimeline( $timeline = $this->buildTransactionTimeline(
@ -81,20 +80,6 @@ final class PonderQuestionViewController extends PonderController {
->addPropertyList($properties) ->addPropertyList($properties)
->appendChild($footer); ->appendChild($footer);
if ($viewer->getPHID() == $question->getAuthorPHID()) {
$status = $question->getStatus();
$answers_list = $question->getAnswers();
if ($answers_list && ($status == PonderQuestionStatus::STATUS_OPEN)) {
$info_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->appendChild(
pht(
'If this question has been resolved, please consider closing
the question and marking the answer as helpful.'));
$object_box->setInfoView($info_view);
}
}
$crumbs = $this->buildApplicationCrumbs($this->buildSideNavView()); $crumbs = $this->buildApplicationCrumbs($this->buildSideNavView());
$crumbs->addTextCrumb('Q'.$id, '/Q'.$id); $crumbs->addTextCrumb('Q'.$id, '/Q'.$id);
@ -107,21 +92,14 @@ final class PonderQuestionViewController extends PonderController {
->appendChild($answer); ->appendChild($answer);
} }
$ponder_view = id(new PHUITwoColumnView()) return $this->buildApplicationPage(
->setMainColumn(array( array(
$crumbs,
$object_box, $object_box,
$comment_view, $comment_view,
$answer_wiki, $answer_wiki,
$answers, $answers,
$answer_add_panel, $answer_add_panel,
))
->setSideColumn($sidebar)
->addClass('ponder-question-view');
return $this->buildApplicationPage(
array(
$crumbs,
$ponder_view,
), ),
array( array(
'title' => 'Q'.$question->getID().' '.$question->getTitle(), 'title' => 'Q'.$question->getID().' '.$question->getTitle(),
@ -261,48 +239,4 @@ final class PonderQuestionViewController extends PonderController {
return $view; return $view;
} }
private function buildSidebar(PonderQuestion $question) {
$viewer = $this->getViewer();
$status = $question->getStatus();
$id = $question->getID();
$questions = id(new PonderQuestionQuery())
->setViewer($viewer)
->withStatuses(array($status))
->withEdgeLogicPHIDs(
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
PhabricatorQueryConstraint::OPERATOR_OR,
$question->getProjectPHIDs())
->setLimit(10)
->execute();
$list = id(new PHUIObjectItemListView())
->setUser($viewer)
->setNoDataString(pht('No similar questions found.'));
foreach ($questions as $question) {
if ($id == $question->getID()) {
continue;
}
$item = new PHUIObjectItemView();
$item->setObjectName('Q'.$question->getID());
$item->setHeader($question->getTitle());
$item->setHref('/Q'.$question->getID());
$item->setObject($question);
$item->addAttribute(
pht(
'%s Answer(s)',
new PhutilNumber($question->getAnswerCount())));
$list->addItem($item);
}
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Similar Questions'))
->setObjectList($list);
return $box;
}
} }

View file

@ -1013,6 +1013,51 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase {
$this->assertColumns($expect, $user, $board, $task); $this->assertColumns($expect, $user, $board, $task);
} }
public function testColumnExtendedPolicies() {
$user = $this->createUser();
$user->save();
$board = $this->createProject($user);
$column = $this->addColumn($user, $board, 0);
// At first, the user should be able to view and edit the column.
$column = $this->refreshColumn($user, $column);
$this->assertTrue((bool)$column);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$user,
$column,
PhabricatorPolicyCapability::CAN_EDIT);
$this->assertTrue($can_edit);
// Now, set the project edit policy to "Members of Project". This should
// disable editing.
$members_policy = id(new PhabricatorProjectMembersPolicyRule())
->getObjectPolicyFullKey();
$board->setEditPolicy($members_policy)->save();
$column = $this->refreshColumn($user, $column);
$this->assertTrue((bool)$column);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$user,
$column,
PhabricatorPolicyCapability::CAN_EDIT);
$this->assertFalse($can_edit);
// Now, join the project. This should make the column editable again.
$this->joinProject($board, $user);
$column = $this->refreshColumn($user, $column);
$this->assertTrue((bool)$column);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$user,
$column,
PhabricatorPolicyCapability::CAN_EDIT);
$this->assertTrue($can_edit);
}
private function moveToColumn( private function moveToColumn(
PhabricatorUser $viewer, PhabricatorUser $viewer,
PhabricatorProject $board, PhabricatorProject $board,
@ -1251,6 +1296,22 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase {
} }
} }
private function refreshColumn(
PhabricatorUser $viewer,
PhabricatorProjectColumn $column) {
$results = id(new PhabricatorProjectColumnQuery())
->setViewer($viewer)
->withIDs(array($column->getID()))
->execute();
if ($results) {
return head($results);
} else {
return null;
}
}
private function createProject( private function createProject(
PhabricatorUser $user, PhabricatorUser $user,
PhabricatorProject $parent = null, PhabricatorProject $parent = null,

View file

@ -84,6 +84,10 @@ final class PhabricatorProjectApplication extends PhabricatorApplication {
=> 'PhabricatorProjectBoardReorderController', => 'PhabricatorProjectBoardReorderController',
'disable/' 'disable/'
=> 'PhabricatorProjectBoardDisableController', => 'PhabricatorProjectBoardDisableController',
'manage/'
=> 'PhabricatorProjectBoardManageController',
'background/'
=> 'PhabricatorProjectBoardBackgroundController',
), ),
'update/(?P<id>[1-9]\d*)/(?P<action>[^/]+)/' 'update/(?P<id>[1-9]\d*)/(?P<action>[^/]+)/'
=> 'PhabricatorProjectUpdateController', => 'PhabricatorProjectUpdateController',

View file

@ -0,0 +1,124 @@
<?php
final class PhabricatorProjectWorkboardBackgroundColor extends Phobject {
public static function getOptions() {
$options = array(
array(
'key' => '',
'name' => pht('Use Parent Background (Default)'),
'special' => 'parent',
'icon' => 'fa-chevron-circle-up',
'group' => 'basic',
),
array(
'key' => 'none',
'name' => pht('No Background'),
'special' => 'none',
'icon' => 'fa-ban',
'group' => 'basic',
),
array(
'key' => 'red',
'name' => pht('Red'),
),
array(
'key' => 'orange',
'name' => pht('Orange'),
),
array(
'key' => 'yellow',
'name' => pht('Yellow'),
),
array(
'key' => 'green',
'name' => pht('Green'),
),
array(
'key' => 'blue',
'name' => pht('Blue'),
),
array(
'key' => 'indigo',
'name' => pht('Indigo'),
),
array(
'key' => 'violet',
'name' => pht('Violet'),
),
array(
'key' => 'sky',
'name' => pht('Sky'),
),
array(
'key' => 'pink',
'name' => pht('Pink'),
),
array(
'key' => 'fire',
'name' => pht('Fire'),
),
array(
'key' => 'grey',
'name' => pht('Grey'),
),
array(
'key' => 'gradient-red',
'name' => pht('Ripe Peach'),
),
array(
'key' => 'gradient-orange',
'name' => pht('Ripe Orange'),
),
array(
'key' => 'gradient-yellow',
'name' => pht('Ripe Mango'),
),
array(
'key' => 'gradient-green',
'name' => pht('Shallows'),
),
array(
'key' => 'gradient-blue',
'name' => pht('Reef'),
),
array(
'key' => 'gradient-bluegrey',
'name' => pht('Depths'),
),
array(
'key' => 'gradient-indigo',
'name' => pht('This One Is Purple'),
),
array(
'key' => 'gradient-violet',
'name' => pht('Unripe Plum'),
),
array(
'key' => 'gradient-sky',
'name' => pht('Blue Sky'),
),
array(
'key' => 'gradient-pink',
'name' => pht('Intensity'),
),
array(
'key' => 'gradient-grey',
'name' => pht('Into The Expanse'),
),
);
foreach ($options as $key => $option) {
if (empty($option['group'])) {
if (preg_match('/^gradient/', $option['key'])) {
$option['group'] = 'gradient';
} else {
$option['group'] = 'solid';
}
}
$options[$key] = $option;
}
return ipull($options, null, 'key');
}
}

View file

@ -0,0 +1,168 @@
<?php
final class PhabricatorProjectBoardBackgroundController
extends PhabricatorProjectBoardController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$board_id = $request->getURIData('projectID');
$board = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withIDs(array($board_id))
->needImages(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$board) {
return new Aphront404Response();
}
if (!$board->getHasWorkboard()) {
return new Aphront404Response();
}
$this->setProject($board);
$id = $board->getID();
$manage_uri = $this->getApplicationURI("board/{$id}/manage/");
if ($request->isFormPost()) {
$background_key = $request->getStr('backgroundKey');
$xactions = array();
$xactions[] = id(new PhabricatorProjectTransaction())
->setTransactionType(PhabricatorProjectTransaction::TYPE_BACKGROUND)
->setNewValue($background_key);
id(new PhabricatorProjectTransactionEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->applyTransactions($board, $xactions);
return id(new AphrontRedirectResponse())
->setURI($manage_uri);
}
$nav = $this->getProfileMenu();
$crumbs = id($this->buildApplicationCrumbs())
->addTextCrumb(pht('Workboard'), "/project/board/{$board_id}/")
->addTextCrumb(pht('Manage'), $manage_uri)
->addTextCrumb(pht('Background Color'));
$form = id(new AphrontFormView())
->setUser($viewer);
$group_info = array(
'basic' => array(
'label' => pht('Basics'),
),
'solid' => array(
'label' => pht('Solid Colors'),
),
'gradient' => array(
'label' => pht('Gradients'),
),
);
$groups = array();
$options = PhabricatorProjectWorkboardBackgroundColor::getOptions();
$option_groups = igroup($options, 'group');
require_celerity_resource('people-profile-css');
require_celerity_resource('phui-workboard-color-css');
Javelin::initBehavior('phabricator-tooltips', array());
foreach ($group_info as $group_key => $spec) {
$buttons = array();
$available_options = idx($option_groups, $group_key, array());
foreach ($available_options as $option) {
$buttons[] = $this->renderOptionButton($option);
}
$form->appendControl(
id(new AphrontFormMarkupControl())
->setLabel($spec['label'])
->setValue($buttons));
}
// NOTE: Each button is its own form, so we can't wrap them in a normal
// form.
$layout_view = $form->buildLayoutView();
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Edit Background Color'))
->appendChild($layout_view);
return $this->newPage()
->setTitle(
array(
pht('Edit Background Color'),
$board->getDisplayName(),
))
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($form_box);
}
private function renderOptionButton(array $option) {
$viewer = $this->getViewer();
$icon = idx($option, 'icon');
if ($icon) {
$preview_class = null;
$preview_content = id(new PHUIIconView())
->setIcon($icon, 'lightbluetext');
} else {
$preview_class = 'phui-workboard-'.$option['key'];
$preview_content = null;
}
$preview = phutil_tag(
'div',
array(
'class' => 'phui-workboard-color-preview '.$preview_class,
),
$preview_content);
$button = javelin_tag(
'button',
array(
'class' => 'grey profile-image-button',
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => $option['name'],
'size' => 300,
),
),
$preview);
$input = phutil_tag(
'input',
array(
'type' => 'hidden',
'name' => 'backgroundKey',
'value' => $option['key'],
));
return phabricator_form(
$viewer,
array(
'class' => 'profile-image-form',
'method' => 'POST',
),
array(
$button,
$input,
));
}
}

View file

@ -0,0 +1,174 @@
<?php
final class PhabricatorProjectBoardManageController
extends PhabricatorProjectBoardController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$board_id = $request->getURIData('projectID');
$board = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withIDs(array($board_id))
->needImages(true)
->executeOne();
if (!$board) {
return new Aphront404Response();
}
$this->setProject($board);
// Perform layout of no tasks to load and populate the columns in the
// correct order.
$layout_engine = id(new PhabricatorBoardLayoutEngine())
->setViewer($viewer)
->setBoardPHIDs(array($board->getPHID()))
->setObjectPHIDs(array())
->setFetchAllBoards(true)
->executeLayout();
$columns = $layout_engine->getColumns($board->getPHID());
$board_id = $board->getID();
$header = $this->buildHeaderView($board);
$actions = $this->buildActionView($board);
$properties = $this->buildPropertyView($board);
$properties->setActionList($actions);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Workboard'), "/project/board/{$board_id}/");
$crumbs->addTextCrumb(pht('Manage'));
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$nav = $this->getProfileMenu();
$title = array(
pht('Manage Workboard'),
$board->getDisplayName(),
);
$columns_list = $this->buildColumnsList($board, $columns);
return $this->newPage()
->setTitle($title)
->setNavigation($nav)
->setCrumbs($crumbs)
->appendChild(
array(
$box,
$columns_list,
));
}
private function buildHeaderView(PhabricatorProject $board) {
$viewer = $this->getRequest()->getUser();
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader(pht('Workboard: %s', $board->getDisplayName()));
return $header;
}
private function buildActionView(PhabricatorProject $board) {
$viewer = $this->getRequest()->getUser();
$id = $board->getID();
$actions = id(new PhabricatorActionListView())
->setUser($viewer);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$board,
PhabricatorPolicyCapability::CAN_EDIT);
$reorder_uri = $this->getApplicationURI("board/{$id}/reorder/");
$actions->addAction(
id(new PhabricatorActionView())
->setIcon('fa-exchange')
->setName(pht('Reorder Columns'))
->setHref($reorder_uri)
->setDisabled(!$can_edit)
->setWorkflow(true));
$background_uri = $this->getApplicationURI("board/{$id}/background/");
$actions->addAction(
id(new PhabricatorActionView())
->setIcon('fa-paint-brush')
->setName(pht('Change Background Color'))
->setHref($background_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$disable_uri = $this->getApplicationURI("board/{$id}/disable/");
$actions->addAction(
id(new PhabricatorActionView())
->setIcon('fa-ban')
->setName(pht('Disable Board'))
->setHref($disable_uri)
->setDisabled(!$can_edit)
->setWorkflow(true));
return $actions;
}
private function buildPropertyView(
PhabricatorProject $board) {
$viewer = $this->getRequest()->getUser();
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($board);
$background = $board->getDisplayWorkboardBackgroundColor();
if ($background !== null) {
$map = PhabricatorProjectWorkboardBackgroundColor::getOptions();
$map = ipull($map, 'name');
$name = idx($map, $background, $background);
$properties->addProperty(pht('Background Color'), $name);
}
return $properties;
}
private function buildColumnsList(
PhabricatorProject $board,
array $columns) {
assert_instances_of($columns, 'PhabricatorProjectColumn');
$board_id = $board->getID();
$view = id(new PHUIObjectItemListView())
->setNoDataString(pht('This board has no columns.'));
foreach ($columns as $column) {
$column_id = $column->getID();
$detail_uri = "/project/board/{$board_id}/column/{$column_id}/";
$item = id(new PHUIObjectItemView())
->setHeader($column->getDisplayName())
->setHref($detail_uri);
if ($column->isHidden()) {
$item->setDisabled(true);
}
$view->addItem($item);
}
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Columns'))
->setObjectList($view);
}
}

View file

@ -23,13 +23,13 @@ final class PhabricatorProjectBoardReorderController
$this->setProject($project); $this->setProject($project);
$project_id = $project->getID(); $project_id = $project->getID();
$board_uri = $this->getApplicationURI("board/{$project_id}/"); $manage_uri = $this->getApplicationURI("board/{$project_id}/manage/");
$reorder_uri = $this->getApplicationURI("board/{$project_id}/reorder/"); $reorder_uri = $this->getApplicationURI("board/{$project_id}/reorder/");
if ($request->isFormPost()) { if ($request->isFormPost()) {
// User clicked "Done", make sure the page reloads to show the new // User clicked "Done", make sure the page reloads to show the new
// column order. // column order.
return id(new AphrontRedirectResponse())->setURI($board_uri); return id(new AphrontRedirectResponse())->setURI($manage_uri);
} }
$columns = id(new PhabricatorProjectColumnQuery()) $columns = id(new PhabricatorProjectColumnQuery())

View file

@ -124,10 +124,14 @@ final class PhabricatorProjectBoardViewController
$board_phid = $project->getPHID(); $board_phid = $project->getPHID();
// Regardless of display order, pass tasks to the layout engine in ID order
// so layout is consistent.
$board_tasks = msort($tasks, 'getID');
$layout_engine = id(new PhabricatorBoardLayoutEngine()) $layout_engine = id(new PhabricatorBoardLayoutEngine())
->setViewer($viewer) ->setViewer($viewer)
->setBoardPHIDs(array($board_phid)) ->setBoardPHIDs(array($board_phid))
->setObjectPHIDs(array_keys($tasks)) ->setObjectPHIDs(array_keys($board_tasks))
->setFetchAllBoards(true) ->setFetchAllBoards(true)
->executeLayout(); ->executeLayout();
@ -427,7 +431,7 @@ final class PhabricatorProjectBoardViewController
$crumbs->addAction($manage_menu); $crumbs->addAction($manage_menu);
$crumbs->addAction($fullscreen); $crumbs->addAction($fullscreen);
return $this->newPage() $page = $this->newPage()
->setTitle( ->setTitle(
array( array(
$project->getDisplayName(), $project->getDisplayName(),
@ -445,6 +449,17 @@ final class PhabricatorProjectBoardViewController
array( array(
$board_box, $board_box,
)); ));
$background = $project->getDisplayWorkboardBackgroundColor();
if ($background !== null) {
require_celerity_resource('phui-workboard-color-css');
$background_color_class = "phui-workboard-{$background}";
$page->addClass('phui-workboard-color');
$page->addClass($background_color_class);
}
return $page;
} }
private function readRequestState() { private function readRequestState() {
@ -671,9 +686,8 @@ final class PhabricatorProjectBoardViewController
$id = $project->getID(); $id = $project->getID();
$disable_uri = $this->getApplicationURI("board/{$id}/disable/"); $manage_uri = $this->getApplicationURI("board/{$id}/manage/");
$add_uri = $this->getApplicationURI("board/{$id}/edit/"); $add_uri = $this->getApplicationURI("board/{$id}/edit/");
$reorder_uri = $this->getApplicationURI("board/{$id}/reorder/");
$can_edit = PhabricatorPolicyFilter::hasCapability( $can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer, $viewer,
@ -687,14 +701,12 @@ final class PhabricatorProjectBoardViewController
->setName(pht('Add Column')) ->setName(pht('Add Column'))
->setHref($add_uri) ->setHref($add_uri)
->setDisabled(!$can_edit) ->setDisabled(!$can_edit)
->setWorkflow(!$can_edit); ->setWorkflow(true);
$manage_items[] = id(new PhabricatorActionView()) $manage_items[] = id(new PhabricatorActionView())
->setIcon('fa-exchange') ->setIcon('fa-pencil')
->setName(pht('Reorder Columns')) ->setName(pht('Manage Board'))
->setHref($reorder_uri) ->setHref($manage_uri);
->setDisabled(!$can_edit)
->setWorkflow(true);
if ($show_hidden) { if ($show_hidden) {
$hidden_uri = $this->getURIWithState() $hidden_uri = $this->getURIWithState()
@ -726,13 +738,6 @@ final class PhabricatorProjectBoardViewController
->setHref($batch_edit_uri) ->setHref($batch_edit_uri)
->setDisabled(!$can_batch_edit); ->setDisabled(!$can_batch_edit);
$manage_items[] = id(new PhabricatorActionView())
->setIcon('fa-ban')
->setName(pht('Disable Workboard'))
->setHref($disable_uri)
->setWorkflow(true)
->setDisabled(!$can_edit);
$manage_menu = id(new PhabricatorActionListView()) $manage_menu = id(new PhabricatorActionListView())
->setUser($viewer); ->setUser($viewer);
foreach ($manage_items as $item) { foreach ($manage_items as $item) {
@ -817,14 +822,6 @@ final class PhabricatorProjectBoardViewController
->setHref($batch_edit_uri) ->setHref($batch_edit_uri)
->setDisabled(!$can_batch_edit); ->setDisabled(!$can_batch_edit);
$detail_uri = $this->getApplicationURI(
'board/'.$this->id.'/column/'.$column->getID().'/');
$column_items[] = id(new PhabricatorActionView())
->setIcon('fa-columns')
->setName(pht('Column Details'))
->setHref($detail_uri);
$can_hide = ($can_edit && !$column->isDefaultColumn()); $can_hide = ($can_edit && !$column->isDefaultColumn());
$hide_uri = 'board/'.$this->id.'/hide/'.$column->getID().'/'; $hide_uri = 'board/'.$this->id.'/hide/'.$column->getID().'/';
$hide_uri = $this->getApplicationURI($hide_uri); $hide_uri = $this->getApplicationURI($hide_uri);

View file

@ -73,8 +73,7 @@ final class PhabricatorProjectColumnDetailController
$header = id(new PHUIHeaderView()) $header = id(new PHUIHeaderView())
->setUser($viewer) ->setUser($viewer)
->setHeader($column->getDisplayName()) ->setHeader($column->getDisplayName());
->setPolicyObject($column);
if ($column->isHidden()) { if ($column->isHidden()) {
$header->setStatus('fa-ban', 'dark', pht('Hidden')); $header->setStatus('fa-ban', 'dark', pht('Hidden'));

View file

@ -136,21 +136,13 @@ final class PhabricatorProjectColumnEditController
$submit = pht('Save Column'); $submit = pht('Save Column');
} }
$form->appendChild( return $this->newDialog()
id(new AphrontFormSubmitControl()) ->setWidth(AphrontDialogView::WIDTH_FORM)
->setValue($submit)
->addCancelButton($view_uri));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setValidationException($validation_exception)
->setForm($form);
$nav = $this->getProfileMenu();
return $this->newPage()
->setTitle($title) ->setTitle($title)
->setNavigation($nav) ->appendForm($form)
->appendChild($form_box); ->setValidationException($validation_exception)
->addCancelButton($view_uri)
->addSubmitButton($submit);
} }
} }

View file

@ -24,6 +24,11 @@ final class PhabricatorProjectEditController
$id = $request->getURIData('id'); $id = $request->getURIData('id');
if (!$id) { if (!$id) {
// This capability is checked again later, but checking it here
// explicitly gives us a better error message.
$this->requireApplicationCapability(
ProjectCreateProjectsCapability::CAPABILITY);
$parent_id = head($request->getArr('parent')); $parent_id = head($request->getArr('parent'));
if (!$parent_id) { if (!$parent_id) {
$parent_id = $request->getStr('parent'); $parent_id = $request->getStr('parent');

View file

@ -59,14 +59,14 @@ final class PhabricatorProjectProfileController
->setUser($viewer) ->setUser($viewer)
->setProject($project) ->setProject($project)
->setLimit(5) ->setLimit(5)
->setBackground(PHUIBoxView::GREY) ->setBackground(PHUIObjectBoxView::GREY)
->setUserPHIDs($project->getMemberPHIDs()); ->setUserPHIDs($project->getMemberPHIDs());
$watcher_list = id(new PhabricatorProjectWatcherListView()) $watcher_list = id(new PhabricatorProjectWatcherListView())
->setUser($viewer) ->setUser($viewer)
->setProject($project) ->setProject($project)
->setLimit(5) ->setLimit(5)
->setBackground(PHUIBoxView::GREY) ->setBackground(PHUIObjectBoxView::GREY)
->setUserPHIDs($project->getWatcherPHIDs()); ->setUserPHIDs($project->getWatcherPHIDs());
$nav = $this->getProfileMenu(); $nav = $this->getProfileMenu();
@ -83,8 +83,11 @@ final class PhabricatorProjectProfileController
$feed = $this->renderStories($stories); $feed = $this->renderStories($stories);
$feed = phutil_tag_div('project-view-feed', $feed); $feed = phutil_tag_div('project-view-feed', $feed);
require_celerity_resource('project-view-css');
$columns = id(new PHUITwoColumnView()) $home = id(new PHUITwoColumnView())
->setHeader($header)
->addClass('project-view-home')
->setMainColumn( ->setMainColumn(
array( array(
$properties, $properties,
@ -101,17 +104,6 @@ final class PhabricatorProjectProfileController
$crumbs = $this->buildApplicationCrumbs(); $crumbs = $this->buildApplicationCrumbs();
$crumbs->setBorder(true); $crumbs->setBorder(true);
require_celerity_resource('project-view-css');
$home = phutil_tag(
'div',
array(
'class' => 'project-view-home',
),
array(
$header,
$columns,
));
return $this->newPage() return $this->newPage()
->setNavigation($nav) ->setNavigation($nav)
->setCrumbs($crumbs) ->setCrumbs($crumbs)
@ -142,7 +134,7 @@ final class PhabricatorProjectProfileController
} }
$view = id(new PHUIBoxView()) $view = id(new PHUIBoxView())
->setColor(PHUIBoxView::GREY) ->setBorder(true)
->appendChild($view) ->appendChild($view)
->addClass('project-view-properties'); ->addClass('project-view-properties');
@ -162,19 +154,32 @@ final class PhabricatorProjectProfileController
private function renderWatchAction(PhabricatorProject $project) { private function renderWatchAction(PhabricatorProject $project) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();
$viewer_phid = $viewer->getPHID();
$id = $project->getID(); $id = $project->getID();
$is_watcher = ($viewer_phid && $project->isUserWatcher($viewer_phid)); if (!$viewer->isLoggedIn()) {
$is_watcher = false;
$is_ancestor = false;
} else {
$viewer_phid = $viewer->getPHID();
$is_watcher = $project->isUserWatcher($viewer_phid);
$is_ancestor = $project->isUserAncestorWatcher($viewer_phid);
}
if (!$is_watcher) { if ($is_ancestor && !$is_watcher) {
$watch_icon = 'fa-eye';
$watch_text = pht('Watching Ancestor');
$watch_href = "/project/watch/{$id}/?via=profile";
$watch_disabled = true;
} else if (!$is_watcher) {
$watch_icon = 'fa-eye'; $watch_icon = 'fa-eye';
$watch_text = pht('Watch Project'); $watch_text = pht('Watch Project');
$watch_href = "/project/watch/{$id}/?via=profile"; $watch_href = "/project/watch/{$id}/?via=profile";
$watch_disabled = false;
} else { } else {
$watch_icon = 'fa-eye-slash'; $watch_icon = 'fa-eye-slash';
$watch_text = pht('Unwatch Project'); $watch_text = pht('Unwatch Project');
$watch_href = "/project/unwatch/{$id}/?via=profile"; $watch_href = "/project/unwatch/{$id}/?via=profile";
$watch_disabled = false;
} }
$watch_icon = id(new PHUIIconView()) $watch_icon = id(new PHUIIconView())
@ -185,7 +190,8 @@ final class PhabricatorProjectProfileController
->setWorkflow(true) ->setWorkflow(true)
->setIcon($watch_icon) ->setIcon($watch_icon)
->setText($watch_text) ->setText($watch_text)
->setHref($watch_href); ->setHref($watch_href)
->setDisabled($watch_disabled);
} }
private function buildMilestoneList(PhabricatorProject $project) { private function buildMilestoneList(PhabricatorProject $project) {
@ -205,7 +211,7 @@ final class PhabricatorProjectProfileController
array( array(
PhabricatorProjectStatus::STATUS_ACTIVE, PhabricatorProjectStatus::STATUS_ACTIVE,
)) ))
->setOrder('newest') ->setOrderVector(array('milestoneNumber', 'id'))
->execute(); ->execute();
if (!$milestones) { if (!$milestones) {
return null; return null;
@ -230,7 +236,7 @@ final class PhabricatorProjectProfileController
return id(new PHUIObjectBoxView()) return id(new PHUIObjectBoxView())
->setHeader($header) ->setHeader($header)
->setBackground(PHUIBoxView::GREY) ->setBackground(PHUIObjectBoxView::GREY)
->setObjectList($milestone_list); ->setObjectList($milestone_list);
} }
@ -278,7 +284,7 @@ final class PhabricatorProjectProfileController
return id(new PHUIObjectBoxView()) return id(new PHUIObjectBoxView())
->setHeader($header) ->setHeader($header)
->setBackground(PHUIBoxView::GREY) ->setBackground(PHUIObjectBoxView::GREY)
->setObjectList($subproject_list); ->setObjectList($subproject_list);
} }

View file

@ -43,7 +43,7 @@ final class PhabricatorProjectSubprojectsController
->withParentProjectPHIDs(array($project->getPHID())) ->withParentProjectPHIDs(array($project->getPHID()))
->needImages(true) ->needImages(true)
->withIsMilestone(true) ->withIsMilestone(true)
->setOrder('newest') ->setOrderVector(array('milestoneNumber', 'id'))
->execute(); ->execute();
} else { } else {
$milestones = array(); $milestones = array();
@ -181,6 +181,9 @@ final class PhabricatorProjectSubprojectsController
$viewer = $this->getViewer(); $viewer = $this->getViewer();
$id = $project->getID(); $id = $project->getID();
$can_create = $this->hasApplicationCapability(
ProjectCreateProjectsCapability::CAPABILITY);
$can_edit = PhabricatorPolicyFilter::hasCapability( $can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer, $viewer,
$project, $project,
@ -198,7 +201,7 @@ final class PhabricatorProjectSubprojectsController
$milestone_text = pht('Create Milestone'); $milestone_text = pht('Create Milestone');
} }
$can_milestone = ($can_edit && $allows_milestones); $can_milestone = ($can_create && $can_edit && $allows_milestones);
$milestone_href = "/project/edit/?milestone={$id}"; $milestone_href = "/project/edit/?milestone={$id}";
$view->addAction( $view->addAction(
@ -209,7 +212,7 @@ final class PhabricatorProjectSubprojectsController
->setDisabled(!$can_milestone) ->setDisabled(!$can_milestone)
->setWorkflow(!$can_milestone)); ->setWorkflow(!$can_milestone));
$can_subproject = ($can_edit && $allows_subprojects); $can_subproject = ($can_create && $can_edit && $allows_subprojects);
// If we're offering to create the first subproject, we're going to warn // If we're offering to create the first subproject, we're going to warn
// the user about the effects before moving forward. // the user about the effects before moving forward.

View file

@ -25,6 +25,23 @@ final class PhabricatorProjectWatchController
$done_uri = "/project/members/{$id}/"; $done_uri = "/project/members/{$id}/";
} }
$is_watcher = $project->isUserWatcher($viewer->getPHID());
$is_ancestor = $project->isUserAncestorWatcher($viewer->getPHID());
if ($is_ancestor && !$is_watcher) {
$ancestor_phid = $project->getWatchedAncestorPHID($viewer->getPHID());
$handles = $viewer->loadHandles(array($ancestor_phid));
$ancestor_handle = $handles[$ancestor_phid];
return $this->newDialog()
->setTitle(pht('Watching Ancestor'))
->appendParagraph(
pht(
'You are already watching %s, an ancestor of this project, and '.
'are thus watching all of its subprojects.',
$ancestor_handle->renderTag()->render()))
->addCancelbutton($done_uri);
}
if ($request->isDialogFormPost()) { if ($request->isDialogFormPost()) {
$edge_action = null; $edge_action = null;
switch ($action) { switch ($action) {
@ -61,10 +78,14 @@ final class PhabricatorProjectWatchController
switch ($action) { switch ($action) {
case 'watch': case 'watch':
$title = pht('Watch Project?'); $title = pht('Watch Project?');
$body = pht( $body = array();
$body[] = pht(
'Watching a project will let you monitor it closely. You will '. 'Watching a project will let you monitor it closely. You will '.
'receive email and notifications about changes to every object '. 'receive email and notifications about changes to every object '.
'associated with projects you watch.'); 'tagged with projects you watch.');
$body[] = pht(
'Watching a project also watches all subprojects and milestones of '.
'that project.');
$submit = pht('Watch Project'); $submit = pht('Watch Project');
break; break;
case 'unwatch': case 'unwatch':
@ -78,12 +99,17 @@ final class PhabricatorProjectWatchController
return new Aphront404Response(); return new Aphront404Response();
} }
return $this->newDialog() $dialog = $this->newDialog()
->setTitle($title) ->setTitle($title)
->addHiddenInput('via', $via) ->addHiddenInput('via', $via)
->appendParagraph($body)
->addCancelButton($done_uri) ->addCancelButton($done_uri)
->addSubmitButton($submit); ->addSubmitButton($submit);
foreach ((array)$body as $paragraph) {
$dialog->appendParagraph($paragraph);
}
return $dialog;
} }
} }

View file

@ -42,6 +42,7 @@ final class PhabricatorProjectTransactionEditor
$types[] = PhabricatorProjectTransaction::TYPE_HASWORKBOARD; $types[] = PhabricatorProjectTransaction::TYPE_HASWORKBOARD;
$types[] = PhabricatorProjectTransaction::TYPE_DEFAULT_SORT; $types[] = PhabricatorProjectTransaction::TYPE_DEFAULT_SORT;
$types[] = PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER; $types[] = PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER;
$types[] = PhabricatorProjectTransaction::TYPE_BACKGROUND;
return $types; return $types;
} }
@ -77,6 +78,8 @@ final class PhabricatorProjectTransactionEditor
return $object->getDefaultWorkboardSort(); return $object->getDefaultWorkboardSort();
case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER: case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER:
return $object->getDefaultWorkboardFilter(); return $object->getDefaultWorkboardFilter();
case PhabricatorProjectTransaction::TYPE_BACKGROUND:
return $object->getWorkboardBackgroundColor();
} }
return parent::getCustomTransactionOldValue($object, $xaction); return parent::getCustomTransactionOldValue($object, $xaction);
@ -100,6 +103,12 @@ final class PhabricatorProjectTransactionEditor
return $xaction->getNewValue(); return $xaction->getNewValue();
case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: case PhabricatorProjectTransaction::TYPE_HASWORKBOARD:
return (int)$xaction->getNewValue(); return (int)$xaction->getNewValue();
case PhabricatorProjectTransaction::TYPE_BACKGROUND:
$value = $xaction->getNewValue();
if (!strlen($value)) {
return null;
}
return $value;
case PhabricatorProjectTransaction::TYPE_SLUGS: case PhabricatorProjectTransaction::TYPE_SLUGS:
return $this->normalizeSlugs($xaction->getNewValue()); return $this->normalizeSlugs($xaction->getNewValue());
} }
@ -153,6 +162,9 @@ final class PhabricatorProjectTransactionEditor
case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER: case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER:
$object->setDefaultWorkboardFilter($xaction->getNewValue()); $object->setDefaultWorkboardFilter($xaction->getNewValue());
return; return;
case PhabricatorProjectTransaction::TYPE_BACKGROUND:
$object->setWorkboardBackgroundColor($xaction->getNewValue());
return;
} }
return parent::applyCustomInternalTransaction($object, $xaction); return parent::applyCustomInternalTransaction($object, $xaction);
@ -198,6 +210,7 @@ final class PhabricatorProjectTransactionEditor
case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: case PhabricatorProjectTransaction::TYPE_HASWORKBOARD:
case PhabricatorProjectTransaction::TYPE_DEFAULT_SORT: case PhabricatorProjectTransaction::TYPE_DEFAULT_SORT:
case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER: case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER:
case PhabricatorProjectTransaction::TYPE_BACKGROUND:
return; return;
} }

View file

@ -506,6 +506,7 @@ final class PhabricatorBoardLayoutEngine extends Phobject {
} }
} }
$view_sequence = 1;
foreach ($object_phids as $object_phid) { foreach ($object_phids as $object_phid) {
$positions = idx($position_groups, $object_phid, array()); $positions = idx($position_groups, $object_phid, array());
@ -554,7 +555,8 @@ final class PhabricatorBoardLayoutEngine extends Phobject {
->setBoardPHID($board_phid) ->setBoardPHID($board_phid)
->setColumnPHID($proxy_hit) ->setColumnPHID($proxy_hit)
->setObjectPHID($object_phid) ->setObjectPHID($object_phid)
->setSequence(0); ->setSequence(0)
->setViewSequence($view_sequence++);
$this->addQueue[] = $new_position; $this->addQueue[] = $new_position;
@ -578,7 +580,8 @@ final class PhabricatorBoardLayoutEngine extends Phobject {
->setBoardPHID($board_phid) ->setBoardPHID($board_phid)
->setColumnPHID($default_phid) ->setColumnPHID($default_phid)
->setObjectPHID($object_phid) ->setObjectPHID($object_phid)
->setSequence(0); ->setSequence(0)
->setViewSequence($view_sequence++);
$this->addQueue[] = $new_position; $this->addQueue[] = $new_position;

View file

@ -6,6 +6,7 @@ final class PhabricatorProjectQuery
private $ids; private $ids;
private $phids; private $phids;
private $memberPHIDs; private $memberPHIDs;
private $watcherPHIDs;
private $slugs; private $slugs;
private $slugNormals; private $slugNormals;
private $slugMap; private $slugMap;
@ -62,6 +63,11 @@ final class PhabricatorProjectQuery
return $this; return $this;
} }
public function withWatcherPHIDs(array $watcher_phids) {
$this->watcherPHIDs = $watcher_phids;
return $this;
}
public function withSlugs(array $slugs) { public function withSlugs(array $slugs) {
$this->slugs = $slugs; $this->slugs = $slugs;
return $this; return $this;
@ -170,6 +176,11 @@ final class PhabricatorProjectQuery
'type' => 'string', 'type' => 'string',
'unique' => true, 'unique' => true,
), ),
'milestoneNumber' => array(
'table' => $this->getPrimaryTableAlias(),
'column' => 'milestoneNumber',
'type' => 'int',
),
); );
} }
@ -242,7 +253,7 @@ final class PhabricatorProjectQuery
$all_graph = $this->getAllReachableAncestors($projects); $all_graph = $this->getAllReachableAncestors($projects);
if ($this->needAncestorMembers) { if ($this->needAncestorMembers || $this->needWatchers) {
$src_projects = $all_graph; $src_projects = $all_graph;
} else { } else {
$src_projects = $projects; $src_projects = $projects;
@ -250,11 +261,13 @@ final class PhabricatorProjectQuery
$all_sources = array(); $all_sources = array();
foreach ($src_projects as $project) { foreach ($src_projects as $project) {
// For milestones, we need parent members.
if ($project->isMilestone()) { if ($project->isMilestone()) {
$phid = $project->getParentProjectPHID(); $parent_phid = $project->getParentProjectPHID();
} else { $all_sources[$parent_phid] = $parent_phid;
$phid = $project->getPHID();
} }
$phid = $project->getPHID();
$all_sources[$phid] = $phid; $all_sources[$phid] = $phid;
} }
@ -313,7 +326,7 @@ final class PhabricatorProjectQuery
if ($this->needWatchers) { if ($this->needWatchers) {
$watcher_phids = $edge_query->getDestinationPHIDs( $watcher_phids = $edge_query->getDestinationPHIDs(
$source_phids, array($project_phid),
array($watcher_type)); array($watcher_type));
$project->attachWatcherPHIDs($watcher_phids); $project->attachWatcherPHIDs($watcher_phids);
$project->setIsUserWatcher( $project->setIsUserWatcher(
@ -429,6 +442,13 @@ final class PhabricatorProjectQuery
$this->memberPHIDs); $this->memberPHIDs);
} }
if ($this->watcherPHIDs !== null) {
$where[] = qsprintf(
$conn,
'w.dst IN (%Ls)',
$this->watcherPHIDs);
}
if ($this->slugs !== null) { if ($this->slugs !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
@ -542,7 +562,7 @@ final class PhabricatorProjectQuery
} }
protected function shouldGroupQueryResultRows() { protected function shouldGroupQueryResultRows() {
if ($this->memberPHIDs || $this->nameTokens) { if ($this->memberPHIDs || $this->watcherPHIDs || $this->nameTokens) {
return true; return true;
} }
return parent::shouldGroupQueryResultRows(); return parent::shouldGroupQueryResultRows();
@ -559,6 +579,14 @@ final class PhabricatorProjectQuery
PhabricatorProjectMaterializedMemberEdgeType::EDGECONST); PhabricatorProjectMaterializedMemberEdgeType::EDGECONST);
} }
if ($this->watcherPHIDs !== null) {
$joins[] = qsprintf(
$conn,
'JOIN %T w ON w.src = p.phid AND w.type = %d',
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
PhabricatorObjectHasWatcherEdgeType::EDGECONST);
}
if ($this->slugs !== null) { if ($this->slugs !== null) {
$joins[] = qsprintf( $joins[] = qsprintf(
$conn, $conn,

View file

@ -26,6 +26,10 @@ final class PhabricatorProjectSearchEngine
->setLabel(pht('Members')) ->setLabel(pht('Members'))
->setKey('memberPHIDs') ->setKey('memberPHIDs')
->setAliases(array('member', 'members')), ->setAliases(array('member', 'members')),
id(new PhabricatorUsersSearchField())
->setLabel(pht('Watchers'))
->setKey('watcherPHIDs')
->setAliases(array('watcher', 'watchers')),
id(new PhabricatorSearchSelectField()) id(new PhabricatorSearchSelectField())
->setLabel(pht('Status')) ->setLabel(pht('Status'))
->setKey('status') ->setKey('status')
@ -54,6 +58,10 @@ final class PhabricatorProjectSearchEngine
$query->withMemberPHIDs($map['memberPHIDs']); $query->withMemberPHIDs($map['memberPHIDs']);
} }
if ($map['watcherPHIDs']) {
$query->withWatcherPHIDs($map['watcherPHIDs']);
}
if ($map['status']) { if ($map['status']) {
$status = idx($this->getStatusValues(), $map['status']); $status = idx($this->getStatusValues(), $map['status']);
if ($status) { if ($status) {

View file

@ -287,6 +287,32 @@ final class PhabricatorProject extends PhabricatorProjectDAO
return $this->assertAttachedKey($this->sparseWatchers, $user_phid); return $this->assertAttachedKey($this->sparseWatchers, $user_phid);
} }
public function isUserAncestorWatcher($user_phid) {
$is_watcher = $this->isUserWatcher($user_phid);
if (!$is_watcher) {
$parent = $this->getParentProject();
if ($parent) {
return $parent->isUserWatcher($user_phid);
}
}
return $is_watcher;
}
public function getWatchedAncestorPHID($user_phid) {
if ($this->isUserWatcher($user_phid)) {
return $this->getPHID();
}
$parent = $this->getParentProject();
if ($parent) {
return $parent->getWatchedAncestorPHID($user_phid);
}
return null;
}
public function setIsUserWatcher($user_phid, $is_watcher) { public function setIsUserWatcher($user_phid, $is_watcher) {
if ($this->sparseWatchers === self::ATTACHABLE) { if ($this->sparseWatchers === self::ATTACHABLE) {
$this->sparseWatchers = array(); $this->sparseWatchers = array();
@ -304,6 +330,21 @@ final class PhabricatorProject extends PhabricatorProjectDAO
return $this->assertAttached($this->watcherPHIDs); return $this->assertAttached($this->watcherPHIDs);
} }
public function getAllAncestorWatcherPHIDs() {
$parent = $this->getParentProject();
if ($parent) {
$watchers = $parent->getAllAncestorWatcherPHIDs();
} else {
$watchers = array();
}
foreach ($this->getWatcherPHIDs() as $phid) {
$watchers[$phid] = $phid;
}
return $watchers;
}
public function attachSlugs(array $slugs) { public function attachSlugs(array $slugs) {
$this->slugs = $slugs; $this->slugs = $slugs;
return $this; return $this;
@ -579,6 +620,31 @@ final class PhabricatorProject extends PhabricatorProjectDAO
return $this->setProperty('workboard.filter.default', $filter); return $this->setProperty('workboard.filter.default', $filter);
} }
public function getWorkboardBackgroundColor() {
return $this->getProperty('workboard.background');
}
public function setWorkboardBackgroundColor($color) {
return $this->setProperty('workboard.background', $color);
}
public function getDisplayWorkboardBackgroundColor() {
$color = $this->getWorkboardBackgroundColor();
if ($color === null) {
$parent = $this->getParentProject();
if ($parent) {
return $parent->getDisplayWorkboardBackgroundColor();
}
}
if ($color === 'none') {
$color = null;
}
return $color;
}
/* -( PhabricatorCustomFieldInterface )------------------------------------ */ /* -( PhabricatorCustomFieldInterface )------------------------------------ */

View file

@ -5,7 +5,8 @@ final class PhabricatorProjectColumn
implements implements
PhabricatorApplicationTransactionInterface, PhabricatorApplicationTransactionInterface,
PhabricatorPolicyInterface, PhabricatorPolicyInterface,
PhabricatorDestructibleInterface { PhabricatorDestructibleInterface,
PhabricatorExtendedPolicyInterface {
const STATUS_ACTIVE = 0; const STATUS_ACTIVE = 0;
const STATUS_HIDDEN = 1; const STATUS_HIDDEN = 1;
@ -169,14 +170,16 @@ final class PhabricatorProjectColumn
// Normal columns and subproject columns go first, in a user-controlled // Normal columns and subproject columns go first, in a user-controlled
// order. // order.
// All the milestone columns go last, in their sequential order. // All the milestone columns go last, in reverse order (newest on the
// left) so that you don't have to scroll across older milestones to get
// to the newest ones.
if (!$proxy || !$proxy->isMilestone()) { if (!$proxy || !$proxy->isMilestone()) {
$group = 'A'; $group = 'A';
$sequence = $this->getSequence(); $sequence = $this->getSequence();
} else { } else {
$group = 'B'; $group = 'B';
$sequence = $proxy->getMilestoneNumber(); $sequence = (10000000 - $proxy->getMilestoneNumber());
} }
return sprintf('%s%012d', $group, $sequence); return sprintf('%s%012d', $group, $sequence);
@ -217,7 +220,14 @@ final class PhabricatorProjectColumn
} }
public function getPolicy($capability) { public function getPolicy($capability) {
return $this->getProject()->getPolicy($capability); // NOTE: Column policies are enforced as an extended policy which makes
// them the same as the project's policies.
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return PhabricatorPolicies::getMostOpenPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return PhabricatorPolicies::POLICY_USER;
}
} }
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
@ -231,6 +241,16 @@ final class PhabricatorProjectColumn
} }
/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */
public function getExtendedPolicy($capability, PhabricatorUser $viewer) {
return array(
array($this->getProject(), $capability),
);
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */ /* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently( public function destroyObjectPermanently(

View file

@ -9,6 +9,7 @@ final class PhabricatorProjectColumnPosition extends PhabricatorProjectDAO
protected $sequence; protected $sequence;
private $column = self::ATTACHABLE; private $column = self::ATTACHABLE;
private $viewSequence = 0;
protected function getConfiguration() { protected function getConfiguration() {
return array( return array(
@ -40,18 +41,30 @@ final class PhabricatorProjectColumnPosition extends PhabricatorProjectDAO
return $this; return $this;
} }
public function getOrderingKey() { public function setViewSequence($view_sequence) {
if (!$this->getID() && !$this->getSequence()) { $this->viewSequence = $view_sequence;
return 0; return $this;
} }
// Low sequence numbers go above high sequence numbers. public function getOrderingKey() {
// High position IDs go above low position IDs. // We're ordering both real positions and "virtual" positions which we have
// Broadly, this makes newly added stuff float to the top. // created but not saved yet.
// Low sequence numbers go above high sequence numbers. Virtual positions
// will have sequence number 0.
// High virtual sequence numbers go above low virtual sequence numbers.
// The layout engine gets objects in ID order, and this puts them in
// reverse ID order.
// High IDs go above low IDs.
// Broadly, this collectively makes newly added stuff float to the top.
return sprintf( return sprintf(
'~%012d%012d', '~%012d%012d%012d',
$this->getSequence(), $this->getSequence(),
((1 << 31) - $this->viewSequence),
((1 << 31) - $this->getID())); ((1 << 31) - $this->getID()));
} }

View file

@ -15,6 +15,7 @@ final class PhabricatorProjectTransaction
const TYPE_HASWORKBOARD = 'project:hasworkboard'; const TYPE_HASWORKBOARD = 'project:hasworkboard';
const TYPE_DEFAULT_SORT = 'project:sort'; const TYPE_DEFAULT_SORT = 'project:sort';
const TYPE_DEFAULT_FILTER = 'project:filter'; const TYPE_DEFAULT_FILTER = 'project:filter';
const TYPE_BACKGROUND = 'project:background';
// NOTE: This is deprecated, members are just a normal edge now. // NOTE: This is deprecated, members are just a normal edge now.
const TYPE_MEMBERS = 'project:members'; const TYPE_MEMBERS = 'project:members';
@ -73,6 +74,7 @@ final class PhabricatorProjectTransaction
case self::TYPE_HASWORKBOARD: case self::TYPE_HASWORKBOARD:
case self::TYPE_DEFAULT_SORT: case self::TYPE_DEFAULT_SORT:
case self::TYPE_DEFAULT_FILTER: case self::TYPE_DEFAULT_FILTER:
case self::TYPE_BACKGROUND:
return true; return true;
} }
@ -84,6 +86,7 @@ final class PhabricatorProjectTransaction
case self::TYPE_HASWORKBOARD: case self::TYPE_HASWORKBOARD:
case self::TYPE_DEFAULT_SORT: case self::TYPE_DEFAULT_SORT:
case self::TYPE_DEFAULT_FILTER: case self::TYPE_DEFAULT_FILTER:
case self::TYPE_BACKGROUND:
return true; return true;
} }
@ -291,6 +294,11 @@ final class PhabricatorProjectTransaction
return pht( return pht(
'%s changed the default filter for the project workboard.', '%s changed the default filter for the project workboard.',
$author_handle); $author_handle);
case self::TYPE_BACKGROUND:
return pht(
'%s changed the background color of the project workboard.',
$author_handle);
} }
return parent::getTitle(); return parent::getTitle();

View file

@ -45,6 +45,7 @@ final class PhabricatorRepositoryEditor
$types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES; $types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES;
$types[] = PhabricatorRepositoryTransaction::TYPE_STAGING_URI; $types[] = PhabricatorRepositoryTransaction::TYPE_STAGING_URI;
$types[] = PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS; $types[] = PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS;
$types[] = PhabricatorRepositoryTransaction::TYPE_CALLSIGN;
$types[] = PhabricatorTransactions::TYPE_EDGE; $types[] = PhabricatorTransactions::TYPE_EDGE;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
@ -110,6 +111,8 @@ final class PhabricatorRepositoryEditor
return $object->getDetail('staging-uri'); return $object->getDetail('staging-uri');
case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS: case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
return $object->getDetail('automation.blueprintPHIDs', array()); return $object->getDetail('automation.blueprintPHIDs', array());
case PhabricatorRepositoryTransaction::TYPE_CALLSIGN:
return $object->getCallsign();
} }
} }
@ -148,6 +151,7 @@ final class PhabricatorRepositoryEditor
case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS: case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
return $xaction->getNewValue(); return $xaction->getNewValue();
case PhabricatorRepositoryTransaction::TYPE_SLUG: case PhabricatorRepositoryTransaction::TYPE_SLUG:
case PhabricatorRepositoryTransaction::TYPE_CALLSIGN:
$name = $xaction->getNewValue(); $name = $xaction->getNewValue();
if (strlen($name)) { if (strlen($name)) {
return $name; return $name;
@ -240,6 +244,9 @@ final class PhabricatorRepositoryEditor
'automation.blueprintPHIDs', 'automation.blueprintPHIDs',
$xaction->getNewValue()); $xaction->getNewValue());
return; return;
case PhabricatorRepositoryTransaction::TYPE_CALLSIGN:
$object->setCallsign($xaction->getNewValue());
return;
case PhabricatorRepositoryTransaction::TYPE_ENCODING: case PhabricatorRepositoryTransaction::TYPE_ENCODING:
// Make sure the encoding is valid by converting to UTF-8. This tests // Make sure the encoding is valid by converting to UTF-8. This tests
// that the user has mbstring installed, and also that they didn't type // that the user has mbstring installed, and also that they didn't type
@ -468,7 +475,7 @@ final class PhabricatorRepositoryEditor
} }
try { try {
PhabricatorRepository::asssertValidRepositorySlug($new); PhabricatorRepository::assertValidRepositorySlug($new);
} catch (Exception $ex) { } catch (Exception $ex) {
$errors[] = new PhabricatorApplicationTransactionValidationError( $errors[] = new PhabricatorApplicationTransactionValidationError(
$type, $type,
@ -495,6 +502,47 @@ final class PhabricatorRepositoryEditor
} }
break; break;
case PhabricatorRepositoryTransaction::TYPE_CALLSIGN:
foreach ($xactions as $xaction) {
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
if (!strlen($new)) {
continue;
}
if ($new === $old) {
continue;
}
try {
PhabricatorRepository::assertValidCallsign($new);
} catch (Exception $ex) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
$ex->getMessage(),
$xaction);
continue;
}
$other = id(new PhabricatorRepositoryQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withCallsigns(array($new))
->executeOne();
if ($other && ($other->getID() !== $object->getID())) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Duplicate'),
pht(
'The selected callsign ("%s") is already in use by another '.
'repository. Choose a unique callsign.',
$new),
$xaction);
continue;
}
}
break;
} }
return $errors; return $errors;
@ -522,4 +570,27 @@ final class PhabricatorRepositoryEditor
return true; return true;
} }
protected function applyFinalEffects(
PhabricatorLiskDAO $object,
array $xactions) {
// If the repository does not have a local path yet, assign it one based
// on its ID. We can't do this earlier because we won't have an ID yet.
$local_path = $object->getDetail('local-path');
if (!strlen($local_path)) {
$local_key = 'repository.default-local-path';
$local_root = PhabricatorEnv::getEnvConfig($local_key);
$local_root = rtrim($local_root, '/');
$id = $object->getID();
$local_path = "{$local_root}/{$id}/";
$object->setDetail('local-path', $local_path);
$object->save();
}
return $xactions;
}
} }

View file

@ -192,7 +192,7 @@ final class PhabricatorRepositoryPullEngine
} }
private function getHookContextIdentifier(PhabricatorRepository $repository) { private function getHookContextIdentifier(PhabricatorRepository $repository) {
$identifier = $repository->getCallsign(); $identifier = $repository->getPHID();
$instance = PhabricatorEnv::getEnvConfig('cluster.instance'); $instance = PhabricatorEnv::getEnvConfig('cluster.instance');
if (strlen($instance)) { if (strlen($instance)) {

View file

@ -93,7 +93,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
), ),
self::CONFIG_COLUMN_SCHEMA => array( self::CONFIG_COLUMN_SCHEMA => array(
'name' => 'sort255', 'name' => 'sort255',
'callsign' => 'sort32', 'callsign' => 'sort32?',
'repositorySlug' => 'sort64?', 'repositorySlug' => 'sort64?',
'versionControlSystem' => 'text32', 'versionControlSystem' => 'text32',
'uuid' => 'text64?', 'uuid' => 'text64?',
@ -149,13 +149,21 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
} }
public function getMonogram() { public function getMonogram() {
return 'r'.$this->getCallsign(); $callsign = $this->getCallsign();
if (strlen($callsign)) {
return "r{$callsign}";
}
$id = $this->getID();
return "R{$id}";
} }
public function getDisplayName() { public function getDisplayName() {
// TODO: This is intended to produce a human-readable name that is not $slug = $this->getRepositorySlug();
// necessarily a global, unique identifier. Eventually, it may just return if (strlen($slug)) {
// a string like "skynet" instead of "rSKYNET". return $slug;
}
return $this->getMonogram(); return $this->getMonogram();
} }
@ -317,14 +325,14 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
public static function isValidRepositorySlug($slug) { public static function isValidRepositorySlug($slug) {
try { try {
self::asssertValidRepositorySlug($slug); self::assertValidRepositorySlug($slug);
return true; return true;
} catch (Exception $ex) { } catch (Exception $ex) {
return false; return false;
} }
} }
public static function asssertValidRepositorySlug($slug) { public static function assertValidRepositorySlug($slug) {
if (!strlen($slug)) { if (!strlen($slug)) {
throw new Exception( throw new Exception(
pht( pht(
@ -391,6 +399,30 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
} }
} }
public static function assertValidCallsign($callsign) {
if (!strlen($callsign)) {
throw new Exception(
pht(
'A repository callsign must be at least one character long.'));
}
if (strlen($callsign) > 32) {
throw new Exception(
pht(
'The callsign "%s" is not a valid repository callsign. Callsigns '.
'must be no more than 32 bytes long.',
$callsign));
}
if (!preg_match('/^[A-Z]+\z/', $callsign)) {
throw new Exception(
pht(
'The callsign "%s" is not a valid repository callsign. Callsigns '.
'may only contain UPPERCASE letters.',
$callsign));
}
}
/* -( Remote Command Execution )------------------------------------------- */ /* -( Remote Command Execution )------------------------------------------- */
@ -675,7 +707,13 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
} }
public function getURI() { public function getURI() {
return '/diffusion/'.$this->getCallsign().'/'; $callsign = $this->getCallsign();
if (strlen($callsign)) {
return "/diffusion/{$callsign}/";
}
$id = $this->getID();
return "/diffusion/{$id}/";
} }
public function getPathURI($path) { public function getPathURI($path) {
@ -684,9 +722,98 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
public function getCommitURI($identifier) { public function getCommitURI($identifier) {
$callsign = $this->getCallsign(); $callsign = $this->getCallsign();
if (strlen($callsign)) {
return "/r{$callsign}{$identifier}"; return "/r{$callsign}{$identifier}";
} }
$id = $this->getID();
return "/R{$id}:{$identifier}";
}
public static function parseRepositoryServicePath($request_path) {
// NOTE: In Mercurial over SSH, the path will begin without a leading "/",
// so we're matching it optionally.
$patterns = array(
'(^'.
'(?P<base>/?diffusion/(?P<identifier>[A-Z]+|[0-9]\d*))'.
'(?P<path>(?:/.*)?)'.
'\z)',
);
$identifier = null;
foreach ($patterns as $pattern) {
$matches = null;
if (!preg_match($pattern, $request_path, $matches)) {
continue;
}
$identifier = $matches['identifier'];
$base = $matches['base'];
$path = $matches['path'];
break;
}
if ($identifier === null) {
return null;
}
return array(
'identifier' => $identifier,
'base' => $base,
'path' => $path,
);
}
public function getCanonicalPath($request_path) {
$standard_pattern =
'(^'.
'(?P<prefix>/diffusion/)'.
'(?P<identifier>[^/]+)'.
'(?P<suffix>(?:/.*)?)'.
'\z)';
$matches = null;
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'];
if (!strlen($suffix)) {
$suffix = '/';
}
return $prefix.$identifier.$suffix;
}
$commit_pattern =
'(^'.
'(?P<prefix>/)'.
'(?P<monogram>'.
'(?:'.
'r(?P<repositoryCallsign>[A-Z]+)'.
'|'.
'R(?P<repositoryID>[1-9]\d*):'.
')'.
'(?P<commit>[a-f0-9]+)'.
')'.
'\z)';
$matches = null;
if (preg_match($commit_pattern, $request_path, $matches)) {
$commit = $matches['commit'];
return $this->getCommitURI($commit);
}
return null;
}
public function generateURI(array $params) { public function generateURI(array $params) {
$req_branch = false; $req_branch = false;
$req_commit = false; $req_commit = false;
@ -955,7 +1082,13 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
} }
if ($need_scope) { if ($need_scope) {
$scope = 'r'.$this->getCallsign(); $callsign = $this->getCallsign();
if ($callsign) {
$scope = "r{$callsign}";
} else {
$id = $this->getID();
$scope = "R{$id}:";
}
$name = $scope.$name; $name = $scope.$name;
} }

View file

@ -29,6 +29,7 @@ final class PhabricatorRepositoryTransaction
const TYPE_SYMBOLS_LANGUAGE = 'repo:symbol-language'; const TYPE_SYMBOLS_LANGUAGE = 'repo:symbol-language';
const TYPE_STAGING_URI = 'repo:staging-uri'; const TYPE_STAGING_URI = 'repo:staging-uri';
const TYPE_AUTOMATION_BLUEPRINTS = 'repo:automation-blueprints'; const TYPE_AUTOMATION_BLUEPRINTS = 'repo:automation-blueprints';
const TYPE_CALLSIGN = 'repo:callsign';
// TODO: Clean up these legacy transaction types. // TODO: Clean up these legacy transaction types.
const TYPE_SSH_LOGIN = 'repo:ssh-login'; const TYPE_SSH_LOGIN = 'repo:ssh-login';
@ -466,6 +467,26 @@ final class PhabricatorRepositoryTransaction
new PhutilNumber(count($rem)), new PhutilNumber(count($rem)),
$this->renderHandleList($rem)); $this->renderHandleList($rem));
} }
case self::TYPE_CALLSIGN:
if ($old === null) {
return pht(
'%s set the callsign for this repository to "%s".',
$this->renderHandleLink($author_phid),
$new);
} else if ($new === null) {
return pht(
'%s removed the callsign ("%s") for this repository.',
$this->renderHandleLink($author_phid),
$old);
} else {
return pht(
'%s changed the callsign for this repository from "%s" to "%s".',
$this->renderHandleLink($author_phid),
$old,
$new);
}
} }
return parent::getTitle(); return parent::getTitle();

View file

@ -9,6 +9,47 @@ final class PhabricatorRepositoryURITestCase
); );
} }
public function testRepositoryURICanonicalization() {
$repo = id(new PhabricatorRepository())
->makeEphemeral()
->setID(123);
$tests = array(
'/diffusion/123' => '/diffusion/123/',
'/diffusion/123/' => '/diffusion/123/',
'/diffusion/123/browse/master/' => '/diffusion/123/browse/master/',
'/kangaroo/' => null,
);
foreach ($tests as $input => $expect) {
$this->assertEqual(
$expect,
$repo->getCanonicalPath($input),
pht('Canonical Path (ID, No Callsign): %s', $input));
}
$repo->setCallsign('XYZ');
$tests = array(
'/diffusion/123' => '/diffusion/XYZ/',
'/diffusion/123/' => '/diffusion/XYZ/',
'/diffusion/123/browse/master/' => '/diffusion/XYZ/browse/master/',
'/diffusion/XYZ' => '/diffusion/XYZ/',
'/diffusion/XYZ/' => '/diffusion/XYZ/',
'/diffusion/XYZ/browse/master/' => '/diffusion/XYZ/browse/master/',
'/diffusion/ABC/' => '/diffusion/XYZ/',
'/kangaroo/' => null,
'/R1:abcdef' => '/rXYZabcdef',
);
foreach ($tests as $input => $expect) {
$this->assertEqual(
$expect,
$repo->getCanonicalPath($input),
pht('Canonical Path (ID, Callsign): %s', $input));
}
}
public function testURIGeneration() { public function testURIGeneration() {
$svn = PhabricatorRepositoryType::REPOSITORY_TYPE_SVN; $svn = PhabricatorRepositoryType::REPOSITORY_TYPE_SVN;
$git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT; $git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;

View file

@ -122,13 +122,13 @@ final class PhabricatorSlowvotePollController
$view->invokeWillRenderEvent(); $view->invokeWillRenderEvent();
if (strlen($poll->getDescription())) { $description = $poll->getDescription();
$view->addTextContent( if (strlen($description)) {
$output = PhabricatorMarkupEngine::renderOneObject( $description = new PHUIRemarkupView($viewer, $description);
id(new PhabricatorMarkupOneOff())->setContent( $view->addSectionHeader(
$poll->getDescription()), pht('Description'),
'default', PHUIPropertyListView::ICON_SUMMARY);
$viewer)); $view->addTextContent($description);
} }
return $view; return $view;

View file

@ -97,6 +97,19 @@ final class PhabricatorSlowvoteTransaction
return parent::getTitle(); return parent::getTitle();
} }
public function getRemarkupBlocks() {
$blocks = parent::getRemarkupBlocks();
$type = $this->getTransactionType();
switch ($type) {
case self::TYPE_DESCRIPTION:
$blocks[] = $this->getNewValue();
break;
}
return $blocks;
}
public function getTitleForFeed() { public function getTitleForFeed() {
$author_phid = $this->getAuthorPHID(); $author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID(); $object_phid = $this->getObjectPHID();

View file

@ -74,13 +74,9 @@ final class SlowvoteEmbedView extends AphrontView {
$header = id(new PHUIHeaderView()) $header = id(new PHUIHeaderView())
->setHeader($link_to_slowvote); ->setHeader($link_to_slowvote);
$description = null; $description = $poll->getDescription();
if ($poll->getDescription()) { if (strlen($description)) {
$description = PhabricatorMarkupEngine::renderOneObject( $description = new PHUIRemarkupView($this->getUser(), $description);
id(new PhabricatorMarkupOneOff())->setContent(
$poll->getDescription()),
'default',
$this->getUser());
$description = phutil_tag( $description = phutil_tag(
'div', 'div',
array( array(

View file

@ -83,15 +83,10 @@ final class PhabricatorSpacesViewController
$description = $space->getDescription(); $description = $space->getDescription();
if (strlen($description)) { if (strlen($description)) {
$description = PhabricatorMarkupEngine::renderOneObject( $description = new PHUIRemarkupView($viewer, $description);
id(new PhabricatorMarkupOneOff())->setContent($description),
'default',
$viewer);
$list->addSectionHeader( $list->addSectionHeader(
pht('Description'), pht('Description'),
PHUIPropertyListView::ICON_SUMMARY); PHUIPropertyListView::ICON_SUMMARY);
$list->addTextContent($description); $list->addTextContent($description);
} }

View file

@ -28,24 +28,10 @@ final class PhabricatorSubscriptionsEditController
->withPHIDs(array($phid)) ->withPHIDs(array($phid))
->executeOne(); ->executeOne();
if (phid_get_type($phid) == PhabricatorProjectProjectPHIDType::TYPECONST) {
// TODO: This is a big hack, but a weak argument for adding some kind
// of "load for role" feature to ObjectQuery, and also not a really great
// argument for adding some kind of "load extra stuff" feature to
// SubscriberInterface. Do this for now and wait for the best way forward
// to become more clear?
$object = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withPHIDs(array($phid))
->needWatchers(true)
->executeOne();
} else {
$object = id(new PhabricatorObjectQuery()) $object = id(new PhabricatorObjectQuery())
->setViewer($viewer) ->setViewer($viewer)
->withPHIDs(array($phid)) ->withPHIDs(array($phid))
->executeOne(); ->executeOne();
}
if (!($object instanceof PhabricatorSubscribableInterface)) { if (!($object instanceof PhabricatorSubscribableInterface)) {
return $this->buildErrorResponse( return $this->buildErrorResponse(

View file

@ -50,10 +50,7 @@ final class PhabricatorApplicationTransactionCommentRawController
$details_text = pht( $details_text = pht(
'For full details, run `/bin/mail show-outbound --id %d`', 'For full details, run `/bin/mail show-outbound --id %d`',
$source_id); $source_id);
$addendum = PhabricatorMarkupEngine::renderOneObject( $addendum = new PHUIRemarkupView($viewer, $details_text);
id(new PhabricatorMarkupOneOff())->setContent($details_text),
'default',
$viewer);
} }
} }
} }

View file

@ -70,6 +70,8 @@ abstract class PhabricatorEditEngineController
->executeOne(); ->executeOne();
if ($config) { if ($config) {
$engine = $config->getEngine(); $engine = $config->getEngine();
} else {
return null;
} }
if (!$engine->isEngineConfigurable()) { if (!$engine->isEngineConfigurable()) {

View file

@ -2607,14 +2607,19 @@ abstract class PhabricatorApplicationTransactionEditor
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
if ($project_phids) { if ($project_phids) {
$watcher_type = PhabricatorObjectHasWatcherEdgeType::EDGECONST; $projects = id(new PhabricatorProjectQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs($project_phids)
->needWatchers(true)
->execute();
$query = id(new PhabricatorEdgeQuery()) $watcher_phids = array();
->withSourcePHIDs($project_phids) foreach ($projects as $project) {
->withEdgeTypes(array($watcher_type)); foreach ($project->getAllAncestorWatcherPHIDs() as $phid) {
$query->execute(); $watcher_phids[$phid] = $phid;
}
}
$watcher_phids = $query->getDestinationPHIDs();
if ($watcher_phids) { if ($watcher_phids) {
// We need to do a visibility check for all the watchers, as // We need to do a visibility check for all the watchers, as
// watching a project is not a guarantee that you can see objects // watching a project is not a guarantee that you can see objects

Some files were not shown because too many files have changed in this diff Show more