diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 644ec2fb2f..b632ba7d1e 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,8 +7,8 @@ */ return array( 'names' => array( - 'core.pkg.css' => 'b59766ad', - 'core.pkg.js' => 'd7daa6d8', + 'core.pkg.css' => '7935f211', + 'core.pkg.js' => '7d8faf57', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '2de124c9', 'differential.pkg.js' => 'd0cd0df6', @@ -17,19 +17,19 @@ return array( 'maniphest.pkg.css' => '4845691a', 'maniphest.pkg.js' => '949a7498', '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/lightbox-attachment.css' => '7acac05d', 'rsrc/css/aphront/list-filter-view.css' => '5d6f0526', '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/phabricator-nav-view.css' => 'a24cb589', + 'rsrc/css/aphront/phabricator-nav-view.css' => 'ac79a758', 'rsrc/css/aphront/table-view.css' => '6d01d468', '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.css' => '0e403212', + 'rsrc/css/aphront/typeahead.css' => 'd4f16145', 'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af', 'rsrc/css/application/auth/auth.css' => '0877ed6e', '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/table-of-contents.css' => 'ae4b7a55', '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/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/harbormaster/harbormaster.css' => 'b0758ca5', '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/paste/paste.css' => 'a5157c48', '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-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.css' => '9149f103', '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-transaction-detail.css' => '82100a43', '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-view.css' => '4693497c', + 'rsrc/css/application/project/project-view.css' => '83bb6654', 'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733', 'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5', 'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '8d8b92cd', @@ -104,7 +104,7 @@ return array( 'rsrc/css/application/tokens/tokens.css' => '3d0f239e', 'rsrc/css/application/uiexample/example.css' => '528b19de', '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/z-index.css' => '5b6fcf3f', '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-badge.css' => 'f25c3476', 'rsrc/css/phui/phui-big-info-view.css' => 'bd903741', - 'rsrc/css/phui/phui-box.css' => '6e8ac7fd', - 'rsrc/css/phui/phui-button.css' => 'd6ac72db', + 'rsrc/css/phui/phui-box.css' => 'dd1294d3', + 'rsrc/css/phui/phui-button.css' => 'edf464e9', 'rsrc/css/phui/phui-chart.css' => '6bf6f78e', '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.css' => '9c71d2bf', 'rsrc/css/phui/phui-feed-story.css' => '04aec08f', 'rsrc/css/phui/phui-fontkit.css' => '9cda225e', 'rsrc/css/phui/phui-form-view.css' => '4a1a0f5e', - 'rsrc/css/phui/phui-form.css' => '0b98e572', - 'rsrc/css/phui/phui-header-view.css' => 'd53cc835', + 'rsrc/css/phui/phui-form.css' => 'aac1d51d', + 'rsrc/css/phui/phui-header-view.css' => '50c5cb6a', 'rsrc/css/phui/phui-hovercard.css' => 'de1a2119', 'rsrc/css/phui/phui-icon-set-selector.css' => '1ab67aad', '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-view.css' => '6d7c3509', 'rsrc/css/phui/phui-list.css' => '9da2aa00', '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-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-remarkup-preview.css' => '1a8f2591', '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-tag-view.css' => '9d5d4400', 'rsrc/css/phui/phui-timeline-view.css' => '2efceff8', - 'rsrc/css/phui/phui-two-column-view.css' => 'c75bfc5b', - 'rsrc/css/phui/workboards/phui-workboard.css' => 'e9e56029', + 'rsrc/css/phui/phui-two-column-view.css' => '0763177e', + '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-workpanel.css' => 'a78c0661', + 'rsrc/css/phui/workboards/phui-workpanel.css' => '92197373', 'rsrc/css/sprite-login.css' => '60e8560e', 'rsrc/css/sprite-menu.css' => '9dd65b92', 'rsrc/css/sprite-tokens.css' => '4f399012', @@ -461,7 +462,7 @@ return array( 'rsrc/js/core/Notification.js' => 'ccf1cbf8', 'rsrc/js/core/Prefab.js' => 'e67df814', '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/ToolTip.js' => '6323f942', '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-toggle-class.js' => '5d7c9f33', '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-workflow.js' => '0a3f3021', 'rsrc/js/core/phtize.js' => 'd254d646', @@ -517,15 +518,15 @@ return array( 'symbols' => array( 'almanac-css' => 'dbb9b3af', 'aphront-bars' => '231ac33c', - 'aphront-dark-console-css' => '6378ef3d', + 'aphront-dark-console-css' => 'f54bf286', 'aphront-dialog-view-css' => 'b4334e08', 'aphront-list-filter-view-css' => '5d6f0526', 'aphront-multi-column-view-css' => 'fd18389d', 'aphront-panel-view-css' => '8427b78d', 'aphront-table-view-css' => '6d01d468', 'aphront-tokenizer-control-css' => '056da01b', - 'aphront-tooltip-css' => '7672b60f', - 'aphront-typeahead-control-css' => '0e403212', + 'aphront-tooltip-css' => '1a07aea8', + 'aphront-typeahead-control-css' => 'd4f16145', 'auth-css' => '0877ed6e', 'bulk-job-css' => 'df9c1d4a', 'changeset-view-manager' => 'a2828756', @@ -550,13 +551,13 @@ return array( 'differential-revision-list-css' => 'f3c47d33', 'differential-table-of-contents-css' => 'ae4b7a55', 'diffusion-icons-css' => '2941baf1', - 'diffusion-readme-css' => '2106ea08', + 'diffusion-readme-css' => '356a4f3c', 'diffusion-source-css' => '075ba788', 'diviner-shared-css' => 'aa3656aa', 'font-aleo' => '8bdb2835', 'font-fontawesome' => 'c43323c5', 'font-lato' => 'c7ccd872', - 'global-drag-and-drop-css' => '697324ad', + 'global-drag-and-drop-css' => '5c1b47c2', 'harbormaster-css' => 'b0758ca5', 'herald-css' => '826075fa', 'herald-rule-editor' => '746ca158', @@ -647,7 +648,7 @@ return array( 'javelin-behavior-phabricator-reveal-content' => '60821bc7', 'javelin-behavior-phabricator-search-typeahead' => '06c32383', '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-list' => '13c739ea', 'javelin-behavior-phabricator-watch-anchor' => '9f36c42d', @@ -760,21 +761,21 @@ return array( 'phabricator-keyboard-shortcut' => '1ae869f2', 'phabricator-keyboard-shortcut-manager' => 'c1700f6f', 'phabricator-main-menu-view' => 'd00a795a', - 'phabricator-nav-view-css' => 'a24cb589', + 'phabricator-nav-view-css' => 'ac79a758', 'phabricator-notification' => 'ccf1cbf8', - 'phabricator-notification-css' => '9c279160', + 'phabricator-notification-css' => '7f684b62', 'phabricator-notification-menu-css' => 'f31c0bde', 'phabricator-object-selector-css' => '85ee8ce6', 'phabricator-phtize' => 'd254d646', 'phabricator-prefab' => 'e67df814', - 'phabricator-remarkup-css' => 'e1c8b32f', + 'phabricator-remarkup-css' => 'fc228f08', 'phabricator-search-results-css' => '7dea472c', 'phabricator-shaped-request' => '7cbe244b', 'phabricator-side-menu-view-css' => '3a3d9f41', 'phabricator-slowvote-css' => 'da0afb1b', 'phabricator-source-code-view-css' => 'cbeef983', 'phabricator-standard-page-view' => 'e709f6d0', - 'phabricator-textareautils' => '9e54692d', + 'phabricator-textareautils' => '5813016a', 'phabricator-title' => 'df5e11d2', 'phabricator-tooltip' => '6323f942', 'phabricator-ui-example-css' => '528b19de', @@ -789,8 +790,8 @@ return array( 'phabricator-uiexample-reactor-sendclass' => '1def2711', 'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee', 'phabricator-zindex-css' => '5b6fcf3f', - 'phame-css' => '1dbbacf9', - 'pholio-css' => '95174bdd', + 'phame-css' => '4ca6fd6c', + 'pholio-css' => 'ca89d380', 'pholio-edit-css' => '3ad9d1ee', 'pholio-inline-comments-css' => '8e545e49', 'phortune-credit-card-form' => '2290aeef', @@ -801,8 +802,8 @@ return array( 'phui-action-panel-css' => '91c7b835', 'phui-badge-view-css' => 'f25c3476', 'phui-big-info-view-css' => 'bd903741', - 'phui-box-css' => '6e8ac7fd', - 'phui-button-css' => 'd6ac72db', + 'phui-box-css' => 'dd1294d3', + 'phui-button-css' => 'edf464e9', 'phui-calendar-css' => 'ccabe893', 'phui-calendar-day-css' => 'd1cf6f93', 'phui-calendar-list-css' => 'c1c7f338', @@ -811,27 +812,27 @@ return array( 'phui-crumbs-view-css' => '79d536e5', 'phui-document-summary-view-css' => '9ca48bdf', 'phui-document-view-css' => '9c71d2bf', - 'phui-document-view-pro-css' => '8799acf7', + 'phui-document-view-pro-css' => 'a8872307', 'phui-feed-story-css' => '04aec08f', 'phui-font-icon-base-css' => 'ecbbb4c2', 'phui-fontkit-css' => '9cda225e', - 'phui-form-css' => '0b98e572', + 'phui-form-css' => 'aac1d51d', 'phui-form-view-css' => '4a1a0f5e', - 'phui-header-view-css' => 'd53cc835', + 'phui-header-view-css' => '50c5cb6a', 'phui-hovercard' => '1bd28176', 'phui-hovercard-view-css' => 'de1a2119', 'phui-icon-set-selector-css' => '1ab67aad', 'phui-icon-view-css' => '3f33ab57', - 'phui-image-mask-css' => '5a8b09c8', + 'phui-image-mask-css' => 'a8498f9c', 'phui-info-panel-css' => '27ea50a1', 'phui-info-view-css' => '6d7c3509', 'phui-inline-comment-view-css' => '0fdb3667', 'phui-list-view-css' => '9da2aa00', 'phui-object-box-css' => '407eaf5a', - 'phui-object-item-list-view-css' => 'be31c3a7', + 'phui-object-item-list-view-css' => '18b2ce8e', 'phui-pager-css' => 'bea33d23', 'phui-pinboard-view-css' => '2495140e', - 'phui-profile-menu-css' => 'f709256c', + 'phui-profile-menu-css' => '7e92a89a', 'phui-property-list-view-css' => '27b2849e', 'phui-remarkup-preview-css' => '1a8f2591', 'phui-segment-bar-view-css' => '46342871', @@ -840,10 +841,11 @@ return array( 'phui-tag-view-css' => '9d5d4400', 'phui-theme-css' => 'ab7b848c', 'phui-timeline-view-css' => '2efceff8', - 'phui-two-column-view-css' => 'c75bfc5b', - 'phui-workboard-view-css' => 'e9e56029', + 'phui-two-column-view-css' => '0763177e', + 'phui-workboard-color-css' => 'ac6fe6a7', + 'phui-workboard-view-css' => 'e6d89647', 'phui-workcard-view-css' => '3646fb96', - 'phui-workpanel-view-css' => 'a78c0661', + 'phui-workpanel-view-css' => '92197373', 'phuix-action-list-view' => 'b5c256b8', 'phuix-action-view' => '8cf6d262', 'phuix-autocomplete' => '9196fb06', @@ -853,9 +855,9 @@ return array( 'policy-css' => '957ea14c', 'policy-edit-css' => '815c66f7', 'policy-transaction-detail-css' => '82100a43', - 'ponder-view-css' => '7b0df4da', + 'ponder-view-css' => 'b40dc156', 'project-card-view-css' => '9418c97d', - 'project-view-css' => '4693497c', + 'project-view-css' => '83bb6654', 'releeph-core' => '9b3c5733', 'releeph-preview-branch' => 'b7a6f4a5', 'releeph-request-differential-create-dialog' => '8d8b92cd', @@ -1117,12 +1119,6 @@ return array( 'javelin-util', 'javelin-uri', ), - '3ee3408b' => array( - 'javelin-behavior', - 'javelin-behavior-device', - 'javelin-stratcom', - 'phabricator-tooltip', - ), '3f5d6dbf' => array( 'javelin-behavior', 'javelin-dom', @@ -1138,6 +1134,12 @@ return array( 'javelin-dom', 'javelin-request', ), + '42fcb747' => array( + 'javelin-behavior', + 'javelin-behavior-device', + 'javelin-stratcom', + 'phabricator-tooltip', + ), '44959b73' => array( 'javelin-util', 'javelin-uri', @@ -1273,6 +1275,11 @@ return array( 'javelin-request', 'javelin-util', ), + '5813016a' => array( + 'javelin-install', + 'javelin-dom', + 'javelin-vector', + ), '59a7976a' => array( 'javelin-install', 'javelin-dom', @@ -1573,6 +1580,9 @@ return array( 'phuix-icon-view', 'phabricator-prefab', ), + 92197373 => array( + 'phui-workcard-view-css', + ), '93d0c9e3' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1616,11 +1626,6 @@ return array( 'phabricator-phtize', 'changeset-view-manager', ), - '9e54692d' => array( - 'javelin-install', - 'javelin-dom', - 'javelin-vector', - ), '9f36c42d' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1659,9 +1664,6 @@ return array( 'javelin-install', 'javelin-dom', ), - 'a78c0661' => array( - 'phui-workcard-view-css', - ), 'a80d0378' => array( 'javelin-behavior', 'javelin-stratcom', diff --git a/resources/sql/autopatches/20160215.owners.policy.1.sql b/resources/sql/autopatches/20160215.owners.policy.1.sql new file mode 100644 index 0000000000..ae63906781 --- /dev/null +++ b/resources/sql/autopatches/20160215.owners.policy.1.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_owners.owners_package + ADD viewPolicy VARBINARY(64) NOT NULL; diff --git a/resources/sql/autopatches/20160215.owners.policy.2.sql b/resources/sql/autopatches/20160215.owners.policy.2.sql new file mode 100644 index 0000000000..f55b61a9ff --- /dev/null +++ b/resources/sql/autopatches/20160215.owners.policy.2.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_owners.owners_package + ADD editPolicy VARBINARY(64) NOT NULL; diff --git a/resources/sql/autopatches/20160215.owners.policy.3.sql b/resources/sql/autopatches/20160215.owners.policy.3.sql new file mode 100644 index 0000000000..9d3ae9f112 --- /dev/null +++ b/resources/sql/autopatches/20160215.owners.policy.3.sql @@ -0,0 +1,2 @@ +UPDATE {$NAMESPACE}_owners.owners_package + SET viewPolicy = 'users' WHERE viewPolicy = ''; diff --git a/resources/sql/autopatches/20160215.owners.policy.4.sql b/resources/sql/autopatches/20160215.owners.policy.4.sql new file mode 100644 index 0000000000..e108a6da9c --- /dev/null +++ b/resources/sql/autopatches/20160215.owners.policy.4.sql @@ -0,0 +1,2 @@ +UPDATE {$NAMESPACE}_owners.owners_package + SET editPolicy = 'users' WHERE editPolicy = ''; diff --git a/resources/sql/autopatches/20160218.callsigns.1.sql b/resources/sql/autopatches/20160218.callsigns.1.sql new file mode 100644 index 0000000000..09d1dd5a1b --- /dev/null +++ b/resources/sql/autopatches/20160218.callsigns.1.sql @@ -0,0 +1,4 @@ +/* Make callsigns nullable, and thus optional. */ + +ALTER TABLE {$NAMESPACE}_repository.repository + CHANGE callsign callsign VARCHAR(32) COLLATE {$COLLATE_SORT}; diff --git a/scripts/repository/commit_hook.php b/scripts/repository/commit_hook.php index 56e828ab7a..4f6997c52a 100755 --- a/scripts/repository/commit_hook.php +++ b/scripts/repository/commit_hook.php @@ -32,14 +32,14 @@ $root = dirname(dirname(dirname(__FILE__))); require_once $root.'/scripts/__init_script__.php'; if ($argc < 2) { - throw new Exception(pht('usage: commit-hook ')); + throw new Exception(pht('usage: commit-hook ')); } $engine = new DiffusionCommitHookEngine(); $repository = id(new PhabricatorRepositoryQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withCallsigns(array($argv[1])) + ->withIdentifiers(array($argv[1])) ->needProjectPHIDs(true) ->executeOne(); @@ -62,8 +62,9 @@ if ($repository->isGit() || $repository->isHg()) { if (!strlen($username)) { throw new Exception( pht( - 'Usage: %s should be defined!', - DiffusionCommitHookEngine::ENV_USER)); + 'No Direct Pushes: You are pushing directly to a repository hosted '. + 'by Phabricator. This will not work. See "No Direct Pushes" in the '. + 'documentation for more information.')); } if ($repository->isHg()) { @@ -77,7 +78,7 @@ if ($repository->isGit() || $repository->isHg()) { // specify the correct user; read this user out of the commit log. if ($argc < 4) { - throw new Exception(pht('usage: commit-hook ')); + throw new Exception(pht('usage: commit-hook ')); } $svn_repo = $argv[2]; diff --git a/scripts/symbols/clear_repository_symbols.php b/scripts/symbols/clear_repository_symbols.php index 8807262cd5..701034c6cc 100755 --- a/scripts/symbols/clear_repository_symbols.php +++ b/scripts/symbols/clear_repository_symbols.php @@ -6,7 +6,7 @@ require_once $root.'/scripts/__init_script__.php'; $args = new PhutilArgumentParser($argv); $args->setSynopsis(<<parseStandardArguments(); $args->parse( array( array( - 'name' => 'callsign', + 'name' => 'repository', 'wildcard' => true, ), )); -$callsigns = $args->getArg('callsign'); -if (count($callsigns) !== 1) { +$identifiers = $args->getArg('repository'); +if (count($identifiers) !== 1) { $args->printHelpAndExit(); } -$callsign = head($callsigns); +$identifier = head($identifiers); $repository = id(new PhabricatorRepositoryQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withCallsigns($callsigns) + ->withIdentifiers($identifiers) ->executeOne(); if (!$repository) { - echo pht("Repository '%s' does not exist.", $callsign); + echo tsprintf( + "%s\n", + pht('Repository "%s" does not exist.', $identifier)); exit(1); } diff --git a/scripts/symbols/import_repository_symbols.php b/scripts/symbols/import_repository_symbols.php index c8dabc8508..b84aea3485 100755 --- a/scripts/symbols/import_repository_symbols.php +++ b/scripts/symbols/import_repository_symbols.php @@ -6,7 +6,7 @@ require_once $root.'/scripts/__init_script__.php'; $args = new PhutilArgumentParser($argv); $args->setSynopsis(<<parse( 'be part of a single transaction.'), ), array( - 'name' => 'callsign', + 'name' => 'repository', 'wildcard' => true, ), )); -$callsigns = $args->getArg('callsign'); -if (count($callsigns) !== 1) { +$identifiers = $args->getArg('repository'); +if (count($identifiers) !== 1) { $args->printHelpAndExit(); } -$callsign = head($callsigns); +$identifier = head($identifiers); $repository = id(new PhabricatorRepositoryQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withCallsigns($callsigns) + ->withIdentifiers($identifiers) ->executeOne(); if (!$repository) { - echo pht("Repository '%s' does not exist.", $callsign); + echo tsprintf( + "%s\n", + pht('Repository "%s" does not exist.', $identifier)); exit(1); } diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index aacf85ac45..ae699acf07 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -689,7 +689,7 @@ phutil_register_library_map(array( 'DiffusionPreCommitRefRepositoryHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefRepositoryHeraldField.php', 'DiffusionPreCommitRefRepositoryProjectsHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefRepositoryProjectsHeraldField.php', 'DiffusionPreCommitRefTypeHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefTypeHeraldField.php', - 'DiffusionPullEventGarbageCollector' => 'applications/diffusion/DiffusionPullEventGarbageCollector.php', + 'DiffusionPullEventGarbageCollector' => 'applications/diffusion/garbagecollector/DiffusionPullEventGarbageCollector.php', 'DiffusionPushCapability' => 'applications/diffusion/capability/DiffusionPushCapability.php', 'DiffusionPushEventViewController' => 'applications/diffusion/controller/DiffusionPushEventViewController.php', 'DiffusionPushLogController' => 'applications/diffusion/controller/DiffusionPushLogController.php', @@ -2665,6 +2665,8 @@ phutil_register_library_map(array( 'PhabricatorOwnersCustomFieldStorage' => 'applications/owners/storage/PhabricatorOwnersCustomFieldStorage.php', 'PhabricatorOwnersCustomFieldStringIndex' => 'applications/owners/storage/PhabricatorOwnersCustomFieldStringIndex.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', 'PhabricatorOwnersEditController' => 'applications/owners/controller/PhabricatorOwnersEditController.php', 'PhabricatorOwnersListController' => 'applications/owners/controller/PhabricatorOwnersListController.php', @@ -2867,9 +2869,11 @@ phutil_register_library_map(array( 'PhabricatorProjectAddHeraldAction' => 'applications/project/herald/PhabricatorProjectAddHeraldAction.php', 'PhabricatorProjectApplication' => 'applications/project/application/PhabricatorProjectApplication.php', 'PhabricatorProjectArchiveController' => 'applications/project/controller/PhabricatorProjectArchiveController.php', + 'PhabricatorProjectBoardBackgroundController' => 'applications/project/controller/PhabricatorProjectBoardBackgroundController.php', 'PhabricatorProjectBoardController' => 'applications/project/controller/PhabricatorProjectBoardController.php', 'PhabricatorProjectBoardDisableController' => 'applications/project/controller/PhabricatorProjectBoardDisableController.php', 'PhabricatorProjectBoardImportController' => 'applications/project/controller/PhabricatorProjectBoardImportController.php', + 'PhabricatorProjectBoardManageController' => 'applications/project/controller/PhabricatorProjectBoardManageController.php', 'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php', 'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php', 'PhabricatorProjectCardView' => 'applications/project/view/PhabricatorProjectCardView.php', @@ -2967,6 +2971,7 @@ phutil_register_library_map(array( 'PhabricatorProjectViewController' => 'applications/project/controller/PhabricatorProjectViewController.php', 'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php', 'PhabricatorProjectWatcherListView' => 'applications/project/view/PhabricatorProjectWatcherListView.php', + 'PhabricatorProjectWorkboardBackgroundColor' => 'applications/project/constants/PhabricatorProjectWorkboardBackgroundColor.php', 'PhabricatorProjectWorkboardProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectWorkboardProfilePanel.php', 'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php', 'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php', @@ -5706,7 +5711,7 @@ phutil_register_library_map(array( 'PHUIListView' => 'AphrontTagView', 'PHUIListViewTestCase' => 'PhabricatorTestCase', 'PHUIMainMenuView' => 'AphrontView', - 'PHUIObjectBoxView' => 'AphrontView', + 'PHUIObjectBoxView' => 'AphrontTagView', 'PHUIObjectItemListExample' => 'PhabricatorUIExample', 'PHUIObjectItemListView' => 'AphrontTagView', 'PHUIObjectItemView' => 'AphrontTagView', @@ -7036,6 +7041,8 @@ phutil_register_library_map(array( 'PhabricatorOwnersCustomFieldStorage' => 'PhabricatorCustomFieldStorage', 'PhabricatorOwnersCustomFieldStringIndex' => 'PhabricatorCustomFieldStringIndexStorage', 'PhabricatorOwnersDAO' => 'PhabricatorLiskDAO', + 'PhabricatorOwnersDefaultEditCapability' => 'PhabricatorPolicyCapability', + 'PhabricatorOwnersDefaultViewCapability' => 'PhabricatorPolicyCapability', 'PhabricatorOwnersDetailController' => 'PhabricatorOwnersController', 'PhabricatorOwnersEditController' => 'PhabricatorOwnersController', 'PhabricatorOwnersListController' => 'PhabricatorOwnersController', @@ -7293,9 +7300,11 @@ phutil_register_library_map(array( 'PhabricatorProjectAddHeraldAction' => 'PhabricatorProjectHeraldAction', 'PhabricatorProjectApplication' => 'PhabricatorApplication', 'PhabricatorProjectArchiveController' => 'PhabricatorProjectController', + 'PhabricatorProjectBoardBackgroundController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardController' => 'PhabricatorProjectController', 'PhabricatorProjectBoardDisableController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardImportController' => 'PhabricatorProjectBoardController', + 'PhabricatorProjectBoardManageController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectCardView' => 'AphrontTagView', @@ -7305,6 +7314,7 @@ phutil_register_library_map(array( 'PhabricatorApplicationTransactionInterface', 'PhabricatorPolicyInterface', 'PhabricatorDestructibleInterface', + 'PhabricatorExtendedPolicyInterface', ), 'PhabricatorProjectColumnDetailController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectColumnEditController' => 'PhabricatorProjectBoardController', @@ -7406,6 +7416,7 @@ phutil_register_library_map(array( 'PhabricatorProjectViewController' => 'PhabricatorProjectController', 'PhabricatorProjectWatchController' => 'PhabricatorProjectController', 'PhabricatorProjectWatcherListView' => 'PhabricatorProjectUserListView', + 'PhabricatorProjectWorkboardBackgroundColor' => 'Phobject', 'PhabricatorProjectWorkboardProfilePanel' => 'PhabricatorProfilePanel', 'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension', 'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField', diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php index f423860a3b..d494fa7a0b 100644 --- a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php +++ b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php @@ -363,11 +363,7 @@ final class PhabricatorCalendarEventViewController ->getIconLabel($event->getIcon())); if (strlen($event->getDescription())) { - $description = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($event->getDescription()), - 'default', - $viewer); - + $description = new PHUIRemarkupView($viewer, $event->getDescription()); $properties->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); diff --git a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php index a47d4a65e4..4fdeaeccb1 100644 --- a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php +++ b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php @@ -72,6 +72,12 @@ final class CelerityDefaultPostprocessor 'hoverselectedblue' => '#e6e9ee', '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 'lightgreyborder' => '#C7CCD9', 'greyborder' => '#A1A6B0', diff --git a/src/applications/conduit/query/PhabricatorConduitLogSearchEngine.php b/src/applications/conduit/query/PhabricatorConduitLogSearchEngine.php index f5d203c772..4c91ce7a8d 100644 --- a/src/applications/conduit/query/PhabricatorConduitLogSearchEngine.php +++ b/src/applications/conduit/query/PhabricatorConduitLogSearchEngine.php @@ -37,7 +37,7 @@ final class PhabricatorConduitLogSearchEngine return array( id(new PhabricatorUsersSearchField()) ->setKey('callerPHIDs') - ->setLabel(pht('Methods')) + ->setLabel(pht('Callers')) ->setAliases(array('caller', 'callers')) ->setDescription(pht('Find calls by specific users.')), id(new PhabricatorSearchStringListField()) diff --git a/src/applications/config/controller/PhabricatorConfigWelcomeController.php b/src/applications/config/controller/PhabricatorConfigWelcomeController.php index e2d868082a..e2d704e0b7 100644 --- a/src/applications/config/controller/PhabricatorConfigWelcomeController.php +++ b/src/applications/config/controller/PhabricatorConfigWelcomeController.php @@ -341,23 +341,14 @@ final class PhabricatorConfigWelcomeController $header = id(new PHUIHeaderView()) ->setHeader(pht('Welcome to Phabricator')); - $setup_header = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setContent(pht('=Setup and Configuration')), - 'default', - $viewer); + $setup_header = new PHUIRemarkupView( + $viewer, pht('=Setup and Configuration')); - $explore_header = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setContent(pht('=Explore Phabricator')), - 'default', - $viewer); + $explore_header = new PHUIRemarkupView( + $viewer, pht('=Explore Phabricator')); - $quick_header = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setContent(pht('=Quick Start Guides')), - 'default', - $viewer); + $quick_header = new PHUIRemarkupView( + $viewer, pht('=Quick Start Guide')); return id(new PHUIDocumentView()) ->setHeader($header) @@ -376,10 +367,7 @@ final class PhabricatorConfigWelcomeController $icon = id(new PHUIIconView()) ->setIcon($icon.' fa-2x'); - $content = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($content), - 'default', - $viewer); + $content = new PHUIRemarkupView($viewer, $content); $icon = phutil_tag( 'div', diff --git a/src/applications/countdown/controller/PhabricatorCountdownViewController.php b/src/applications/countdown/controller/PhabricatorCountdownViewController.php index 9cd0d982ff..401e159c5f 100644 --- a/src/applications/countdown/controller/PhabricatorCountdownViewController.php +++ b/src/applications/countdown/controller/PhabricatorCountdownViewController.php @@ -132,11 +132,7 @@ final class PhabricatorCountdownViewController $description = $countdown->getDescription(); if (strlen($description)) { - $description = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($description), - 'default', - $viewer); - + $description = new PHUIRemarkupView($viewer, $description); $view->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); diff --git a/src/applications/differential/customfield/DifferentialRevertPlanField.php b/src/applications/differential/customfield/DifferentialRevertPlanField.php index 1d86a794a4..646116c74e 100644 --- a/src/applications/differential/customfield/DifferentialRevertPlanField.php +++ b/src/applications/differential/customfield/DifferentialRevertPlanField.php @@ -44,12 +44,7 @@ final class DifferentialRevertPlanField return null; } - return PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setPreserveLinebreaks(true) - ->setContent($this->getValue()), - 'default', - $this->getViewer()); + return new PHUIRemarkupView($this->getViewer(), $this->getValue()); } public function shouldAppearInGlobalSearch() { diff --git a/src/applications/differential/customfield/DifferentialSummaryField.php b/src/applications/differential/customfield/DifferentialSummaryField.php index 16a3f701c1..5052e238e0 100644 --- a/src/applications/differential/customfield/DifferentialSummaryField.php +++ b/src/applications/differential/customfield/DifferentialSummaryField.php @@ -122,12 +122,7 @@ final class DifferentialSummaryField return null; } - return PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setPreserveLinebreaks(true) - ->setContent($this->getValue()), - 'default', - $this->getViewer()); + return new PHUIRemarkupView($this->getViewer(), $this->getValue()); } public function getApplicationTransactionRemarkupBlocks( diff --git a/src/applications/differential/customfield/DifferentialTestPlanField.php b/src/applications/differential/customfield/DifferentialTestPlanField.php index 32d3c15c31..6a1029dba4 100644 --- a/src/applications/differential/customfield/DifferentialTestPlanField.php +++ b/src/applications/differential/customfield/DifferentialTestPlanField.php @@ -136,12 +136,7 @@ final class DifferentialTestPlanField return null; } - return PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setPreserveLinebreaks(true) - ->setContent($this->getValue()), - 'default', - $this->getViewer()); + return new PHUIRemarkupView($this->getViewer(), $this->getValue()); } public function getApplicationTransactionRemarkupBlocks( diff --git a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php index fa82b4e80f..b8766920fc 100644 --- a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php +++ b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php @@ -64,7 +64,11 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication { '(?:query/(?P[^/]+)/)?' => 'DiffusionPushLogListController', 'view/(?P\d+)/' => 'DiffusionPushEventViewController', ), - '(?P[A-Z]+)/' => array( + '(?:'. + '(?P[A-Z]+)'. + '|'. + '(?P[1-9]\d*)'. + ')/' => array( '' => 'DiffusionRepositoryController', 'repository/(?P.*)' => 'DiffusionRepositoryController', @@ -115,8 +119,9 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication { // catch-all for serving repositories over HTTP. We must accept // requests without the trailing "/" because SVN commands don't // necessarily include it. - '(?P[A-Z]+)(?:/.*)?' => - 'DiffusionRepositoryDefaultController', + '(?:(?P[A-Z]+)|(?P[1-9]\d*))'. + '(?:/.*)?' + => 'DiffusionRepositoryDefaultController', 'inline/' => array( 'edit/(?P[^/]+)/' => 'DiffusionInlineCommentController', diff --git a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php index f32583708c..29059c283f 100644 --- a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php @@ -246,7 +246,16 @@ final class DiffusionBrowseQueryConduitAPIMethod DiffusionBrowseResultSet::REASON_IS_FILE); return $result; } + $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) { $type = DifferentialChangeType::FILE_NORMAL; } else { @@ -254,7 +263,7 @@ final class DiffusionBrowseQueryConduitAPIMethod } if ($count >= $offset) { - $results[reset($parts)] = $type; + $results[$name] = $type; } $count++; diff --git a/src/applications/diffusion/controller/DiffusionBranchTableController.php b/src/applications/diffusion/controller/DiffusionBranchTableController.php index a040c8084e..3a025e90c5 100644 --- a/src/applications/diffusion/controller/DiffusionBranchTableController.php +++ b/src/applications/diffusion/controller/DiffusionBranchTableController.php @@ -19,13 +19,19 @@ final class DiffusionBranchTableController extends DiffusionController { $pager = id(new PHUIPagerView()) ->readFromRequest($request); - // TODO: Add support for branches that contain commit + $params = array( + 'offset' => $pager->getOffset(), + 'limit' => $pager->getPageSize() + 1, + ); + + $contains = $drequest->getSymbolicCommit(); + if (strlen($contains)) { + $params['contains'] = $contains; + } + $branches = $this->callConduitWithDiffusionRequest( 'diffusion.branchquery', - array( - 'offset' => $pager->getOffset(), - 'limit' => $pager->getPageSize() + 1, - )); + $params); $branches = $pager->sliceResults($branches); $branches = DiffusionRepositoryRef::loadAllFromDictionaries($branches); diff --git a/src/applications/diffusion/controller/DiffusionCommitBranchesController.php b/src/applications/diffusion/controller/DiffusionCommitBranchesController.php index 1e5cc0ac31..e698ff6fc7 100644 --- a/src/applications/diffusion/controller/DiffusionCommitBranchesController.php +++ b/src/applications/diffusion/controller/DiffusionCommitBranchesController.php @@ -15,20 +15,18 @@ final class DiffusionCommitBranchesController extends DiffusionController { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); - switch ($repository->getVersionControlSystem()) { - case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: - $branches = array(); - break; - default: - $branches = $this->callConduitWithDiffusionRequest( - 'diffusion.branchquery', - array( - 'contains' => $drequest->getCommit(), - )); - break; - } + $branch_limit = 10; + $branches = DiffusionRepositoryRef::loadAllFromDictionaries( + $this->callConduitWithDiffusionRequest( + 'diffusion.branchquery', + array( + 'contains' => $drequest->getCommit(), + 'limit' => $branch_limit + 1, + ))); + + $has_more_branches = (count($branches) > $branch_limit); + $branches = array_slice($branches, 0, $branch_limit); - $branches = DiffusionRepositoryRef::loadAllFromDictionaries($branches); $branch_links = array(); foreach ($branches as $branch) { $branch_links[] = phutil_tag( @@ -43,6 +41,18 @@ final class DiffusionCommitBranchesController extends DiffusionController { $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()) ->setContent($branch_links ? implode(', ', $branch_links) : pht('None')); } diff --git a/src/applications/diffusion/controller/DiffusionCommitTagsController.php b/src/applications/diffusion/controller/DiffusionCommitTagsController.php index 0f825a9b64..aef73dd842 100644 --- a/src/applications/diffusion/controller/DiffusionCommitTagsController.php +++ b/src/applications/diffusion/controller/DiffusionCommitTagsController.php @@ -16,20 +16,14 @@ final class DiffusionCommitTagsController extends DiffusionController { $repository = $drequest->getRepository(); $tag_limit = 10; - switch ($repository->getVersionControlSystem()) { - case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: - $tags = array(); - break; - default: - $tags = DiffusionRepositoryTag::newFromConduit( - $this->callConduitWithDiffusionRequest( - 'diffusion.tagsquery', - array( - 'commit' => $drequest->getCommit(), - 'limit' => $tag_limit + 1, - ))); - break; - } + $tags = DiffusionRepositoryTag::newFromConduit( + $this->callConduitWithDiffusionRequest( + 'diffusion.tagsquery', + array( + 'commit' => $drequest->getCommit(), + 'limit' => $tag_limit + 1, + ))); + $has_more_tags = (count($tags) > $tag_limit); $tags = array_slice($tags, 0, $tag_limit); diff --git a/src/applications/diffusion/controller/DiffusionController.php b/src/applications/diffusion/controller/DiffusionController.php index 00bb0ce4d4..c83990cc15 100644 --- a/src/applications/diffusion/controller/DiffusionController.php +++ b/src/applications/diffusion/controller/DiffusionController.php @@ -64,6 +64,20 @@ abstract class DiffusionController extends PhabricatorController { 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; return null; diff --git a/src/applications/diffusion/controller/DiffusionRepositoryController.php b/src/applications/diffusion/controller/DiffusionRepositoryController.php index 18989818b6..6504ee50e1 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryController.php @@ -290,10 +290,7 @@ final class DiffusionRepositoryController extends DiffusionController { $description = $repository->getDetail('description'); if (strlen($description)) { - $description = PhabricatorMarkupEngine::renderOneObject( - $repository, - 'description', - $user); + $description = new PHUIRemarkupView($user, $description); $view->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); $view->addTextContent($description); diff --git a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php b/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php index c6e2796fc1..1333bf67f9 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php @@ -134,7 +134,6 @@ final class DiffusionRepositoryCreateController $type_name = PhabricatorRepositoryTransaction::TYPE_NAME; $type_vcs = PhabricatorRepositoryTransaction::TYPE_VCS; $type_activate = PhabricatorRepositoryTransaction::TYPE_ACTIVATE; - $type_local_path = PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH; $type_remote_uri = PhabricatorRepositoryTransaction::TYPE_REMOTE_URI; $type_hosting = PhabricatorRepositoryTransaction::TYPE_HOSTING; $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 ($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) ->setTransactionType($type_name) ->setNewValue( @@ -179,16 +170,6 @@ final class DiffusionRepositoryCreateController ->setTransactionType($type_service) ->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) { @@ -354,7 +335,7 @@ final class DiffusionRepositoryCreateController } -/* -( Page: Name and Callsign )-------------------------------------------- */ +/* -( Page: Name )--------------------------------------------------------- */ private function buildNamePage() { @@ -370,23 +351,7 @@ final class DiffusionRepositoryCreateController ->addControl( id(new AphrontFormTextControl()) ->setName('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.'))); + ->setLabel(pht('Name'))); } public function validateNamePage(PHUIFormPageView $page) { @@ -398,38 +363,7 @@ final class DiffusionRepositoryCreateController pht('You must choose a name for this repository.')); } - $c_call = $page->getControl('callsign'); - $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(); + return $c_name->isValid(); } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php index 65f7dd0974..e23a57e655 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php @@ -18,11 +18,13 @@ final class DiffusionRepositoryEditBasicController $v_name = $repository->getName(); $v_desc = $repository->getDetail('description'); $v_slug = $repository->getRepositorySlug(); + $v_callsign = $repository->getCallsign(); $v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs( $repository->getPHID(), PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); $e_name = true; $e_slug = null; + $e_callsign = null; $errors = array(); $validation_exception = null; @@ -31,6 +33,7 @@ final class DiffusionRepositoryEditBasicController $v_desc = $request->getStr('description'); $v_projects = $request->getArr('projectPHIDs'); $v_slug = $request->getStr('slug'); + $v_callsign = $request->getStr('callsign'); if (!strlen($v_name)) { $e_name = pht('Required'); @@ -47,6 +50,7 @@ final class DiffusionRepositoryEditBasicController $type_desc = PhabricatorRepositoryTransaction::TYPE_DESCRIPTION; $type_edge = PhabricatorTransactions::TYPE_EDGE; $type_slug = PhabricatorRepositoryTransaction::TYPE_SLUG; + $type_callsign = PhabricatorRepositoryTransaction::TYPE_CALLSIGN; $xactions[] = id(clone $template) ->setTransactionType($type_name) @@ -60,6 +64,10 @@ final class DiffusionRepositoryEditBasicController ->setTransactionType($type_slug) ->setNewValue($v_slug); + $xactions[] = id(clone $template) + ->setTransactionType($type_callsign) + ->setNewValue($v_callsign); + $xactions[] = id(clone $template) ->setTransactionType($type_edge) ->setMetadataValue( @@ -78,11 +86,16 @@ final class DiffusionRepositoryEditBasicController try { $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); } catch (PhabricatorApplicationTransactionValidationException $ex) { $validation_exception = $ex; $e_slug = $ex->getShortMessage($type_slug); + $e_callsign = $ex->getShortMessage($type_callsign); } } } @@ -106,6 +119,12 @@ final class DiffusionRepositoryEditBasicController ->setLabel(pht('Short Name')) ->setValue($v_slug) ->setError($e_slug)) + ->appendChild( + id(new AphrontFormTextControl()) + ->setName('callsign') + ->setLabel(pht('Callsign')) + ->setValue($v_callsign) + ->setError($e_callsign)) ->appendChild( id(new PhabricatorRemarkupControl()) ->setUser($viewer) diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php index 886809dedd..0ec8cdd6b8 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php @@ -284,7 +284,12 @@ final class DiffusionRepositoryEditMainController $repository->getVersionControlSystem()); $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(); if ($short_name === null) { @@ -309,10 +314,7 @@ final class DiffusionRepositoryEditMainController if (!strlen($description)) { $description = phutil_tag('em', array(), pht('No description provided.')); } else { - $description = PhabricatorMarkupEngine::renderOneObject( - $repository, - 'description', - $viewer); + $description = new PHUIRemarkupView($viewer, $description); } $view->addTextContent($description); diff --git a/src/applications/diffusion/controller/DiffusionServeController.php b/src/applications/diffusion/controller/DiffusionServeController.php index 9de692d620..84010d76af 100644 --- a/src/applications/diffusion/controller/DiffusionServeController.php +++ b/src/applications/diffusion/controller/DiffusionServeController.php @@ -262,17 +262,23 @@ final class DiffusionServeController extends DiffusionController { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $result = new PhabricatorVCSResponse( 500, - pht('This is not a Git repository.')); + pht( + 'This repository ("%s") is not a Git repository.', + $repository->getDisplayName())); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $result = new PhabricatorVCSResponse( 500, - pht('This is not a Mercurial repository.')); + pht( + 'This repository ("%s") is not a Mercurial repository.', + $repository->getDisplayName())); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $result = new PhabricatorVCSResponse( 500, - pht('This is not a Subversion repository.')); + pht( + 'This repository ("%s") is not a Subversion repository.', + $repository->getDisplayName())); break; default: $result = new PhabricatorVCSResponse( @@ -480,7 +486,9 @@ final class DiffusionServeController extends DiffusionController { private function getRequestDirectoryPath(PhabricatorRepository $repository) { $request = $this->getRequest(); $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 // isn't the name of a known Git resource. This allows users to clone diff --git a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php index 741b21bd19..53dc417be4 100644 --- a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php +++ b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php @@ -10,6 +10,7 @@ */ final class DiffusionCommitHookEngine extends Phobject { + const ENV_REPOSITORY = 'PHABRICATOR_REPOSITORY'; const ENV_USER = 'PHABRICATOR_USER'; const ENV_REMOTE_ADDRESS = 'PHABRICATOR_REMOTE_ADDRESS'; const ENV_REMOTE_PROTOCOL = 'PHABRICATOR_REMOTE_PROTOCOL'; @@ -610,7 +611,7 @@ final class DiffusionCommitHookEngine extends Phobject { $console = PhutilConsole::getConsole(); $env = array( - 'PHABRICATOR_REPOSITORY' => $this->getRepository()->getCallsign(), + self::ENV_REPOSITORY => $this->getRepository()->getPHID(), self::ENV_USER => $this->getViewer()->getUsername(), self::ENV_REMOTE_PROTOCOL => $this->getRemoteProtocol(), self::ENV_REMOTE_ADDRESS => $this->getRemoteAddress(), diff --git a/src/applications/diffusion/DiffusionPullEventGarbageCollector.php b/src/applications/diffusion/garbagecollector/DiffusionPullEventGarbageCollector.php similarity index 100% rename from src/applications/diffusion/DiffusionPullEventGarbageCollector.php rename to src/applications/diffusion/garbagecollector/DiffusionPullEventGarbageCollector.php diff --git a/src/applications/diffusion/request/DiffusionRequest.php b/src/applications/diffusion/request/DiffusionRequest.php index 8d326b81bc..f86170f129 100644 --- a/src/applications/diffusion/request/DiffusionRequest.php +++ b/src/applications/diffusion/request/DiffusionRequest.php @@ -243,10 +243,6 @@ abstract class DiffusionRequest extends Phobject { return $this->repository; } - public function getCallsign() { - return $this->getRepository()->getCallsign(); - } - public function setPath($path) { $this->path = $path; return $this; diff --git a/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php b/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php index 2b2c820773..15333ff360 100644 --- a/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php +++ b/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php @@ -6,6 +6,7 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow { private $repository; private $hasWriteAccess; private $proxyURI; + private $baseRequestPath; public function getRepository() { if (!$this->repository) { @@ -45,6 +46,10 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow { abstract protected function identifyRepository(); abstract protected function executeRepositoryOperations(); + protected function getBaseRequestPath() { + return $this->baseRequestPath; + } + protected function writeError($message) { $this->getErrorChannel()->write($message); return $this; @@ -149,25 +154,29 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow { protected function loadRepositoryWithPath($path) { $viewer = $this->getUser(); - $regex = '@^/?diffusion/(?P[A-Z]+)(?:/|\z)@'; - $matches = null; - if (!preg_match($regex, $path, $matches)) { + $info = PhabricatorRepository::parseRepositoryServicePath($path); + if ($info === null) { throw new Exception( pht( - 'Unrecognized repository path "%s". Expected a path like "%s".', + 'Unrecognized repository path "%s". Expected a path like "%s" '. + 'or "%s".', $path, - '/diffusion/X/')); + '/diffusion/X/', + '/diffusion/123/')); } - $callsign = $matches[1]; + $identifier = $info['identifier']; + $base = $info['base']; + + $this->baseRequestPath = $base; + $repository = id(new PhabricatorRepositoryQuery()) ->setViewer($viewer) - ->withCallsigns(array($callsign)) + ->withIdentifiers(array($identifier)) ->executeOne(); - if (!$repository) { throw new Exception( - pht('No repository "%s" exists!', $callsign)); + pht('No repository "%s" exists!', $identifier)); } switch ($repository->getServeOverSSH()) { @@ -179,7 +188,9 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow { case PhabricatorRepository::SERVE_OFF: default: throw new Exception( - pht('This repository is not available over SSH.')); + pht( + 'This repository ("%s") is not available over SSH.', + $repository->getDisplayName())); } return $repository; diff --git a/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php b/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php index 257e6e9adb..4e591d318b 100644 --- a/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php +++ b/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php @@ -377,14 +377,12 @@ final class DiffusionSubversionServeSSHWorkflow $repository = $this->getRepository(); $path = $this->getPathFromSubversionURI($uri_string); - $path = preg_replace( - '(^/diffusion/[A-Z]+)', - rtrim($repository->getLocalPath(), '/'), - $path); + $external_base = $this->getBaseRequestPath(); - if (preg_match('(^/diffusion/[A-Z]+/\z)', $path)) { - $path = rtrim($path, '/'); - } + // Replace "/diffusion/X" in the request with the repository local 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 // URI. Subversion retains it over the course of the request and considers @@ -398,7 +396,7 @@ final class DiffusionSubversionServeSSHWorkflow if ($this->externalBaseURI === null) { $pre = (string)id(clone $uri)->setPath(''); - $external_path = '/diffusion/'.$repository->getCallsign(); + $external_path = $external_base; $external_path = $this->normalizeSVNPath($external_path); $this->externalBaseURI = $pre.$external_path; diff --git a/src/applications/diffusion/view/DiffusionReadmeView.php b/src/applications/diffusion/view/DiffusionReadmeView.php index 4f4e70ed0d..16924ddd98 100644 --- a/src/applications/diffusion/view/DiffusionReadmeView.php +++ b/src/applications/diffusion/view/DiffusionReadmeView.php @@ -109,11 +109,14 @@ final class DiffusionReadmeView extends DiffusionView { $header = id(new PHUIHeaderView()) ->setHeader($readme_name); - return id(new PHUIDocumentView()) + $document = id(new PHUIDocumentViewPro()) ->setFluid(true) - ->appendChild($readme_content) - ->addClass('diffusion-readme-view') - ->setHeader($header); + ->appendChild($readme_content); + + return id(new PHUIObjectBoxView()) + ->setHeader($header) + ->appendChild($document) + ->addClass('diffusion-readme-view'); } } diff --git a/src/applications/diviner/controller/DivinerBookController.php b/src/applications/diviner/controller/DivinerBookController.php index 0c84955b2f..74b2a8f3d9 100644 --- a/src/applications/diviner/controller/DivinerBookController.php +++ b/src/applications/diviner/controller/DivinerBookController.php @@ -29,20 +29,12 @@ final class DivinerBookController extends DivinerController { $book->getShortTitle(), '/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()) ->setHeader($book->getTitle()) ->setUser($viewer) ->setPolicyObject($book) ->setEpoch($book->getDateModified()) - ->addActionLink($action_button); + ->setActionList($actions); // TODO: This could probably look better. if ($book->getRepositoryPHID()) { @@ -94,11 +86,7 @@ final class DivinerBookController extends DivinerController { $preface = $book->getPreface(); $preface_view = null; if (strlen($preface)) { - $preface_view = - PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($preface), - 'default', - $viewer); + $preface_view = new PHUIRemarkupView($viewer, $preface); } $document->appendChild($preface_view); diff --git a/src/applications/diviner/controller/DivinerMainController.php b/src/applications/diviner/controller/DivinerMainController.php index 97149778c1..e85769060f 100644 --- a/src/applications/diviner/controller/DivinerMainController.php +++ b/src/applications/diviner/controller/DivinerMainController.php @@ -61,11 +61,7 @@ final class DivinerMainController extends DivinerController { " %s\n\n", 'phabricator/ $ ./bin/diviner generate'); - $text = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($text), - 'default', - $viewer); - + $text = new PHUIRemarkupView($viewer, $text); $document->appendChild($text); } diff --git a/src/applications/diviner/workflow/DivinerGenerateWorkflow.php b/src/applications/diviner/workflow/DivinerGenerateWorkflow.php index ad671bbbea..86b75c8480 100644 --- a/src/applications/diviner/workflow/DivinerGenerateWorkflow.php +++ b/src/applications/diviner/workflow/DivinerGenerateWorkflow.php @@ -27,7 +27,7 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow { ), array( 'name' => 'repository', - 'param' => 'callsign', + 'param' => 'identifier', 'help' => pht('Repository that the documentation belongs to.'), ), )); @@ -192,19 +192,19 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow { } $publisher = newv($publisher_class, array()); - $callsign = $args->getArg('repository'); + $identifier = $args->getArg('repository'); $repository = null; - if ($callsign) { + if (strlen($identifier)) { $repository = id(new PhabricatorRepositoryQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withCallsigns(array($callsign)) + ->withIdentifiers(array($identifier)) ->executeOne(); if (!$repository) { throw new PhutilArgumentUsageException( pht( - "Repository '%s' does not exist.", - $callsign)); + 'Repository "%s" does not exist.', + $identifier)); } $publisher->setRepositoryPHID($repository->getPHID()); diff --git a/src/applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php b/src/applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php index 991392f484..1771a6615e 100644 --- a/src/applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php +++ b/src/applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php @@ -111,10 +111,7 @@ final class PhabricatorAsanaConfigOptions "The Asana Workspaces your linked account has access to are:\n\n%s", $out); - return PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($out), - 'default', - $viewer); + return new PHUIRemarkupView($viewer, $out); } private function renderContextualProjectDescription( @@ -155,10 +152,7 @@ final class PhabricatorAsanaConfigOptions $out = implode("\n", $out); - return PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($out), - 'default', - $viewer); + return new PHUIRemarkupView($viewer, $out); } } diff --git a/src/applications/fund/controller/FundInitiativeViewController.php b/src/applications/fund/controller/FundInitiativeViewController.php index 71cebc842d..6f780e4e72 100644 --- a/src/applications/fund/controller/FundInitiativeViewController.php +++ b/src/applications/fund/controller/FundInitiativeViewController.php @@ -98,11 +98,7 @@ final class FundInitiativeViewController $description = $initiative->getDescription(); if (strlen($description)) { - $description = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($description), - 'default', - $viewer); - + $description = new PHUIRemarkupView($viewer, $description); $view->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); $view->addTextContent($description); @@ -110,11 +106,7 @@ final class FundInitiativeViewController $risks = $initiative->getRisks(); if (strlen($risks)) { - $risks = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($risks), - 'default', - $viewer); - + $risks = new PHUIRemarkupView($viewer, $risks); $view->addSectionHeader( pht('Risks/Challenges'), 'fa-ambulance'); $view->addTextContent($risks); diff --git a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php index d197174175..a9cbb43ccf 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php @@ -181,16 +181,10 @@ final class HarbormasterBuildViewController if ($step) { $description = $step->getDescription(); if ($description) { - $rendered = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setContent($description) - ->setPreserveLinebreaks(true), - 'default', - $viewer); - + $description = new PHUIRemarkupView($viewer, $description); $properties->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); - $properties->addTextContent($rendered); + $properties->addTextContent($description); } } else { $target_box->setFormErrors( diff --git a/src/applications/legalpad/controller/LegalpadDocumentSignController.php b/src/applications/legalpad/controller/LegalpadDocumentSignController.php index 2221ae4499..00549c1adc 100644 --- a/src/applications/legalpad/controller/LegalpadDocumentSignController.php +++ b/src/applications/legalpad/controller/LegalpadDocumentSignController.php @@ -258,11 +258,7 @@ final class LegalpadDocumentSignController extends LegalpadController { $preamble_box = null; if (strlen($document->getPreamble())) { - $preamble_text = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent( - $document->getPreamble()), - 'default', - $viewer); + $preamble_text = new PHUIRemarkupView($viewer, $document->getPreamble()); // NOTE: We're avoiding `setObject()` here so we don't pick up extra UI // elements like "Subscribers". This information is available on the diff --git a/src/applications/macro/application/PhabricatorMacroApplication.php b/src/applications/macro/application/PhabricatorMacroApplication.php index fc7dcabab6..3519a3f520 100644 --- a/src/applications/macro/application/PhabricatorMacroApplication.php +++ b/src/applications/macro/application/PhabricatorMacroApplication.php @@ -42,13 +42,6 @@ final class PhabricatorMacroApplication extends PhabricatorApplication { ); } - public function getRemarkupRules() { - return array( - new PhabricatorIconRemarkupRule(), - new PhabricatorEmojiRemarkupRule(), - ); - } - protected function getCustomCapabilities() { return array( PhabricatorMacroManageCapability::CAPABILITY => array( diff --git a/src/applications/maniphest/editor/ManiphestTransactionEditor.php b/src/applications/maniphest/editor/ManiphestTransactionEditor.php index 08cfc66632..97c567c30d 100644 --- a/src/applications/maniphest/editor/ManiphestTransactionEditor.php +++ b/src/applications/maniphest/editor/ManiphestTransactionEditor.php @@ -289,8 +289,14 @@ final class ManiphestTransactionEditor array($select_phids)) ->execute(); - $object_phids = mpull($board_tasks, 'getPHID'); - $object_phids[] = $object_phid; + $board_tasks = mpull($board_tasks, null, 'getPHID'); + $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()) ->setViewer($omnipotent_viewer) diff --git a/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php b/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php index 0ef4b83abd..19aad0e6a5 100644 --- a/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php +++ b/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php @@ -104,14 +104,11 @@ final class PhabricatorApplicationDetailViewController } $overview = $application->getOverview(); - if ($overview) { + if (strlen($overview)) { + $overview = new PHUIRemarkupView($viewer, $overview); $properties->addSectionHeader( pht('Overview'), PHUIPropertyListView::ICON_SUMMARY); - $properties->addTextContent( - PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($overview), - 'default', - $viewer)); + $properties->addTextContent($overview); } $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions( diff --git a/src/applications/meta/controller/PhabricatorApplicationEmailCommandsController.php b/src/applications/meta/controller/PhabricatorApplicationEmailCommandsController.php index 060ca951ba..08f8e450b5 100644 --- a/src/applications/meta/controller/PhabricatorApplicationEmailCommandsController.php +++ b/src/applications/meta/controller/PhabricatorApplicationEmailCommandsController.php @@ -117,10 +117,7 @@ final class PhabricatorApplicationEmailCommandsController $crumbs->addTextCrumb($title); $crumbs->setBorder(true); - $content_box = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($content), - 'default', - $viewer); + $content_box = new PHUIRemarkupView($viewer, $content); $info_view = null; if (!PhabricatorEnv::getEnvConfig('metamta.reply-handler-domain')) { diff --git a/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php b/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php index 675b2634a9..cca5a19cd9 100644 --- a/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php +++ b/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php @@ -120,11 +120,7 @@ final class NuancePhabricatorFormSourceDefinition PHUIPropertyListView $view) { $complaint = $item->getNuanceProperty('complaint'); - $complaint = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($complaint), - 'default', - $viewer); - + $complaint = new PHUIRemarkupView($viewer, $complaint); $view->addSectionHeader( pht('Complaint'), 'fa-exclamation-circle'); $view->addTextContent($complaint); diff --git a/src/applications/owners/application/PhabricatorOwnersApplication.php b/src/applications/owners/application/PhabricatorOwnersApplication.php index 574dd6ff7e..4b4841390a 100644 --- a/src/applications/owners/application/PhabricatorOwnersApplication.php +++ b/src/applications/owners/application/PhabricatorOwnersApplication.php @@ -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, + ), + ); + } + } diff --git a/src/applications/owners/capability/PhabricatorOwnersDefaultEditCapability.php b/src/applications/owners/capability/PhabricatorOwnersDefaultEditCapability.php new file mode 100644 index 0000000000..9b89bfb83a --- /dev/null +++ b/src/applications/owners/capability/PhabricatorOwnersDefaultEditCapability.php @@ -0,0 +1,12 @@ +getDescription(); if (strlen($description)) { + $description = new PHUIRemarkupView($viewer, $description); $view->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); - $view->addTextContent( - $output = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($description), - 'default', - $viewer)); + $view->addTextContent($description); } $view->invokeWillRenderEvent(); @@ -211,8 +208,10 @@ final class PhabricatorOwnersDetailController private function buildPackageActionView(PhabricatorOwnersPackage $package) { $viewer = $this->getViewer(); - // TODO: Implement this capability. - $can_edit = true; + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $package, + PhabricatorPolicyCapability::CAN_EDIT); $id = $package->getID(); $edit_uri = $this->getApplicationURI("/edit/{$id}/"); diff --git a/src/applications/owners/controller/PhabricatorOwnersPathsController.php b/src/applications/owners/controller/PhabricatorOwnersPathsController.php index b02f5437be..ccca55b6c5 100644 --- a/src/applications/owners/controller/PhabricatorOwnersPathsController.php +++ b/src/applications/owners/controller/PhabricatorOwnersPathsController.php @@ -12,8 +12,7 @@ final class PhabricatorOwnersPathsController ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, - // TODO: Support this capability. - // PhabricatorPolicyCapability::CAN_EDIT, + PhabricatorPolicyCapability::CAN_EDIT, )) ->needPaths(true) ->executeOne(); diff --git a/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php b/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php index f2cfc5151a..aa5ae1c6f3 100644 --- a/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php +++ b/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php @@ -21,6 +21,9 @@ final class PhabricatorOwnersPackageTransactionEditor $types[] = PhabricatorOwnersPackageTransaction::TYPE_PATHS; $types[] = PhabricatorOwnersPackageTransaction::TYPE_STATUS; + $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; + $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; + return $types; } diff --git a/src/applications/owners/storage/PhabricatorOwnersPackage.php b/src/applications/owners/storage/PhabricatorOwnersPackage.php index 23d540375d..e007b18c00 100644 --- a/src/applications/owners/storage/PhabricatorOwnersPackage.php +++ b/src/applications/owners/storage/PhabricatorOwnersPackage.php @@ -18,6 +18,8 @@ final class PhabricatorOwnersPackage protected $primaryOwnerPHID; protected $mailKey; protected $status; + protected $viewPolicy; + protected $editPolicy; private $paths = self::ATTACHABLE; private $owners = self::ATTACHABLE; @@ -27,8 +29,20 @@ final class PhabricatorOwnersPackage const STATUS_ARCHIVED = 'archived'; 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()) ->setAuditingEnabled(0) + ->setViewPolicy($view_policy) + ->setEditPolicy($edit_policy) ->attachPaths(array()) ->setStatus(self::STATUS_ACTIVE) ->attachOwners(array()) @@ -287,8 +301,12 @@ final class PhabricatorOwnersPackage } public function getPolicy($capability) { - // TODO: Implement proper policies. - return PhabricatorPolicies::POLICY_USER; + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return $this->getViewPolicy(); + case PhabricatorPolicyCapability::CAN_EDIT: + return $this->getEditPolicy(); + } } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { diff --git a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php index ed2a2189a6..ce15aa2b38 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php @@ -56,9 +56,11 @@ final class PhabricatorPeopleProfileViewController $projects = $this->buildProjectsView($user); $badges = $this->buildBadgesView($user); + require_celerity_resource('project-view-css'); - $columns = id(new PHUITwoColumnView()) - ->addClass('project-view-badges') + $home = id(new PHUITwoColumnView()) + ->setHeader($header) + ->addClass('project-view-home') ->setMainColumn( array( $properties, @@ -76,17 +78,6 @@ final class PhabricatorPeopleProfileViewController $crumbs = $this->buildApplicationCrumbs(); $crumbs->setBorder(true); - require_celerity_resource('project-view-css'); - $home = phutil_tag( - 'div', - array( - 'class' => 'project-view-home', - ), - array( - $header, - $columns, - )); - return $this->newPage() ->setTitle($user->getUsername()) ->setNavigation($nav) @@ -115,7 +106,7 @@ final class PhabricatorPeopleProfileViewController } $view = id(new PHUIBoxView()) - ->setColor(PHUIBoxView::GREY) + ->setBorder(true) ->appendChild($view) ->addClass('project-view-properties'); @@ -174,7 +165,7 @@ final class PhabricatorPeopleProfileViewController $box = id(new PHUIObjectBoxView()) ->setHeader($header) ->appendChild($list) - ->setBackground(PHUIBoxView::GREY); + ->setBackground(PHUIObjectBoxView::GREY); return $box; } @@ -217,8 +208,9 @@ final class PhabricatorPeopleProfileViewController $box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Badges')) + ->addClass('project-view-badges') ->appendChild($flex) - ->setBackground(PHUIBoxView::GREY); + ->setBackground(PHUIObjectBoxView::GREY); return $box; } diff --git a/src/applications/phame/controller/blog/PhameBlogManageController.php b/src/applications/phame/controller/blog/PhameBlogManageController.php index 0402245d41..14caafaab4 100644 --- a/src/applications/phame/controller/blog/PhameBlogManageController.php +++ b/src/applications/phame/controller/blog/PhameBlogManageController.php @@ -118,10 +118,7 @@ final class PhameBlogManageController extends PhameBlogController { $properties->invokeWillRenderEvent(); if (strlen($blog->getDescription())) { - $description = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($blog->getDescription()), - 'default', - $viewer); + $description = new PHUIRemarkupView($viewer, $description); $properties->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); diff --git a/src/applications/phame/controller/blog/PhameBlogViewController.php b/src/applications/phame/controller/blog/PhameBlogViewController.php index 60972c57b8..ea0822b856 100644 --- a/src/applications/phame/controller/blog/PhameBlogViewController.php +++ b/src/applications/phame/controller/blog/PhameBlogViewController.php @@ -44,16 +44,7 @@ final class PhameBlogViewController extends PhameLiveController { $header->setStatus($header_icon, $header_color, $header_name); $actions = $this->renderActions($blog); - $action_button = id(new PHUIButtonView()) - ->setTag('a') - ->setText(pht('Actions')) - ->setHref('#') - ->setIcon('fa-bars') - ->addClass('phui-mobile-menu') - ->setDropdownMenu($actions); - - $header->addActionLink($action_button); - + $header->setActionList($actions); $header->setPolicyObject($blog); } diff --git a/src/applications/phame/controller/post/PhamePostViewController.php b/src/applications/phame/controller/post/PhamePostViewController.php index 537bfbb713..a4231cc1ac 100644 --- a/src/applications/phame/controller/post/PhamePostViewController.php +++ b/src/applications/phame/controller/post/PhamePostViewController.php @@ -24,17 +24,8 @@ final class PhamePostViewController if (!$is_external) { $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->addActionLink($action_button); + $header->setActionList($actions); } $document = id(new PHUIDocumentViewPro()) diff --git a/src/applications/phid/view/PHUIHandleTagListView.php b/src/applications/phid/view/PHUIHandleTagListView.php index 0bbfc4d249..baad938051 100644 --- a/src/applications/phid/view/PHUIHandleTagListView.php +++ b/src/applications/phid/view/PHUIHandleTagListView.php @@ -52,6 +52,13 @@ final class PHUIHandleTagListView extends AphrontTagView { protected function getTagContent() { $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 (!$handles) { if (strlen($this->noDataString)) { diff --git a/src/applications/pholio/controller/PholioInlineController.php b/src/applications/pholio/controller/PholioInlineController.php index 101ef9e758..ddced1f9eb 100644 --- a/src/applications/pholio/controller/PholioInlineController.php +++ b/src/applications/pholio/controller/PholioInlineController.php @@ -86,16 +86,13 @@ final class PholioInlineController extends PholioController { ), $author_handle->renderLink()); + $inline_content = new PHUIRemarkupView($viewer, $inline->getContent()); $comment_body = phutil_tag( 'div', array( 'class' => 'pholio-inline-comment-body', ), - PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setContent($inline->getContent()), - 'default', - $viewer)); + $inline_content); return $this->newDialog() ->setTitle(pht('Inline Comment')) diff --git a/src/applications/phortune/controller/PhortuneCartController.php b/src/applications/phortune/controller/PhortuneCartController.php index d3e19f52f1..9fefc7db6e 100644 --- a/src/applications/phortune/controller/PhortuneCartController.php +++ b/src/applications/phortune/controller/PhortuneCartController.php @@ -48,12 +48,7 @@ abstract class PhortuneCartController return null; } - $output = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setPreserveLinebreaks(true) - ->setContent($description), - 'default', - $this->getViewer()); + $output = new PHUIRemarkupView($this->getUser(), $description); $box = id(new PHUIBoxView()) ->addMargin(PHUI::MARGIN_LARGE) diff --git a/src/applications/phortune/controller/PhortuneMerchantViewController.php b/src/applications/phortune/controller/PhortuneMerchantViewController.php index 55bb136dab..1921f0c5a5 100644 --- a/src/applications/phortune/controller/PhortuneMerchantViewController.php +++ b/src/applications/phortune/controller/PhortuneMerchantViewController.php @@ -136,11 +136,7 @@ final class PhortuneMerchantViewController $description = $merchant->getDescription(); if (strlen($description)) { - $description = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($description), - 'default', - $viewer); - + $description = new PHUIRemarkupView($viewer, $description); $view->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); $view->addTextContent($description); diff --git a/src/applications/phriction/controller/PhrictionDocumentController.php b/src/applications/phriction/controller/PhrictionDocumentController.php index 7d98e9ed3b..7dae0fad03 100644 --- a/src/applications/phriction/controller/PhrictionDocumentController.php +++ b/src/applications/phriction/controller/PhrictionDocumentController.php @@ -203,19 +203,11 @@ final class PhrictionDocumentController $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()) ->setUser($viewer) ->setPolicyObject($document) ->setHeader($page_title) - ->addActionLink($action_button); + ->setActionList($actions); if ($content) { $header->setEpoch($content->getDateCreated()); diff --git a/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php b/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php index 51abcd8059..dbc7fbcc79 100644 --- a/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php +++ b/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php @@ -134,16 +134,12 @@ final class PhabricatorPhurlURLViewController $properties->invokeWillRenderEvent(); - if (strlen($url->getDescription())) { - $description = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($url->getDescription()), - 'default', - $viewer); - + $description = $url->getDescription(); + if (strlen($description)) { + $description = new PHUIRemarkupView($viewer, $description); $properties->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); - $properties->addTextContent($description); } diff --git a/src/applications/policy/editor/PhabricatorPolicyEditEngineExtension.php b/src/applications/policy/editor/PhabricatorPolicyEditEngineExtension.php index fd973c0da7..568b7bc399 100644 --- a/src/applications/policy/editor/PhabricatorPolicyEditEngineExtension.php +++ b/src/applications/policy/editor/PhabricatorPolicyEditEngineExtension.php @@ -101,6 +101,9 @@ final class PhabricatorPolicyEditEngineExtension if ($capability == PhabricatorPolicyCapability::CAN_VIEW) { $type_space = PhabricatorTransactions::TYPE_SPACE; if (isset($types[$type_space])) { + $space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID( + $object); + $space_field = id(new PhabricatorSpaceEditField()) ->setKey('spacePHID') ->setLabel(pht('Space')) @@ -114,7 +117,7 @@ final class PhabricatorPolicyEditEngineExtension ->setConduitDescription( pht('Shift the object between spaces.')) ->setConduitTypeDescription(pht('New space PHID.')) - ->setValue($object->getSpacePHID()); + ->setValue($space_phid); $fields[] = $space_field; $space_field->setPolicyField($policy_field); diff --git a/src/applications/ponder/controller/PonderQuestionViewController.php b/src/applications/ponder/controller/PonderQuestionViewController.php index 2ec911bdde..38ca4cad30 100644 --- a/src/applications/ponder/controller/PonderQuestionViewController.php +++ b/src/applications/ponder/controller/PonderQuestionViewController.php @@ -44,7 +44,6 @@ final class PonderQuestionViewController extends PonderController { $actions = $this->buildActionListView($question); $properties = $this->buildPropertyListView($question, $actions); - $sidebar = $this->buildSidebar($question); $content_id = celerity_generate_unique_node_id(); $timeline = $this->buildTransactionTimeline( @@ -81,20 +80,6 @@ final class PonderQuestionViewController extends PonderController { ->addPropertyList($properties) ->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->addTextCrumb('Q'.$id, '/Q'.$id); @@ -107,21 +92,14 @@ final class PonderQuestionViewController extends PonderController { ->appendChild($answer); } - $ponder_view = id(new PHUITwoColumnView()) - ->setMainColumn(array( - $object_box, - $comment_view, - $answer_wiki, - $answers, - $answer_add_panel, - )) - ->setSideColumn($sidebar) - ->addClass('ponder-question-view'); - return $this->buildApplicationPage( array( $crumbs, - $ponder_view, + $object_box, + $comment_view, + $answer_wiki, + $answers, + $answer_add_panel, ), array( 'title' => 'Q'.$question->getID().' '.$question->getTitle(), @@ -261,48 +239,4 @@ final class PonderQuestionViewController extends PonderController { 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; - } - } diff --git a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php index ee90afcb3b..39efcadb55 100644 --- a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php +++ b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php @@ -1013,6 +1013,51 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { $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( PhabricatorUser $viewer, 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( PhabricatorUser $user, PhabricatorProject $parent = null, diff --git a/src/applications/project/application/PhabricatorProjectApplication.php b/src/applications/project/application/PhabricatorProjectApplication.php index 3a39aefa22..41620ad4e4 100644 --- a/src/applications/project/application/PhabricatorProjectApplication.php +++ b/src/applications/project/application/PhabricatorProjectApplication.php @@ -84,6 +84,10 @@ final class PhabricatorProjectApplication extends PhabricatorApplication { => 'PhabricatorProjectBoardReorderController', 'disable/' => 'PhabricatorProjectBoardDisableController', + 'manage/' + => 'PhabricatorProjectBoardManageController', + 'background/' + => 'PhabricatorProjectBoardBackgroundController', ), 'update/(?P[1-9]\d*)/(?P[^/]+)/' => 'PhabricatorProjectUpdateController', diff --git a/src/applications/project/constants/PhabricatorProjectWorkboardBackgroundColor.php b/src/applications/project/constants/PhabricatorProjectWorkboardBackgroundColor.php new file mode 100644 index 0000000000..a6c0fc9b1b --- /dev/null +++ b/src/applications/project/constants/PhabricatorProjectWorkboardBackgroundColor.php @@ -0,0 +1,124 @@ + '', + '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'); + } +} diff --git a/src/applications/project/controller/PhabricatorProjectBoardBackgroundController.php b/src/applications/project/controller/PhabricatorProjectBoardBackgroundController.php new file mode 100644 index 0000000000..3ba1f03a56 --- /dev/null +++ b/src/applications/project/controller/PhabricatorProjectBoardBackgroundController.php @@ -0,0 +1,168 @@ +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, + )); + } + +} diff --git a/src/applications/project/controller/PhabricatorProjectBoardManageController.php b/src/applications/project/controller/PhabricatorProjectBoardManageController.php new file mode 100644 index 0000000000..db4dfe31be --- /dev/null +++ b/src/applications/project/controller/PhabricatorProjectBoardManageController.php @@ -0,0 +1,174 @@ +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); + } + + +} diff --git a/src/applications/project/controller/PhabricatorProjectBoardReorderController.php b/src/applications/project/controller/PhabricatorProjectBoardReorderController.php index 011bbd069a..05f1dd2d43 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardReorderController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardReorderController.php @@ -23,13 +23,13 @@ final class PhabricatorProjectBoardReorderController $this->setProject($project); $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/"); if ($request->isFormPost()) { // User clicked "Done", make sure the page reloads to show the new // column order. - return id(new AphrontRedirectResponse())->setURI($board_uri); + return id(new AphrontRedirectResponse())->setURI($manage_uri); } $columns = id(new PhabricatorProjectColumnQuery()) diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php index e9a5159295..107d3dc0d0 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php @@ -124,10 +124,14 @@ final class PhabricatorProjectBoardViewController $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()) ->setViewer($viewer) ->setBoardPHIDs(array($board_phid)) - ->setObjectPHIDs(array_keys($tasks)) + ->setObjectPHIDs(array_keys($board_tasks)) ->setFetchAllBoards(true) ->executeLayout(); @@ -427,7 +431,7 @@ final class PhabricatorProjectBoardViewController $crumbs->addAction($manage_menu); $crumbs->addAction($fullscreen); - return $this->newPage() + $page = $this->newPage() ->setTitle( array( $project->getDisplayName(), @@ -445,6 +449,17 @@ final class PhabricatorProjectBoardViewController array( $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() { @@ -671,9 +686,8 @@ final class PhabricatorProjectBoardViewController $id = $project->getID(); - $disable_uri = $this->getApplicationURI("board/{$id}/disable/"); + $manage_uri = $this->getApplicationURI("board/{$id}/manage/"); $add_uri = $this->getApplicationURI("board/{$id}/edit/"); - $reorder_uri = $this->getApplicationURI("board/{$id}/reorder/"); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, @@ -687,14 +701,12 @@ final class PhabricatorProjectBoardViewController ->setName(pht('Add Column')) ->setHref($add_uri) ->setDisabled(!$can_edit) - ->setWorkflow(!$can_edit); + ->setWorkflow(true); $manage_items[] = id(new PhabricatorActionView()) - ->setIcon('fa-exchange') - ->setName(pht('Reorder Columns')) - ->setHref($reorder_uri) - ->setDisabled(!$can_edit) - ->setWorkflow(true); + ->setIcon('fa-pencil') + ->setName(pht('Manage Board')) + ->setHref($manage_uri); if ($show_hidden) { $hidden_uri = $this->getURIWithState() @@ -726,13 +738,6 @@ final class PhabricatorProjectBoardViewController ->setHref($batch_edit_uri) ->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()) ->setUser($viewer); foreach ($manage_items as $item) { @@ -817,14 +822,6 @@ final class PhabricatorProjectBoardViewController ->setHref($batch_edit_uri) ->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()); $hide_uri = 'board/'.$this->id.'/hide/'.$column->getID().'/'; $hide_uri = $this->getApplicationURI($hide_uri); diff --git a/src/applications/project/controller/PhabricatorProjectColumnDetailController.php b/src/applications/project/controller/PhabricatorProjectColumnDetailController.php index b0cd4ac0b4..84bdc2b89c 100644 --- a/src/applications/project/controller/PhabricatorProjectColumnDetailController.php +++ b/src/applications/project/controller/PhabricatorProjectColumnDetailController.php @@ -73,8 +73,7 @@ final class PhabricatorProjectColumnDetailController $header = id(new PHUIHeaderView()) ->setUser($viewer) - ->setHeader($column->getDisplayName()) - ->setPolicyObject($column); + ->setHeader($column->getDisplayName()); if ($column->isHidden()) { $header->setStatus('fa-ban', 'dark', pht('Hidden')); diff --git a/src/applications/project/controller/PhabricatorProjectColumnEditController.php b/src/applications/project/controller/PhabricatorProjectColumnEditController.php index 5ebc721c69..9f880b9515 100644 --- a/src/applications/project/controller/PhabricatorProjectColumnEditController.php +++ b/src/applications/project/controller/PhabricatorProjectColumnEditController.php @@ -136,21 +136,13 @@ final class PhabricatorProjectColumnEditController $submit = pht('Save Column'); } - $form->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue($submit) - ->addCancelButton($view_uri)); - - $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) - ->setValidationException($validation_exception) - ->setForm($form); - - $nav = $this->getProfileMenu(); - - return $this->newPage() + return $this->newDialog() + ->setWidth(AphrontDialogView::WIDTH_FORM) ->setTitle($title) - ->setNavigation($nav) - ->appendChild($form_box); + ->appendForm($form) + ->setValidationException($validation_exception) + ->addCancelButton($view_uri) + ->addSubmitButton($submit); + } } diff --git a/src/applications/project/controller/PhabricatorProjectEditController.php b/src/applications/project/controller/PhabricatorProjectEditController.php index 5091135bec..7c041af93a 100644 --- a/src/applications/project/controller/PhabricatorProjectEditController.php +++ b/src/applications/project/controller/PhabricatorProjectEditController.php @@ -24,6 +24,11 @@ final class PhabricatorProjectEditController $id = $request->getURIData('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')); if (!$parent_id) { $parent_id = $request->getStr('parent'); diff --git a/src/applications/project/controller/PhabricatorProjectProfileController.php b/src/applications/project/controller/PhabricatorProjectProfileController.php index 27cf1bf344..8220592074 100644 --- a/src/applications/project/controller/PhabricatorProjectProfileController.php +++ b/src/applications/project/controller/PhabricatorProjectProfileController.php @@ -59,14 +59,14 @@ final class PhabricatorProjectProfileController ->setUser($viewer) ->setProject($project) ->setLimit(5) - ->setBackground(PHUIBoxView::GREY) + ->setBackground(PHUIObjectBoxView::GREY) ->setUserPHIDs($project->getMemberPHIDs()); $watcher_list = id(new PhabricatorProjectWatcherListView()) ->setUser($viewer) ->setProject($project) ->setLimit(5) - ->setBackground(PHUIBoxView::GREY) + ->setBackground(PHUIObjectBoxView::GREY) ->setUserPHIDs($project->getWatcherPHIDs()); $nav = $this->getProfileMenu(); @@ -83,8 +83,11 @@ final class PhabricatorProjectProfileController $feed = $this->renderStories($stories); $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( array( $properties, @@ -101,17 +104,6 @@ final class PhabricatorProjectProfileController $crumbs = $this->buildApplicationCrumbs(); $crumbs->setBorder(true); - require_celerity_resource('project-view-css'); - $home = phutil_tag( - 'div', - array( - 'class' => 'project-view-home', - ), - array( - $header, - $columns, - )); - return $this->newPage() ->setNavigation($nav) ->setCrumbs($crumbs) @@ -142,7 +134,7 @@ final class PhabricatorProjectProfileController } $view = id(new PHUIBoxView()) - ->setColor(PHUIBoxView::GREY) + ->setBorder(true) ->appendChild($view) ->addClass('project-view-properties'); @@ -162,19 +154,32 @@ final class PhabricatorProjectProfileController private function renderWatchAction(PhabricatorProject $project) { $viewer = $this->getViewer(); - $viewer_phid = $viewer->getPHID(); $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_text = pht('Watch Project'); $watch_href = "/project/watch/{$id}/?via=profile"; + $watch_disabled = false; } else { $watch_icon = 'fa-eye-slash'; $watch_text = pht('Unwatch Project'); $watch_href = "/project/unwatch/{$id}/?via=profile"; + $watch_disabled = false; } $watch_icon = id(new PHUIIconView()) @@ -185,7 +190,8 @@ final class PhabricatorProjectProfileController ->setWorkflow(true) ->setIcon($watch_icon) ->setText($watch_text) - ->setHref($watch_href); + ->setHref($watch_href) + ->setDisabled($watch_disabled); } private function buildMilestoneList(PhabricatorProject $project) { @@ -205,7 +211,7 @@ final class PhabricatorProjectProfileController array( PhabricatorProjectStatus::STATUS_ACTIVE, )) - ->setOrder('newest') + ->setOrderVector(array('milestoneNumber', 'id')) ->execute(); if (!$milestones) { return null; @@ -230,7 +236,7 @@ final class PhabricatorProjectProfileController return id(new PHUIObjectBoxView()) ->setHeader($header) - ->setBackground(PHUIBoxView::GREY) + ->setBackground(PHUIObjectBoxView::GREY) ->setObjectList($milestone_list); } @@ -278,7 +284,7 @@ final class PhabricatorProjectProfileController return id(new PHUIObjectBoxView()) ->setHeader($header) - ->setBackground(PHUIBoxView::GREY) + ->setBackground(PHUIObjectBoxView::GREY) ->setObjectList($subproject_list); } diff --git a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php index afafed77b1..eb32d00b92 100644 --- a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php +++ b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php @@ -43,7 +43,7 @@ final class PhabricatorProjectSubprojectsController ->withParentProjectPHIDs(array($project->getPHID())) ->needImages(true) ->withIsMilestone(true) - ->setOrder('newest') + ->setOrderVector(array('milestoneNumber', 'id')) ->execute(); } else { $milestones = array(); @@ -181,6 +181,9 @@ final class PhabricatorProjectSubprojectsController $viewer = $this->getViewer(); $id = $project->getID(); + $can_create = $this->hasApplicationCapability( + ProjectCreateProjectsCapability::CAPABILITY); + $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $project, @@ -198,7 +201,7 @@ final class PhabricatorProjectSubprojectsController $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}"; $view->addAction( @@ -209,7 +212,7 @@ final class PhabricatorProjectSubprojectsController ->setDisabled(!$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 // the user about the effects before moving forward. diff --git a/src/applications/project/controller/PhabricatorProjectWatchController.php b/src/applications/project/controller/PhabricatorProjectWatchController.php index 9624a0b730..00117768e1 100644 --- a/src/applications/project/controller/PhabricatorProjectWatchController.php +++ b/src/applications/project/controller/PhabricatorProjectWatchController.php @@ -25,6 +25,23 @@ final class PhabricatorProjectWatchController $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()) { $edge_action = null; switch ($action) { @@ -61,10 +78,14 @@ final class PhabricatorProjectWatchController switch ($action) { case 'watch': $title = pht('Watch Project?'); - $body = pht( + $body = array(); + $body[] = pht( 'Watching a project will let you monitor it closely. You will '. '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'); break; case 'unwatch': @@ -78,12 +99,17 @@ final class PhabricatorProjectWatchController return new Aphront404Response(); } - return $this->newDialog() + $dialog = $this->newDialog() ->setTitle($title) ->addHiddenInput('via', $via) - ->appendParagraph($body) ->addCancelButton($done_uri) ->addSubmitButton($submit); + + foreach ((array)$body as $paragraph) { + $dialog->appendParagraph($paragraph); + } + + return $dialog; } } diff --git a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php index b1aab1e417..aa8c0852ab 100644 --- a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php +++ b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php @@ -42,6 +42,7 @@ final class PhabricatorProjectTransactionEditor $types[] = PhabricatorProjectTransaction::TYPE_HASWORKBOARD; $types[] = PhabricatorProjectTransaction::TYPE_DEFAULT_SORT; $types[] = PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER; + $types[] = PhabricatorProjectTransaction::TYPE_BACKGROUND; return $types; } @@ -77,6 +78,8 @@ final class PhabricatorProjectTransactionEditor return $object->getDefaultWorkboardSort(); case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER: return $object->getDefaultWorkboardFilter(); + case PhabricatorProjectTransaction::TYPE_BACKGROUND: + return $object->getWorkboardBackgroundColor(); } return parent::getCustomTransactionOldValue($object, $xaction); @@ -100,6 +103,12 @@ final class PhabricatorProjectTransactionEditor return $xaction->getNewValue(); case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: return (int)$xaction->getNewValue(); + case PhabricatorProjectTransaction::TYPE_BACKGROUND: + $value = $xaction->getNewValue(); + if (!strlen($value)) { + return null; + } + return $value; case PhabricatorProjectTransaction::TYPE_SLUGS: return $this->normalizeSlugs($xaction->getNewValue()); } @@ -153,6 +162,9 @@ final class PhabricatorProjectTransactionEditor case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER: $object->setDefaultWorkboardFilter($xaction->getNewValue()); return; + case PhabricatorProjectTransaction::TYPE_BACKGROUND: + $object->setWorkboardBackgroundColor($xaction->getNewValue()); + return; } return parent::applyCustomInternalTransaction($object, $xaction); @@ -198,6 +210,7 @@ final class PhabricatorProjectTransactionEditor case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: case PhabricatorProjectTransaction::TYPE_DEFAULT_SORT: case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER: + case PhabricatorProjectTransaction::TYPE_BACKGROUND: return; } diff --git a/src/applications/project/engine/PhabricatorBoardLayoutEngine.php b/src/applications/project/engine/PhabricatorBoardLayoutEngine.php index 3525af5ff0..248677fbec 100644 --- a/src/applications/project/engine/PhabricatorBoardLayoutEngine.php +++ b/src/applications/project/engine/PhabricatorBoardLayoutEngine.php @@ -506,6 +506,7 @@ final class PhabricatorBoardLayoutEngine extends Phobject { } } + $view_sequence = 1; foreach ($object_phids as $object_phid) { $positions = idx($position_groups, $object_phid, array()); @@ -554,7 +555,8 @@ final class PhabricatorBoardLayoutEngine extends Phobject { ->setBoardPHID($board_phid) ->setColumnPHID($proxy_hit) ->setObjectPHID($object_phid) - ->setSequence(0); + ->setSequence(0) + ->setViewSequence($view_sequence++); $this->addQueue[] = $new_position; @@ -578,7 +580,8 @@ final class PhabricatorBoardLayoutEngine extends Phobject { ->setBoardPHID($board_phid) ->setColumnPHID($default_phid) ->setObjectPHID($object_phid) - ->setSequence(0); + ->setSequence(0) + ->setViewSequence($view_sequence++); $this->addQueue[] = $new_position; diff --git a/src/applications/project/query/PhabricatorProjectQuery.php b/src/applications/project/query/PhabricatorProjectQuery.php index d12e66e392..9845628b4b 100644 --- a/src/applications/project/query/PhabricatorProjectQuery.php +++ b/src/applications/project/query/PhabricatorProjectQuery.php @@ -6,6 +6,7 @@ final class PhabricatorProjectQuery private $ids; private $phids; private $memberPHIDs; + private $watcherPHIDs; private $slugs; private $slugNormals; private $slugMap; @@ -62,6 +63,11 @@ final class PhabricatorProjectQuery return $this; } + public function withWatcherPHIDs(array $watcher_phids) { + $this->watcherPHIDs = $watcher_phids; + return $this; + } + public function withSlugs(array $slugs) { $this->slugs = $slugs; return $this; @@ -170,6 +176,11 @@ final class PhabricatorProjectQuery 'type' => 'string', 'unique' => true, ), + 'milestoneNumber' => array( + 'table' => $this->getPrimaryTableAlias(), + 'column' => 'milestoneNumber', + 'type' => 'int', + ), ); } @@ -242,7 +253,7 @@ final class PhabricatorProjectQuery $all_graph = $this->getAllReachableAncestors($projects); - if ($this->needAncestorMembers) { + if ($this->needAncestorMembers || $this->needWatchers) { $src_projects = $all_graph; } else { $src_projects = $projects; @@ -250,11 +261,13 @@ final class PhabricatorProjectQuery $all_sources = array(); foreach ($src_projects as $project) { + // For milestones, we need parent members. if ($project->isMilestone()) { - $phid = $project->getParentProjectPHID(); - } else { - $phid = $project->getPHID(); + $parent_phid = $project->getParentProjectPHID(); + $all_sources[$parent_phid] = $parent_phid; } + + $phid = $project->getPHID(); $all_sources[$phid] = $phid; } @@ -313,7 +326,7 @@ final class PhabricatorProjectQuery if ($this->needWatchers) { $watcher_phids = $edge_query->getDestinationPHIDs( - $source_phids, + array($project_phid), array($watcher_type)); $project->attachWatcherPHIDs($watcher_phids); $project->setIsUserWatcher( @@ -429,6 +442,13 @@ final class PhabricatorProjectQuery $this->memberPHIDs); } + if ($this->watcherPHIDs !== null) { + $where[] = qsprintf( + $conn, + 'w.dst IN (%Ls)', + $this->watcherPHIDs); + } + if ($this->slugs !== null) { $where[] = qsprintf( $conn, @@ -542,7 +562,7 @@ final class PhabricatorProjectQuery } protected function shouldGroupQueryResultRows() { - if ($this->memberPHIDs || $this->nameTokens) { + if ($this->memberPHIDs || $this->watcherPHIDs || $this->nameTokens) { return true; } return parent::shouldGroupQueryResultRows(); @@ -559,6 +579,14 @@ final class PhabricatorProjectQuery 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) { $joins[] = qsprintf( $conn, diff --git a/src/applications/project/query/PhabricatorProjectSearchEngine.php b/src/applications/project/query/PhabricatorProjectSearchEngine.php index ea91dc9754..cfb1868a61 100644 --- a/src/applications/project/query/PhabricatorProjectSearchEngine.php +++ b/src/applications/project/query/PhabricatorProjectSearchEngine.php @@ -26,6 +26,10 @@ final class PhabricatorProjectSearchEngine ->setLabel(pht('Members')) ->setKey('memberPHIDs') ->setAliases(array('member', 'members')), + id(new PhabricatorUsersSearchField()) + ->setLabel(pht('Watchers')) + ->setKey('watcherPHIDs') + ->setAliases(array('watcher', 'watchers')), id(new PhabricatorSearchSelectField()) ->setLabel(pht('Status')) ->setKey('status') @@ -54,6 +58,10 @@ final class PhabricatorProjectSearchEngine $query->withMemberPHIDs($map['memberPHIDs']); } + if ($map['watcherPHIDs']) { + $query->withWatcherPHIDs($map['watcherPHIDs']); + } + if ($map['status']) { $status = idx($this->getStatusValues(), $map['status']); if ($status) { diff --git a/src/applications/project/storage/PhabricatorProject.php b/src/applications/project/storage/PhabricatorProject.php index 861fac646b..061886f6f0 100644 --- a/src/applications/project/storage/PhabricatorProject.php +++ b/src/applications/project/storage/PhabricatorProject.php @@ -287,6 +287,32 @@ final class PhabricatorProject extends PhabricatorProjectDAO 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) { if ($this->sparseWatchers === self::ATTACHABLE) { $this->sparseWatchers = array(); @@ -304,6 +330,21 @@ final class PhabricatorProject extends PhabricatorProjectDAO 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) { $this->slugs = $slugs; return $this; @@ -579,6 +620,31 @@ final class PhabricatorProject extends PhabricatorProjectDAO 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 )------------------------------------ */ diff --git a/src/applications/project/storage/PhabricatorProjectColumn.php b/src/applications/project/storage/PhabricatorProjectColumn.php index 4092ca7fa8..28aed0f5a8 100644 --- a/src/applications/project/storage/PhabricatorProjectColumn.php +++ b/src/applications/project/storage/PhabricatorProjectColumn.php @@ -5,7 +5,8 @@ final class PhabricatorProjectColumn implements PhabricatorApplicationTransactionInterface, PhabricatorPolicyInterface, - PhabricatorDestructibleInterface { + PhabricatorDestructibleInterface, + PhabricatorExtendedPolicyInterface { const STATUS_ACTIVE = 0; const STATUS_HIDDEN = 1; @@ -169,14 +170,16 @@ final class PhabricatorProjectColumn // Normal columns and subproject columns go first, in a user-controlled // 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()) { $group = 'A'; $sequence = $this->getSequence(); } else { $group = 'B'; - $sequence = $proxy->getMilestoneNumber(); + $sequence = (10000000 - $proxy->getMilestoneNumber()); } return sprintf('%s%012d', $group, $sequence); @@ -217,7 +220,14 @@ final class PhabricatorProjectColumn } 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) { @@ -231,6 +241,16 @@ final class PhabricatorProjectColumn } +/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */ + + + public function getExtendedPolicy($capability, PhabricatorUser $viewer) { + return array( + array($this->getProject(), $capability), + ); + } + + /* -( PhabricatorDestructibleInterface )----------------------------------- */ public function destroyObjectPermanently( diff --git a/src/applications/project/storage/PhabricatorProjectColumnPosition.php b/src/applications/project/storage/PhabricatorProjectColumnPosition.php index c59676f8e4..40929606ac 100644 --- a/src/applications/project/storage/PhabricatorProjectColumnPosition.php +++ b/src/applications/project/storage/PhabricatorProjectColumnPosition.php @@ -9,6 +9,7 @@ final class PhabricatorProjectColumnPosition extends PhabricatorProjectDAO protected $sequence; private $column = self::ATTACHABLE; + private $viewSequence = 0; protected function getConfiguration() { return array( @@ -40,18 +41,30 @@ final class PhabricatorProjectColumnPosition extends PhabricatorProjectDAO return $this; } - public function getOrderingKey() { - if (!$this->getID() && !$this->getSequence()) { - return 0; - } + public function setViewSequence($view_sequence) { + $this->viewSequence = $view_sequence; + return $this; + } - // Low sequence numbers go above high sequence numbers. - // High position IDs go above low position IDs. - // Broadly, this makes newly added stuff float to the top. + public function getOrderingKey() { + // We're ordering both real positions and "virtual" positions which we have + // 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( - '~%012d%012d', + '~%012d%012d%012d', $this->getSequence(), + ((1 << 31) - $this->viewSequence), ((1 << 31) - $this->getID())); } diff --git a/src/applications/project/storage/PhabricatorProjectTransaction.php b/src/applications/project/storage/PhabricatorProjectTransaction.php index e812952630..ef5d39b4e8 100644 --- a/src/applications/project/storage/PhabricatorProjectTransaction.php +++ b/src/applications/project/storage/PhabricatorProjectTransaction.php @@ -15,6 +15,7 @@ final class PhabricatorProjectTransaction const TYPE_HASWORKBOARD = 'project:hasworkboard'; const TYPE_DEFAULT_SORT = 'project:sort'; const TYPE_DEFAULT_FILTER = 'project:filter'; + const TYPE_BACKGROUND = 'project:background'; // NOTE: This is deprecated, members are just a normal edge now. const TYPE_MEMBERS = 'project:members'; @@ -73,6 +74,7 @@ final class PhabricatorProjectTransaction case self::TYPE_HASWORKBOARD: case self::TYPE_DEFAULT_SORT: case self::TYPE_DEFAULT_FILTER: + case self::TYPE_BACKGROUND: return true; } @@ -84,6 +86,7 @@ final class PhabricatorProjectTransaction case self::TYPE_HASWORKBOARD: case self::TYPE_DEFAULT_SORT: case self::TYPE_DEFAULT_FILTER: + case self::TYPE_BACKGROUND: return true; } @@ -291,6 +294,11 @@ final class PhabricatorProjectTransaction return pht( '%s changed the default filter for the project workboard.', $author_handle); + + case self::TYPE_BACKGROUND: + return pht( + '%s changed the background color of the project workboard.', + $author_handle); } return parent::getTitle(); diff --git a/src/applications/repository/editor/PhabricatorRepositoryEditor.php b/src/applications/repository/editor/PhabricatorRepositoryEditor.php index 54f737c232..fa585a826a 100644 --- a/src/applications/repository/editor/PhabricatorRepositoryEditor.php +++ b/src/applications/repository/editor/PhabricatorRepositoryEditor.php @@ -45,6 +45,7 @@ final class PhabricatorRepositoryEditor $types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES; $types[] = PhabricatorRepositoryTransaction::TYPE_STAGING_URI; $types[] = PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS; + $types[] = PhabricatorRepositoryTransaction::TYPE_CALLSIGN; $types[] = PhabricatorTransactions::TYPE_EDGE; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; @@ -110,6 +111,8 @@ final class PhabricatorRepositoryEditor return $object->getDetail('staging-uri'); case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS: 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: return $xaction->getNewValue(); case PhabricatorRepositoryTransaction::TYPE_SLUG: + case PhabricatorRepositoryTransaction::TYPE_CALLSIGN: $name = $xaction->getNewValue(); if (strlen($name)) { return $name; @@ -240,6 +244,9 @@ final class PhabricatorRepositoryEditor 'automation.blueprintPHIDs', $xaction->getNewValue()); return; + case PhabricatorRepositoryTransaction::TYPE_CALLSIGN: + $object->setCallsign($xaction->getNewValue()); + return; case PhabricatorRepositoryTransaction::TYPE_ENCODING: // 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 @@ -468,7 +475,7 @@ final class PhabricatorRepositoryEditor } try { - PhabricatorRepository::asssertValidRepositorySlug($new); + PhabricatorRepository::assertValidRepositorySlug($new); } catch (Exception $ex) { $errors[] = new PhabricatorApplicationTransactionValidationError( $type, @@ -495,6 +502,47 @@ final class PhabricatorRepositoryEditor } 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; @@ -522,4 +570,27 @@ final class PhabricatorRepositoryEditor 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; + } + } diff --git a/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php b/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php index 30ab234027..690f9425a4 100644 --- a/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php +++ b/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php @@ -192,7 +192,7 @@ final class PhabricatorRepositoryPullEngine } private function getHookContextIdentifier(PhabricatorRepository $repository) { - $identifier = $repository->getCallsign(); + $identifier = $repository->getPHID(); $instance = PhabricatorEnv::getEnvConfig('cluster.instance'); if (strlen($instance)) { diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index 9b38f911d4..c2ed8023f3 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -93,7 +93,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO ), self::CONFIG_COLUMN_SCHEMA => array( 'name' => 'sort255', - 'callsign' => 'sort32', + 'callsign' => 'sort32?', 'repositorySlug' => 'sort64?', 'versionControlSystem' => 'text32', 'uuid' => 'text64?', @@ -149,13 +149,21 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO } 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() { - // TODO: This is intended to produce a human-readable name that is not - // necessarily a global, unique identifier. Eventually, it may just return - // a string like "skynet" instead of "rSKYNET". + $slug = $this->getRepositorySlug(); + if (strlen($slug)) { + return $slug; + } + return $this->getMonogram(); } @@ -317,14 +325,14 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO public static function isValidRepositorySlug($slug) { try { - self::asssertValidRepositorySlug($slug); + self::assertValidRepositorySlug($slug); return true; } catch (Exception $ex) { return false; } } - public static function asssertValidRepositorySlug($slug) { + public static function assertValidRepositorySlug($slug) { if (!strlen($slug)) { throw new Exception( 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 )------------------------------------------- */ @@ -675,7 +707,13 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO } 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) { @@ -684,7 +722,96 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO public function getCommitURI($identifier) { $callsign = $this->getCallsign(); - return "/r{$callsign}{$identifier}"; + if (strlen($callsign)) { + 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/?diffusion/(?P[A-Z]+|[0-9]\d*))'. + '(?P(?:/.*)?)'. + '\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/diffusion/)'. + '(?P[^/]+)'. + '(?P(?:/.*)?)'. + '\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/)'. + '(?P'. + '(?:'. + 'r(?P[A-Z]+)'. + '|'. + 'R(?P[1-9]\d*):'. + ')'. + '(?P[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) { @@ -955,7 +1082,13 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO } 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; } diff --git a/src/applications/repository/storage/PhabricatorRepositoryTransaction.php b/src/applications/repository/storage/PhabricatorRepositoryTransaction.php index a10ac0a60b..20c975cb33 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryTransaction.php +++ b/src/applications/repository/storage/PhabricatorRepositoryTransaction.php @@ -29,6 +29,7 @@ final class PhabricatorRepositoryTransaction const TYPE_SYMBOLS_LANGUAGE = 'repo:symbol-language'; const TYPE_STAGING_URI = 'repo:staging-uri'; const TYPE_AUTOMATION_BLUEPRINTS = 'repo:automation-blueprints'; + const TYPE_CALLSIGN = 'repo:callsign'; // TODO: Clean up these legacy transaction types. const TYPE_SSH_LOGIN = 'repo:ssh-login'; @@ -466,6 +467,26 @@ final class PhabricatorRepositoryTransaction new PhutilNumber(count($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(); diff --git a/src/applications/repository/storage/__tests__/PhabricatorRepositoryURITestCase.php b/src/applications/repository/storage/__tests__/PhabricatorRepositoryURITestCase.php index 0d13cf0e08..3994567225 100644 --- a/src/applications/repository/storage/__tests__/PhabricatorRepositoryURITestCase.php +++ b/src/applications/repository/storage/__tests__/PhabricatorRepositoryURITestCase.php @@ -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() { $svn = PhabricatorRepositoryType::REPOSITORY_TYPE_SVN; $git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT; diff --git a/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php b/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php index 87de1ff660..fd05afb057 100644 --- a/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php +++ b/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php @@ -122,13 +122,13 @@ final class PhabricatorSlowvotePollController $view->invokeWillRenderEvent(); - if (strlen($poll->getDescription())) { - $view->addTextContent( - $output = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent( - $poll->getDescription()), - 'default', - $viewer)); + $description = $poll->getDescription(); + if (strlen($description)) { + $description = new PHUIRemarkupView($viewer, $description); + $view->addSectionHeader( + pht('Description'), + PHUIPropertyListView::ICON_SUMMARY); + $view->addTextContent($description); } return $view; diff --git a/src/applications/slowvote/storage/PhabricatorSlowvoteTransaction.php b/src/applications/slowvote/storage/PhabricatorSlowvoteTransaction.php index e45fdb013f..fb1b7fc2f3 100644 --- a/src/applications/slowvote/storage/PhabricatorSlowvoteTransaction.php +++ b/src/applications/slowvote/storage/PhabricatorSlowvoteTransaction.php @@ -97,7 +97,20 @@ final class PhabricatorSlowvoteTransaction return parent::getTitle(); } - public function getTitleForFeed() { + public function getRemarkupBlocks() { + $blocks = parent::getRemarkupBlocks(); + + $type = $this->getTransactionType(); + switch ($type) { + case self::TYPE_DESCRIPTION: + $blocks[] = $this->getNewValue(); + break; + } + + return $blocks; + } + + public function getTitleForFeed() { $author_phid = $this->getAuthorPHID(); $object_phid = $this->getObjectPHID(); diff --git a/src/applications/slowvote/view/SlowvoteEmbedView.php b/src/applications/slowvote/view/SlowvoteEmbedView.php index 0270e82f00..3582ea45b3 100644 --- a/src/applications/slowvote/view/SlowvoteEmbedView.php +++ b/src/applications/slowvote/view/SlowvoteEmbedView.php @@ -74,13 +74,9 @@ final class SlowvoteEmbedView extends AphrontView { $header = id(new PHUIHeaderView()) ->setHeader($link_to_slowvote); - $description = null; - if ($poll->getDescription()) { - $description = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent( - $poll->getDescription()), - 'default', - $this->getUser()); + $description = $poll->getDescription(); + if (strlen($description)) { + $description = new PHUIRemarkupView($this->getUser(), $description); $description = phutil_tag( 'div', array( diff --git a/src/applications/spaces/controller/PhabricatorSpacesViewController.php b/src/applications/spaces/controller/PhabricatorSpacesViewController.php index 7515b4dcc2..25bb70f646 100644 --- a/src/applications/spaces/controller/PhabricatorSpacesViewController.php +++ b/src/applications/spaces/controller/PhabricatorSpacesViewController.php @@ -83,15 +83,10 @@ final class PhabricatorSpacesViewController $description = $space->getDescription(); if (strlen($description)) { - $description = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($description), - 'default', - $viewer); - + $description = new PHUIRemarkupView($viewer, $description); $list->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); - $list->addTextContent($description); } diff --git a/src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php b/src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php index ad089d8f3d..add833c127 100644 --- a/src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php +++ b/src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php @@ -28,24 +28,10 @@ final class PhabricatorSubscriptionsEditController ->withPHIDs(array($phid)) ->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()) - ->setViewer($viewer) - ->withPHIDs(array($phid)) - ->executeOne(); - } + $object = id(new PhabricatorObjectQuery()) + ->setViewer($viewer) + ->withPHIDs(array($phid)) + ->executeOne(); if (!($object instanceof PhabricatorSubscribableInterface)) { return $this->buildErrorResponse( diff --git a/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentRawController.php b/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentRawController.php index 6670c147fe..65b6282aa9 100644 --- a/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentRawController.php +++ b/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentRawController.php @@ -50,10 +50,7 @@ final class PhabricatorApplicationTransactionCommentRawController $details_text = pht( 'For full details, run `/bin/mail show-outbound --id %d`', $source_id); - $addendum = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($details_text), - 'default', - $viewer); + $addendum = new PHUIRemarkupView($viewer, $details_text); } } } diff --git a/src/applications/transactions/controller/PhabricatorEditEngineController.php b/src/applications/transactions/controller/PhabricatorEditEngineController.php index b02bf8302f..d4b5e0e9f0 100644 --- a/src/applications/transactions/controller/PhabricatorEditEngineController.php +++ b/src/applications/transactions/controller/PhabricatorEditEngineController.php @@ -70,6 +70,8 @@ abstract class PhabricatorEditEngineController ->executeOne(); if ($config) { $engine = $config->getEngine(); + } else { + return null; } if (!$engine->isEngineConfigurable()) { diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index d6f70cac39..cbfaebb182 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -2607,14 +2607,19 @@ abstract class PhabricatorApplicationTransactionEditor PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); if ($project_phids) { - $watcher_type = PhabricatorObjectHasWatcherEdgeType::EDGECONST; + $projects = id(new PhabricatorProjectQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withPHIDs($project_phids) + ->needWatchers(true) + ->execute(); - $query = id(new PhabricatorEdgeQuery()) - ->withSourcePHIDs($project_phids) - ->withEdgeTypes(array($watcher_type)); - $query->execute(); + $watcher_phids = array(); + foreach ($projects as $project) { + foreach ($project->getAllAncestorWatcherPHIDs() as $phid) { + $watcher_phids[$phid] = $phid; + } + } - $watcher_phids = $query->getDestinationPHIDs(); if ($watcher_phids) { // We need to do a visibility check for all the watchers, as // watching a project is not a guarantee that you can see objects diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php index 6fa6a6458e..7d50f8495c 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -527,6 +527,11 @@ abstract class PhabricatorApplicationTransaction // TODO: Remove this eventually, this is handling old changes during // object creation prior to the introduction of "create" and "default" // transaction display flags. + + // NOTE: We can also hit this case with Space transactions that later + // update a default space (`null`) to an explicit space, so handling + // the Space case may require some finesse. + if ($this->getOldValue() === null) { return true; } else { diff --git a/src/applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php b/src/applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php index 0770bc6d3d..a771bf0747 100644 --- a/src/applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php +++ b/src/applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php @@ -121,11 +121,7 @@ final class PhabricatorTypeaheadFunctionHelpController } $content = implode("\n\n", $content); - - $content_box = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($content), - 'default', - $viewer); + $content_box = new PHUIRemarkupView($viewer, $content); $header = id(new PHUIHeaderView()) ->setHeader($title); diff --git a/src/applications/uiexample/examples/PhabricatorRemarkupUIExample.php b/src/applications/uiexample/examples/PhabricatorRemarkupUIExample.php index 3e338558fd..46f0262bbb 100644 --- a/src/applications/uiexample/examples/PhabricatorRemarkupUIExample.php +++ b/src/applications/uiexample/examples/PhabricatorRemarkupUIExample.php @@ -40,10 +40,7 @@ IMPORTANT: This is not really important. EOCONTENT ); - $remarkup = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($content), - 'default', - $viewer); + $remarkup = new PHUIRemarkupView($viewer, $content); $frame = id(new PHUIBoxView()) ->addPadding(PHUI::PADDING_LARGE) diff --git a/src/docs/user/userguide/diffusion_hooks.diviner b/src/docs/user/userguide/diffusion_hooks.diviner index db52c0667d..54c93a933b 100644 --- a/src/docs/user/userguide/diffusion_hooks.diviner +++ b/src/docs/user/userguide/diffusion_hooks.diviner @@ -42,7 +42,7 @@ These hooks act like normal `pre-commit` or `pre-receive` hooks: These additional variables will be available in the environment, in addition to the variables the VCS normally provides: - - `PHABRICATOR_REPOSITORY` The callsign of the repository the hook is + - `PHABRICATOR_REPOSITORY` The PHID of the repository the hook is executing for. - `PHABRICATOR_USER` The Phabricator username that the session is authenticated under. diff --git a/src/docs/user/userguide/diffusion_hosting.diviner b/src/docs/user/userguide/diffusion_hosting.diviner index 6427be92e5..f4baddf5a5 100644 --- a/src/docs/user/userguide/diffusion_hosting.diviner +++ b/src/docs/user/userguide/diffusion_hosting.diviner @@ -371,6 +371,41 @@ with `sudoers` configuration. is caused by SVN wiping the environment (including PATH) when invoking commit hooks. +No Direct Pushes +================ + +You may get an error about "No Direct Pushes" when trying to push. This means +you are pushing directly to the repository instead of pushing through +Phabricator. This is not supported: writes to hosted repositories must go +through Phabricator so it can perform authentication, enforce permissions, +write logs, proxy requests, apply rewriting, etc. + +One way to do a direct push by mistake is to use a `file:///` URI to interact +with the repository from the same machine. This is not supported. Instead, use +one of the repository URIs provided in the web interface, even if you're +working on the same machine. + +Another way to do a direct push is to misconfigure SSH (or not configure it at +all) so that none of the logic described above runs and you just connect +normally as a system user. In this case, the `ssh` test described above will +fail (you'll get a command prompt when you connect, instead of the message you +are supposed to get, as described above). + +If you encounter this error: make sure you're using a remote URI given to +you by Diffusion in the web interface, then run through the troubleshooting +steps above carefully. + +Sometimes users encounter this problem because they skip this whole document +assuming they don't need to configure anything. This will not work, and you +MUST configure things as described above for hosted repositories to work. + +The technical reason this error occurs is that the `PHABRICATOR_USER` variable +is not defined in the environment when commit hooks run. This variable is set +by Phabricator when a request passes through the authentication layer that this +document provides instructions for configuring. Its absence indicates that the +request did not pass through Phabricator. + + = Next Steps = Once hosted repositories are set up: diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php index be7db42004..7709233454 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php @@ -39,12 +39,7 @@ final class PhabricatorStandardCustomFieldRemarkup // end of the world. $viewer = $this->getViewer(); - return PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setContent($value) - ->setPReserveLinebreaks(true), - 'default', - $viewer); + return new PHUIRemarkupView($viewer, $value); } public function getApplicationTransactionTitle( diff --git a/src/infrastructure/markup/PhabricatorMarkupEngine.php b/src/infrastructure/markup/PhabricatorMarkupEngine.php index ed51b00175..c3d9dab02a 100644 --- a/src/infrastructure/markup/PhabricatorMarkupEngine.php +++ b/src/infrastructure/markup/PhabricatorMarkupEngine.php @@ -492,6 +492,9 @@ final class PhabricatorMarkupEngine extends Phobject { $rules[] = new PhabricatorYoutubeRemarkupRule(); } + $rules[] = new PhabricatorIconRemarkupRule(); + $rules[] = new PhabricatorEmojiRemarkupRule(); + $applications = PhabricatorApplication::getAllInstalledApplications(); foreach ($applications as $application) { foreach ($application->getRemarkupRules() as $rule) { diff --git a/src/infrastructure/markup/view/PHUIRemarkupView.php b/src/infrastructure/markup/view/PHUIRemarkupView.php index 8a11653589..8a8d9ddf7f 100644 --- a/src/infrastructure/markup/view/PHUIRemarkupView.php +++ b/src/infrastructure/markup/view/PHUIRemarkupView.php @@ -12,22 +12,41 @@ final class PHUIRemarkupView extends AphrontView { private $corpus; + private $markupType; + + const DOCUMENT = 'document'; public function __construct(PhabricatorUser $viewer, $corpus) { $this->setUser($viewer); $this->corpus = $corpus; } + private function setMarkupType($type) { + $this->markupType($type); + return $this; + } + public function render() { $viewer = $this->getUser(); $corpus = $this->corpus; - return PhabricatorMarkupEngine::renderOneObject( + $content = PhabricatorMarkupEngine::renderOneObject( id(new PhabricatorMarkupOneOff()) ->setPreserveLinebreaks(true) ->setContent($corpus), 'default', $viewer); + + if ($this->markupType == self::DOCUMENT) { + return phutil_tag( + 'div', + array( + 'class' => 'phabricator-remarkup phui-document-view', + ), + $content); + } + + return $content; } } diff --git a/src/view/form/AphrontFormView.php b/src/view/form/AphrontFormView.php index f9f281ff20..810208ffae 100644 --- a/src/view/form/AphrontFormView.php +++ b/src/view/form/AphrontFormView.php @@ -85,10 +85,8 @@ final class AphrontFormView extends AphrontView { public function appendRemarkupInstructions($remarkup) { return $this->appendInstructions( - PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($remarkup), - 'default', - $this->getUser())); + new PHUIRemarkupView($this->getUser(), $remarkup)); + } public function buildLayoutView() { diff --git a/src/view/form/PHUIFormLayoutView.php b/src/view/form/PHUIFormLayoutView.php index 1c5ccbb7f2..ffc0eb31d5 100644 --- a/src/view/form/PHUIFormLayoutView.php +++ b/src/view/form/PHUIFormLayoutView.php @@ -35,11 +35,10 @@ final class PHUIFormLayoutView extends AphrontView { throw new PhutilInvalidStateException('setUser'); } - return $this->appendInstructions( - PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($remarkup), - 'default', - $this->getUser())); + $viewer = $this->getUser(); + $instructions = new PHUIRemarkupView($viewer, $remarkup); + + return $this->appendInstructions($instructions); } public function render() { diff --git a/src/view/form/PHUIFormPageView.php b/src/view/form/PHUIFormPageView.php index cb62d33127..aeff9e38f5 100644 --- a/src/view/form/PHUIFormPageView.php +++ b/src/view/form/PHUIFormPageView.php @@ -74,11 +74,8 @@ class PHUIFormPageView extends AphrontView { } public function addRemarkupInstructions($remarkup, $before = null) { - return $this->addInstructions( - PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($remarkup), - 'default', - $this->getUser()), $before); + $remarkup = new PHUIRemarkupView($this->getUser(), $remarkup); + return $this->addInstructions($remarkup, $before); } public function addControl(AphrontFormControl $control) { diff --git a/src/view/phui/PHUIHeaderView.php b/src/view/phui/PHUIHeaderView.php index 50f0e36317..233e5a7c72 100644 --- a/src/view/phui/PHUIHeaderView.php +++ b/src/view/phui/PHUIHeaderView.php @@ -23,6 +23,7 @@ final class PHUIHeaderView extends AphrontTagView { private $actionIcons = array(); private $badges = array(); private $href; + private $actionList; public function setHeader($header) { $this->header = $header; @@ -84,6 +85,11 @@ final class PHUIHeaderView extends AphrontTagView { return $this; } + public function setActionList(PhabricatorActionListView $list) { + $this->actionList = $list; + return $this; + } + public function setPolicyObject(PhabricatorPolicyInterface $object) { $this->policyObject = $object; return $this; @@ -191,6 +197,17 @@ final class PHUIHeaderView extends AphrontTagView { protected function getTagContent() { + if ($this->actionList) { + $action_button = id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('Actions')) + ->setHref('#') + ->setIcon('fa-bars') + ->addClass('phui-mobile-menu') + ->setDropdownMenu($this->actionList); + $this->addActionLink($action_button); + } + $image = null; if ($this->image) { $image_href = null; diff --git a/src/view/phui/PHUIObjectBoxView.php b/src/view/phui/PHUIObjectBoxView.php index 32440a09cf..5cbb3a58b6 100644 --- a/src/view/phui/PHUIObjectBoxView.php +++ b/src/view/phui/PHUIObjectBoxView.php @@ -1,6 +1,6 @@ sigils[] = $sigil; - return $this; - } - - public function setMetadata(array $metadata) { - $this->metadata = $metadata; - return $this; - } + const BLUE = 'phui-box-blue'; + const GREY = 'phui-box-grey'; public function addPropertyList( PHUIPropertyListView $property_list, @@ -144,11 +137,6 @@ final class PHUIObjectBoxView extends AphrontView { return $this; } - public function setID($id) { - $this->id = $id; - return $this; - } - public function setHeader($header) { $this->header = $header; return $this; @@ -195,7 +183,109 @@ final class PHUIObjectBoxView extends AphrontView { return $this; } - public function render() { + public function willRender() { + $tab_lists = array(); + $property_lists = array(); + $tab_map = array(); + + $default_key = 'tab.default'; + + // Find the selected tab, or select the first tab if none are selected. + if ($this->tabs) { + $selected_tab = null; + foreach ($this->tabs as $key => $tab) { + if ($tab->getSelected()) { + $selected_tab = $key; + break; + } + } + if ($selected_tab === null) { + head($this->tabs)->setSelected(true); + $selected_tab = head_key($this->tabs); + } + } + + foreach ($this->propertyLists as $key => $list) { + $group = new PHUIPropertyGroupView(); + $i = 0; + foreach ($list as $item) { + $group->addPropertyList($item); + if ($i > 0) { + $item->addClass('phui-property-list-section-noninitial'); + } + $i++; + } + + if ($this->tabs && $key != $default_key) { + $tab_id = celerity_generate_unique_node_id(); + $tab_map[$key] = $tab_id; + + if ($key === $selected_tab) { + $style = null; + } else { + $style = 'display: none'; + } + + $tab_lists[] = phutil_tag( + 'div', + array( + 'style' => $style, + 'id' => $tab_id, + ), + $group); + } else { + if ($this->tabs) { + $group->addClass('phui-property-group-noninitial'); + } + $property_lists[] = $group; + } + $this->propertyList = $property_lists; + $this->tabMap = $tab_map; + $this->tabLists = $tab_lists; + } + } + + protected function getTagAttributes() { + $classes = array(); + $classes[] = 'phui-box'; + $classes[] = 'phui-box-border'; + $classes[] = 'phui-object-box'; + $classes[] = 'mlt mll mlr'; + + if ($this->color) { + $classes[] = 'phui-object-box-'.$this->color; + } + + if ($this->collapsed) { + $classes[] = 'phui-object-box-collapsed'; + } + + if ($this->flush) { + $classes[] = 'phui-object-box-flush'; + } + + if ($this->background) { + $classes[] = $this->background; + } + + $sigil = null; + $metadata = null; + if ($this->tabs) { + $sigil = 'phui-object-box'; + $metadata = array( + 'tabMap' => $this->tabMap, + ); + } + + return array( + 'class' => implode(' ', $classes), + 'sigil' => $sigil, + 'meta' => $metadata, + ); + } + + protected function getTagContent() { + require_celerity_resource('phui-box-css'); require_celerity_resource('phui-object-box-css'); $header = $this->header; @@ -296,63 +386,6 @@ final class PHUIObjectBoxView extends AphrontView { } } - $tab_lists = array(); - $property_lists = array(); - $tab_map = array(); - - $default_key = 'tab.default'; - - // Find the selected tab, or select the first tab if none are selected. - if ($this->tabs) { - $selected_tab = null; - foreach ($this->tabs as $key => $tab) { - if ($tab->getSelected()) { - $selected_tab = $key; - break; - } - } - if ($selected_tab === null) { - head($this->tabs)->setSelected(true); - $selected_tab = head_key($this->tabs); - } - } - - foreach ($this->propertyLists as $key => $list) { - $group = new PHUIPropertyGroupView(); - $i = 0; - foreach ($list as $item) { - $group->addPropertyList($item); - if ($i > 0) { - $item->addClass('phui-property-list-section-noninitial'); - } - $i++; - } - - if ($this->tabs && $key != $default_key) { - $tab_id = celerity_generate_unique_node_id(); - $tab_map[$key] = $tab_id; - - if ($key === $selected_tab) { - $style = null; - } else { - $style = 'display: none'; - } - - $tab_lists[] = phutil_tag( - 'div', - array( - 'style' => $style, - 'id' => $tab_id, - ), - $group); - } else { - if ($this->tabs) { - $group->addClass('phui-property-group-noninitial'); - } - $property_lists[] = $group; - } - } - $tabs = null; if ($this->tabs) { $tabs = id(new PHUIListView()) @@ -360,69 +393,28 @@ final class PHUIObjectBoxView extends AphrontView { foreach ($this->tabs as $tab) { $tabs->addMenuItem($tab); } - Javelin::initBehavior('phui-object-box-tabs'); } - $content = id(new PHUIBoxView()) - ->appendChild( - array( - ($this->showHideOpen == false ? $this->anchor : null), - $header, - $this->infoView, - $this->formErrors, - $this->formSaved, - $exception_errors, - $this->form, - $tabs, - $tab_lists, - $showhide, - ($this->showHideOpen == true ? $this->anchor : null), - $property_lists, - $this->table, - $this->renderChildren(), - )) - ->setBorder(true) - ->setID($this->id) - ->addMargin(PHUI::MARGIN_LARGE_TOP) - ->addMargin(PHUI::MARGIN_LARGE_LEFT) - ->addMargin(PHUI::MARGIN_LARGE_RIGHT) - ->addClass('phui-object-box'); - - if ($this->color) { - $content->addClass('phui-object-box-'.$this->color); - } - - if ($this->background) { - $content->setColor($this->background); - } - - if ($this->collapsed) { - $content->addClass('phui-object-box-collapsed'); - } - - if ($this->tabs) { - $content->addSigil('phui-object-box'); - $content->setMetadata( - array( - 'tabMap' => $tab_map, - )); - } - - if ($this->flush) { - $content->addClass('phui-object-box-flush'); - } - - foreach ($this->sigils as $sigil) { - $content->addSigil($sigil); - } - - if ($this->metadata !== null) { - $content->setMetadata($this->metadata); - } + $content = array( + ($this->showHideOpen == false ? $this->anchor : null), + $header, + $this->infoView, + $this->formErrors, + $this->formSaved, + $exception_errors, + $this->form, + $tabs, + $this->tabLists, + $showhide, + ($this->showHideOpen == true ? $this->anchor : null), + $this->propertyList, + $this->table, + $this->renderChildren(), + ); if ($this->objectList) { - $content->appendChild($this->objectList); + $content[] = $this->objectList; } return $content; diff --git a/src/view/phui/PHUITwoColumnView.php b/src/view/phui/PHUITwoColumnView.php index a01ce913b8..97ff269d83 100644 --- a/src/view/phui/PHUITwoColumnView.php +++ b/src/view/phui/PHUITwoColumnView.php @@ -5,6 +5,7 @@ final class PHUITwoColumnView extends AphrontTagView { private $mainColumn; private $sideColumn; private $display; + private $header; const DISPLAY_LEFT = 'phui-side-column-left'; const DISPLAY_RIGHT = 'phui-side-column-right'; @@ -19,6 +20,11 @@ final class PHUITwoColumnView extends AphrontTagView { return $this; } + public function setHeader(PHUIHeaderView $header) { + $this->header = $header; + return $this; + } + public function setDisplay($display) { $this->display = $display; return $this; @@ -35,7 +41,6 @@ final class PHUITwoColumnView extends AphrontTagView { protected function getTagAttributes() { $classes = array(); $classes[] = 'phui-two-column-view'; - $classes[] = 'grouped'; $classes[] = $this->getDisplay(); return array( @@ -66,6 +71,14 @@ final class PHUITwoColumnView extends AphrontTagView { $order = array($main, $side); } - return phutil_tag_div('phui-two-column-row', $order); + $inner = phutil_tag_div('phui-two-column-row', $order); + $table = phutil_tag_div('phui-two-column-content', $inner); + + $header = null; + if ($this->header) { + $header = phutil_tag_div('phui-two-column-header', $this->header); + } + + return array($header, $table); } } diff --git a/webroot/rsrc/css/aphront/dark-console.css b/webroot/rsrc/css/aphront/dark-console.css index e1df876dac..40dabd7bf6 100644 --- a/webroot/rsrc/css/aphront/dark-console.css +++ b/webroot/rsrc/css/aphront/dark-console.css @@ -31,7 +31,7 @@ .dark-console-panel, .dark-console-load { border-left: 1px solid #111111; - box-shadow: -2px 0px 2px rgba(0, 0, 0, 0.25); + box-shadow: -2px 0px 2px rgba({$alphablack}, 0.25); } .dark-console-requests { diff --git a/webroot/rsrc/css/aphront/notification.css b/webroot/rsrc/css/aphront/notification.css index 406f3a3143..1940309569 100644 --- a/webroot/rsrc/css/aphront/notification.css +++ b/webroot/rsrc/css/aphront/notification.css @@ -23,7 +23,7 @@ cursor: pointer; border-radius: 3px; - box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.25); + box-shadow: 0px 1px 2px rgba({$alphablack}, 0.25); margin-top: 4px; } diff --git a/webroot/rsrc/css/aphront/phabricator-nav-view.css b/webroot/rsrc/css/aphront/phabricator-nav-view.css index 52a977c13e..7dbb56da07 100644 --- a/webroot/rsrc/css/aphront/phabricator-nav-view.css +++ b/webroot/rsrc/css/aphront/phabricator-nav-view.css @@ -52,7 +52,7 @@ border-width: 0 1px 0 1px; border-color: #fff #999c9e #fff #999c9e; - box-shadow: inset -1px 0px 1px rgba(0, 0, 0, 0.15); + box-shadow: inset -1px 0px 1px rgba({$alphablack}, 0.15); background-image: url(/rsrc/image/divot.png); background-position: center; diff --git a/webroot/rsrc/css/aphront/tooltip.css b/webroot/rsrc/css/aphront/tooltip.css index caa21415d1..f2c3151373 100644 --- a/webroot/rsrc/css/aphront/tooltip.css +++ b/webroot/rsrc/css/aphront/tooltip.css @@ -9,7 +9,7 @@ .jx-tooltip-inner { position: relative; - background: rgba(0,0,0, .9); + background: rgba({$alphablack}, .9); border-radius: 3px; } @@ -28,7 +28,7 @@ width: 0; position: absolute; pointer-events: none; - border-color: rgba(0, 0, 0, 0); + border-color: rgba({$alphablack}, 0); border-width: 5px; } diff --git a/webroot/rsrc/css/aphront/typeahead.css b/webroot/rsrc/css/aphront/typeahead.css index 30ff456d74..0f63918e40 100644 --- a/webroot/rsrc/css/aphront/typeahead.css +++ b/webroot/rsrc/css/aphront/typeahead.css @@ -14,7 +14,7 @@ div.jx-typeahead-results { padding: 0; background: #fefefe; width: 98%; - box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); + box-shadow: 0px 1px 2px rgba({$alphablack}, 0.2); margin: -1px 1% 0; } diff --git a/webroot/rsrc/css/application/base/main-menu-view.css b/webroot/rsrc/css/application/base/main-menu-view.css index 92385e6dc2..2b8fcb1a0b 100644 --- a/webroot/rsrc/css/application/base/main-menu-view.css +++ b/webroot/rsrc/css/application/base/main-menu-view.css @@ -65,7 +65,7 @@ } .device-desktop .phabricator-main-menu-brand:hover { - background-color: rgba(55,55,55,.2); + background-color: rgba({$alphagrey},.2); cursor: hand; } diff --git a/webroot/rsrc/css/application/base/phabricator-application-launch-view.css b/webroot/rsrc/css/application/base/phabricator-application-launch-view.css index a4b97ce83f..714fa849e6 100644 --- a/webroot/rsrc/css/application/base/phabricator-application-launch-view.css +++ b/webroot/rsrc/css/application/base/phabricator-application-launch-view.css @@ -52,7 +52,7 @@ div.phabricator-application-launch-container { } .device-desktop a.phabricator-application-launch-container:hover { - background-color: rgba(0,0,0,.07); + background-color: rgba({$alphablack},.07); text-decoration: none; } diff --git a/webroot/rsrc/css/application/base/standard-page-view.css b/webroot/rsrc/css/application/base/standard-page-view.css index 4b53f61a6a..05686898b6 100644 --- a/webroot/rsrc/css/application/base/standard-page-view.css +++ b/webroot/rsrc/css/application/base/standard-page-view.css @@ -21,7 +21,7 @@ text-align: right; margin: 44px 16px 16px; padding: 12px 0; - border-top: 1px solid rgba(55,55,55,.1); + border-top: 1px solid rgba({$alphagrey},.1); color: {$greytext}; } diff --git a/webroot/rsrc/css/application/conpherence/durable-column.css b/webroot/rsrc/css/application/conpherence/durable-column.css index a416813fc3..d204592cf3 100644 --- a/webroot/rsrc/css/application/conpherence/durable-column.css +++ b/webroot/rsrc/css/application/conpherence/durable-column.css @@ -108,8 +108,8 @@ } .conpherence-durable-column-header { - border-left: 1px solid rgba(0,0,0,.1); - border-right: 1px solid rgba(0,0,0,.1); + border-left: 1px solid rgba({$alphablack},.1); + border-right: 1px solid rgba({$alphablack},.1); } .conpherence-durable-column-header-text { diff --git a/webroot/rsrc/css/application/conpherence/menu.css b/webroot/rsrc/css/application/conpherence/menu.css index 9918b89c71..49549c5738 100644 --- a/webroot/rsrc/css/application/conpherence/menu.css +++ b/webroot/rsrc/css/application/conpherence/menu.css @@ -117,7 +117,7 @@ } .conpherence-menu .conpherence-selected { - background: rgba(0,0,0,0.05); + background: rgba({$alphablack},0.05); border-left: 4px solid {$sky}; } @@ -127,7 +127,7 @@ .device-desktop .conpherence-menu .conpherence-selected.conpherence-menu-item-view:hover { - background-color: rgba(0,0,0,0.07); + background-color: rgba({$alphablack},0.07); } .conpherence-menu .loading { @@ -135,7 +135,7 @@ } .device-desktop .conpherence-menu .conpherence-menu-item-view:hover { - background-color: rgba(0,0,0,0.05); + background-color: rgba({$alphablack},0.05); } .conpherence-menu .conpherence-menu-item-view .conpherence-menu-item-image { diff --git a/webroot/rsrc/css/application/differential/phui-inline-comment.css b/webroot/rsrc/css/application/differential/phui-inline-comment.css index 02998aaaf0..0055f25699 100644 --- a/webroot/rsrc/css/application/differential/phui-inline-comment.css +++ b/webroot/rsrc/css/application/differential/phui-inline-comment.css @@ -340,7 +340,7 @@ } .ghost-icon { - background: rgba(55,55,55,.07); + background: rgba({$alphagrey},.07); float: left; padding: 2px 4px 2px 2px; position: absolute; @@ -389,7 +389,7 @@ } .inline-button-divider { - border-left: 1px solid rgba(55,55,55,.25); + border-left: 1px solid rgba({$alphagrey},.25); margin-left: 8px; } diff --git a/webroot/rsrc/css/application/diffusion/diffusion-readme.css b/webroot/rsrc/css/application/diffusion/diffusion-readme.css index 17f9baa04c..f7f8506e2d 100644 --- a/webroot/rsrc/css/application/diffusion/diffusion-readme.css +++ b/webroot/rsrc/css/application/diffusion/diffusion-readme.css @@ -2,13 +2,12 @@ * @provides diffusion-readme-css */ -.device .diffusion-readme-view { - margin: 16px 8px 0; - background-color: #fff; - border: 1px solid {$lightblueborder}; - border-bottom: 1px solid {$blueborder}; +.device-desktop .diffusion-readme-view .phui-document-fluid + .phui-document-view { + margin: 0; + padding: 0 8px; } -.device-tablet .diffusion-readme-view { - margin: 16px; +.diffusion-readme-view .phui-document-container { + border: none; } diff --git a/webroot/rsrc/css/application/files/global-drag-and-drop.css b/webroot/rsrc/css/application/files/global-drag-and-drop.css index 7539a046fb..e338b987b0 100644 --- a/webroot/rsrc/css/application/files/global-drag-and-drop.css +++ b/webroot/rsrc/css/application/files/global-drag-and-drop.css @@ -15,7 +15,7 @@ padding: 18px 0; color: #ffffff; - background: rgba(0, 0, 0, 0.8); + background: rgba({$alphablack}, 0.8); border-radius: 18px; } diff --git a/webroot/rsrc/css/application/people/people-profile.css b/webroot/rsrc/css/application/people/people-profile.css index d73b94fb97..c5ec73551a 100644 --- a/webroot/rsrc/css/application/people/people-profile.css +++ b/webroot/rsrc/css/application/people/people-profile.css @@ -38,7 +38,7 @@ form.compose-dialog { } .compose-dialog .compose-icon-bg { - color: rgba(255,255,255,0.8); + color: rgba({$alphawhite},0.8); line-height: 50px; width: 50px; text-align: center; diff --git a/webroot/rsrc/css/application/phame/phame.css b/webroot/rsrc/css/application/phame/phame.css index 3d151426d6..299474fee1 100644 --- a/webroot/rsrc/css/application/phame/phame.css +++ b/webroot/rsrc/css/application/phame/phame.css @@ -23,7 +23,7 @@ display: inline-block; background-repeat: no-repeat; background-size: 100%; - box-shadow: inset 0 0 0 1px rgba(55,55,55,.15); + box-shadow: inset 0 0 0 1px rgba({$alphagrey},.15); width: 40px; height: 40px; border-radius: 3px; @@ -31,7 +31,7 @@ } .phame-blog-description + .phui-property-list-section { - border-top: 1px solid rgba(71, 87, 120, 0.20); + border-top: 1px solid rgba({$alphablue}, 0.20); padding-top: 16px; } @@ -45,7 +45,7 @@ .phame-home-view { background-color: #fff; - border-bottom: 1px solid rgba(55,55,55,.1); + border-bottom: 1px solid rgba({$alphagrey},.1); } .phame-home-view .phame-home-container { @@ -100,7 +100,7 @@ display: inline-block; background-repeat: no-repeat; background-size: 100%; - box-shadow: inset 0 0 0 1px rgba(55,55,55,.15); + box-shadow: inset 0 0 0 1px rgba({$alphagrey},.15); width: 24px; height: 24px; border-radius: 3px; diff --git a/webroot/rsrc/css/application/pholio/pholio.css b/webroot/rsrc/css/application/pholio/pholio.css index 1cd8e604a1..52ff4ca5bd 100644 --- a/webroot/rsrc/css/application/pholio/pholio.css +++ b/webroot/rsrc/css/application/pholio/pholio.css @@ -116,8 +116,8 @@ } .pholio-mock-reticle-selection { - border: 1px solid rgba(0,0,0,.5); - box-shadow: 0 0 0 4px rgba(255,255,255,.5); + border: 1px solid rgba({$alphablack},.5); + box-shadow: 0 0 0 4px rgba({$alphawhite},.5); } .pholio-mock-comment-icon { @@ -132,21 +132,21 @@ .pholio-mock-reticle-draft .pholio-mock-comment-icon { font-size: 2.2em; color: {$yellow}; - text-shadow: 0 3px 8px rgba(0, 0, 0, 0.35); + text-shadow: 0 3px 8px rgba({$alphablack}, 0.35); -webkit-text-stroke: 1px white; } .pholio-mock-reticle-final .pholio-mock-comment-icon { font-size: 2.2em; color: {$pink}; - text-shadow: 0 3px 8px rgba(0, 0, 0, 0.35); + text-shadow: 0 3px 8px rgba({$alphablack}, 0.35); -webkit-text-stroke: 1px white; } .pholio-mock-reticle-draft:hover, .pholio-mock-reticle-final:hover { - border: 1px solid rgba(0,0,0,.5); - box-shadow: 0 0 0 4px rgba(255,255,255,.5); + border: 1px solid rgba({$alphablack},.5); + box-shadow: 0 0 0 4px rgba({$alphawhite},.5); cursor: pointer; } @@ -170,7 +170,7 @@ right: 0; font-size: 14px; padding: 4px 8px; - background: rgba(255,255,255,.6); + background: rgba({$alphawhite},.6); color: {$greytext}; } diff --git a/webroot/rsrc/css/application/ponder/ponder-view.css b/webroot/rsrc/css/application/ponder/ponder-view.css index f1182d531c..da0eab6e38 100644 --- a/webroot/rsrc/css/application/ponder/ponder-view.css +++ b/webroot/rsrc/css/application/ponder/ponder-view.css @@ -14,14 +14,6 @@ border-right: 1px solid {$lightblueborder}; } -.ponder-question-view .phui-property-list-properties-wrap { - width: 66%; -} - -.ponder-question-view .phui-property-list-actions { - width: 30%; -} - .ponder-answer-view { margin-top: 16px; } @@ -63,7 +55,7 @@ margin-right: 8px; color: {$anchor}; display: inline-block; - background-color: rgba(71, 87, 120, 0.06); + background-color: rgba({$alphablue}, 0.06); } .ponder-footer-view .ponder-footer-action.ponder-footer-action-helpful { @@ -82,5 +74,5 @@ .ponder-footer-view a:hover { text-decoration: none; - background-color: rgba(71, 87, 120, 0.10); + background-color: rgba({$alphablue}, 0.10); } diff --git a/webroot/rsrc/css/application/project/project-view.css b/webroot/rsrc/css/application/project/project-view.css index afbeff3950..f84790cf12 100644 --- a/webroot/rsrc/css/application/project/project-view.css +++ b/webroot/rsrc/css/application/project/project-view.css @@ -3,8 +3,8 @@ */ .project-view-home { - background: #fff; padding-bottom: 64px; + background: #fff; } .project-view-header-tag { @@ -24,18 +24,25 @@ color: {$bluetext}; } -.phui-box.phui-box-grey.project-view-properties { - margin: 0 16px 0 16px; +.project-view-home .phui-box.project-view-properties { + margin: 0 16px 16px 16px; padding: 4px 12px; + border: 2px solid rgba({$alphagrey},.1); + background-color: #F7F7F9; } -.device-phone .phui-box.phui-box-grey.project-view-properties { +.device-phone .phui-box.project-view-properties { margin: 0 12px 0 12px; } +.project-view-properties .phui-property-list-container + + .phui-property-list-text-content { + border-color: rgba({$alphagrey},.15); +} + .project-view-properties .phui-property-list-key { width: auto; - margin-left: 4px; + margin-left: 4px2 } .project-view-properties .phui-property-list-section-header { @@ -44,8 +51,8 @@ } .project-view-feed .phui-object-box.phui-box-border { + padding: 0 4px 8px 4px; border: none; - padding: 8px; } .project-view-feed .phui-object-box .phui-header-shell { @@ -67,7 +74,7 @@ } .project-view-home .phui-box-grey .phui-header-shell { - padding: 8px 8px 8px 12px; + padding: 6px 6px 6px 12px; } .project-view-home .phui-box-grey .phui-header-header { @@ -75,11 +82,15 @@ } .project-view-home .phui-box-grey .phui-object-item-list-view { - padding: 8px; + padding: 4px 8px 0 8px; } .device-desktop .phui-two-column-view.project-view-badges .phui-side-column { - width: 364px; + width: 366px; +} + +.project-view-badges .phui-badge-flex-view { + background-color: #fff; } .project-view-home .phui-box-grey .phui-object-item-attribute .phui-icon-view { diff --git a/webroot/rsrc/css/core/remarkup.css b/webroot/rsrc/css/core/remarkup.css index 3b9e51641f..7b537f5d67 100644 --- a/webroot/rsrc/css/core/remarkup.css +++ b/webroot/rsrc/css/core/remarkup.css @@ -30,7 +30,7 @@ padding: 6px 12px; font-size: 13px; font-weight: bold; - background: rgba(71,87,120,0.08); + background: rgba({$alphablue},0.08); display: inline-block; border-top-left-radius: 3px; border-top-right-radius: 3px; @@ -45,7 +45,7 @@ } .phabricator-remarkup .remarkup-code-block pre { - background: rgba(71,87,120,0.08); + background: rgba({$alphablue},0.08); display: block; color: #000; overflow: auto; @@ -59,7 +59,7 @@ .phabricator-remarkup tt.remarkup-monospaced { color: #000; - background: rgba(71,87,120,0.1); + background: rgba({$alphablue},0.1); padding: 1px 4px; border-radius: 3px; white-space: pre-wrap; @@ -97,17 +97,19 @@ list-style: lower-roman; } -.phabricator-remarkup ul.remarkup-list-with-checkmarks { +.phabricator-remarkup .remarkup-list-with-checkmarks .remarkup-checked-item, +.phabricator-remarkup .remarkup-list-with-checkmarks .remarkup-unchecked-item { list-style: none; - margin-left: 18px; + margin-left: -18px; } .phabricator-remarkup .remarkup-list-with-checkmarks input { margin-right: 2px; + opacity: 1; } .phabricator-remarkup .remarkup-list-with-checkmarks .remarkup-checked-item { - color: {$greytext}; + text-decoration: line-through; } .phabricator-remarkup ul.remarkup-list ol.remarkup-list, @@ -328,7 +330,7 @@ .phabricator-remarkup-embed-image { display: inline-block; border: 3px solid white; - box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.20); + box-shadow: 1px 1px 2px rgba({$alphablack}, 0.20); } .phabricator-remarkup-embed-image-full { diff --git a/webroot/rsrc/css/layout/phabricator-side-menu-view.css b/webroot/rsrc/css/layout/phabricator-side-menu-view.css index 5b425c2a5f..77c0b7cf39 100644 --- a/webroot/rsrc/css/layout/phabricator-side-menu-view.css +++ b/webroot/rsrc/css/layout/phabricator-side-menu-view.css @@ -19,7 +19,7 @@ } .phabricator-basic-nav .phabricator-side-menu .phui-list-item-selected { - background-color: rgba(0,0,0,.05); + background-color: rgba({$alphablack},.05); border-left: 4px solid {$sky}; border-top-right-radius: 3px; border-bottom-right-radius: 3px; @@ -29,7 +29,7 @@ .device-desktop .phabricator-basic-nav .phabricator-side-menu .phui-list-item-selected a.phui-list-item-href:hover { - background-color: rgba(0,0,0,.05); + background-color: rgba({$alphablack},.05); } .phabricator-basic-nav .phabricator-side-menu .phui-list-item-selected @@ -49,5 +49,5 @@ .device-desktop .phabricator-basic-nav .phabricator-side-menu a.phui-list-item-href:hover { text-decoration: none; - background-color: rgba(0,0,0,.07); + background-color: rgba({$alphablack},.07); } diff --git a/webroot/rsrc/css/phui/phui-action-panel.css b/webroot/rsrc/css/phui/phui-action-panel.css index 6d7c566de7..8cfd69a94f 100644 --- a/webroot/rsrc/css/phui/phui-action-panel.css +++ b/webroot/rsrc/css/phui/phui-action-panel.css @@ -22,7 +22,7 @@ .device-desktop .phui-action-panel-hitarea:hover { text-decoration: none; - background-color: rgba(255,255,255,.25); + background-color: rgba({$alphawhite},.25); } .device-desktop .phui-action-panel-hitarea:hover .phui-icon-view { diff --git a/webroot/rsrc/css/phui/phui-badge.css b/webroot/rsrc/css/phui/phui-badge.css index f45a88f258..fdb396b539 100644 --- a/webroot/rsrc/css/phui/phui-badge.css +++ b/webroot/rsrc/css/phui/phui-badge.css @@ -88,7 +88,7 @@ .phui-badge-illustration { background-color: #fff; - box-shadow: inset 0 -1px 1px 0 rgba(0,0,0,.1); + box-shadow: inset 0 -1px 1px 0 rgba({$alphablack},.1); text-align: center; height: 100px; width: 100%; diff --git a/webroot/rsrc/css/phui/phui-box.css b/webroot/rsrc/css/phui/phui-box.css index 16be92ad48..1516285cf8 100644 --- a/webroot/rsrc/css/phui/phui-box.css +++ b/webroot/rsrc/css/phui/phui-box.css @@ -13,15 +13,15 @@ } .phui-box-grey { - background-color: {$page.background}; + background-color: #F7F7F9; border-radius: 3px; - border: none; + border-color: rgba({$alphagrey},.1); } .phui-box-blue { background-color: {$bluebackground}; border-radius: 3px; - border: none; + border-color: {$thinblueborder}; } .phui-box-blue .phui-object-item, @@ -34,15 +34,23 @@ color: {$darkbluetext}; } -.phui-box-blue .phui-header-shell, +.phui-box-blue .phui-object-item-list-view, +.phui-box-grey .phui-object-item-list-view { + background-color: #fff; +} + +.phui-box-blue .phui-header-shell { + border-color: {$thinblueborder}; +} + .phui-box-grey .phui-header-shell { - border-bottom: 2px solid #fff; + border-color: rgba({$alphagrey},.1); } .phui-object-box.phui-box-blue div.phui-info-severity-nodata, .phui-object-box.phui-box-grey div.phui-info-severity-nodata { background: transparent; - padding: 12px 4px; + padding: 20px 4px 24px; text-align: center; border: none; color: {$greytext}; diff --git a/webroot/rsrc/css/phui/phui-button.css b/webroot/rsrc/css/phui/phui-button.css index 958deb7f67..0ba3007e5f 100644 --- a/webroot/rsrc/css/phui/phui-button.css +++ b/webroot/rsrc/css/phui/phui-button.css @@ -59,8 +59,8 @@ button.grey, input[type="submit"].grey, a.grey, a.grey:visited { - background-color: rgba(71, 87, 120, 0.06); - border-color: rgba(71, 87, 120, 0.12); + background-color: #F7F7F9; + border: 1px solid rgba({$alphagrey},.1); color: {$darkgreytext}; } @@ -106,8 +106,8 @@ button:hover { a.button.grey:hover, button.grey:hover { - background-color: rgba(71, 87, 120, 0.12); - border-color: rgba(71, 87, 120, 0.2); + background-color: rgba({$alphablue}, 0.1); + border-color: rgba({$alphablue}, 0.15); transition: 0.1s; } @@ -315,7 +315,7 @@ a.policy-control .caret { .phui-button-bar-borderless .button .phui-icon-view { font-size: 15px; - color: rgba(55,55,55,.4); + color: rgba({$alphagrey},.4); } .phui-button-bar-borderless .button:hover { @@ -324,7 +324,7 @@ a.policy-control .caret { } .phui-button-bar-borderless .button:hover .phui-icon-view { - color: rgba(55,55,55,.9); + color: rgba({$alphagrey},.9); } .phui-button-bar-borderless .button { diff --git a/webroot/rsrc/css/phui/phui-crumbs-view.css b/webroot/rsrc/css/phui/phui-crumbs-view.css index 81c8471c4f..47fd0a4da9 100644 --- a/webroot/rsrc/css/phui/phui-crumbs-view.css +++ b/webroot/rsrc/css/phui/phui-crumbs-view.css @@ -105,7 +105,7 @@ a.phui-crumbs-action .phui-crumbs-action-name { } .phui-crumbs-view.phui-crumbs-border { - border-bottom: 1px solid rgba(55,55,55,.1); + border-bottom: 1px solid rgba({$alphagrey},.1); } body .phui-crumbs-view + .phui-object-box { diff --git a/webroot/rsrc/css/phui/phui-document-pro.css b/webroot/rsrc/css/phui/phui-document-pro.css index 90f268cf66..138eec0955 100644 --- a/webroot/rsrc/css/phui/phui-document-pro.css +++ b/webroot/rsrc/css/phui/phui-document-pro.css @@ -140,7 +140,7 @@ a.button.phui-document-toc { .phui-document-view-pro-box .phui-timeline-view { padding: 16px 0 0 0; background: none; - border-top: 1px solid rgba(71, 87, 120, 0.20); + border-top: 1px solid rgba({$alphablue}, 0.20); } .phui-document-view-pro-box .phui-timeline-image { diff --git a/webroot/rsrc/css/phui/phui-document-summary.css b/webroot/rsrc/css/phui/phui-document-summary.css index 5b09bb9c7f..736efe9d20 100644 --- a/webroot/rsrc/css/phui/phui-document-summary.css +++ b/webroot/rsrc/css/phui/phui-document-summary.css @@ -4,7 +4,7 @@ .phui-document-summary-view { margin-top: 8px; - border-bottom: 1px solid rgba(55,55,55,.1); + border-bottom: 1px solid rgba({$alphagrey},.1); } .phui-document-summary-view.is-draft { diff --git a/webroot/rsrc/css/phui/phui-form.css b/webroot/rsrc/css/phui/phui-form.css index 52f41e4ab6..5c2ef38446 100644 --- a/webroot/rsrc/css/phui/phui-form.css +++ b/webroot/rsrc/css/phui/phui-form.css @@ -48,9 +48,9 @@ div.jx-tokenizer-container { background-color: #ffffff; border: 1px solid {$blueborder}; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-box-shadow: inset 0 1px 1px rgba({$alphablack}, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba({$alphablack}, 0.075); + box-shadow: inset 0 1px 1px rgba({$alphablack}, 0.075); -webkit-transition: border linear .05s, box-shadow linear .05s; -moz-transition: border linear .05s, box-shadow linear .05s; @@ -87,13 +87,13 @@ div.jx-tokenizer-container-focused { /* IE6-9 */ -webkit-box-shadow: - inset 0 1px 1px rgba(0,0,0,.075), + inset 0 1px 1px rgba({$alphablack},.075), 0 0 8px rgba(82,168,236,.6); -moz-box-shadow: - inset 0 1px 1px rgba(0,0,0,.075), + inset 0 1px 1px rgba({$alphablack},.075), 0 0 8px rgba(82,168,236,.6); box-shadow: - inset 0 1px 1px rgba(0,0,0,.075), + inset 0 1px 1px rgba({$alphablack},.075), 0 0 8px rgba(82,168,236,.6); } input[type="radio"], diff --git a/webroot/rsrc/css/phui/phui-header-view.css b/webroot/rsrc/css/phui/phui-header-view.css index 63852820df..5dc0586667 100644 --- a/webroot/rsrc/css/phui/phui-header-view.css +++ b/webroot/rsrc/css/phui/phui-header-view.css @@ -146,7 +146,7 @@ body .phui-header-shell.phui-bleed-header display: block; position: absolute; left: 0; - background: rgba(0,0,0,0.4); + background: rgba({$alphablack},0.4); color: #fff; font-weight: normal; bottom: 4px; @@ -179,7 +179,7 @@ body .phui-header-shell.phui-bleed-header .phui-header-subheader .phui-header-status { padding: 3px 9px; border-radius: 3px; - background: rgba(71, 87, 120, 0.06); + background: rgba({$alphablue}, 0.06); margin-right: 8px; } diff --git a/webroot/rsrc/css/phui/phui-image-mask.css b/webroot/rsrc/css/phui/phui-image-mask.css index a8b2212ce4..70350aa488 100644 --- a/webroot/rsrc/css/phui/phui-image-mask.css +++ b/webroot/rsrc/css/phui/phui-image-mask.css @@ -17,7 +17,7 @@ } .phui-image-mask-mask { - border: 300px solid rgba(0, 0, 0, 0.5); + border: 300px solid rgba({$alphablack}, 0.5); background-clip: content-box; position: absolute; } diff --git a/webroot/rsrc/css/phui/phui-info-view.css b/webroot/rsrc/css/phui/phui-info-view.css index cb7c7e77ba..59405995a5 100644 --- a/webroot/rsrc/css/phui/phui-info-view.css +++ b/webroot/rsrc/css/phui/phui-info-view.css @@ -24,7 +24,7 @@ .phui-info-view-body tt { padding: 0 2px; - background-color: rgba(55,55,55,.1); + background-color: rgba({$alphagrey},.1); } .phui-info-view-actions { diff --git a/webroot/rsrc/css/phui/phui-object-item-list-view.css b/webroot/rsrc/css/phui/phui-object-item-list-view.css index 913975c6eb..17894f6f9d 100644 --- a/webroot/rsrc/css/phui/phui-object-item-list-view.css +++ b/webroot/rsrc/css/phui/phui-object-item-list-view.css @@ -29,7 +29,6 @@ ul.phui-object-item-list-view { } .phui-object-box .phui-object-item-list-view .phui-info-view { - margin: 4px 0; color: {$greytext}; border: none; } diff --git a/webroot/rsrc/css/phui/phui-profile-menu.css b/webroot/rsrc/css/phui/phui-profile-menu.css index 76a51d9130..30855f0a05 100644 --- a/webroot/rsrc/css/phui/phui-profile-menu.css +++ b/webroot/rsrc/css/phui/phui-profile-menu.css @@ -34,7 +34,7 @@ .phui-profile-menu .phabricator-side-menu { background: #525867; - box-shadow: inset -2px 0 2px rgba(0, 0, 0, 0.150); + box-shadow: inset -2px 0 2px rgba({$alphablack}, 0.150); width: 240px; } @@ -107,7 +107,7 @@ .device-desktop .phui-profile-menu .phabricator-side-menu .phui-list-item-href:hover { - background-color: rgba(0,0,0,0.15); + background-color: rgba({$alphablack},0.15); color: {$menu.profile.text.selected}; } @@ -122,18 +122,18 @@ .phui-profile-menu .phabricator-side-menu .phui-list-item-selected .phui-list-item-href { - background-color: rgba(0,0,0,0.3); + background-color: rgba({$alphablack},0.3); color: {$menu.profile.text.selected}; } .phui-profile-menu .phabricator-side-menu .phui-list-item-selected .phui-list-item-href:hover { - background-color: rgba(0,0,0,0.45); + background-color: rgba({$alphablack},0.45); } .phui-profile-menu .phabricator-side-menu .phui-divider { margin: 4px 0; - border-bottom: 1px solid rgba(0, 0, 0, 0.2); + border-bottom: 1px solid rgba({$alphablack}, 0.2); } .phui-profile-menu .phabricator-side-menu .phui-motivator { @@ -150,11 +150,18 @@ } .phui-profile-menu .phabricator-side-menu .phui-profile-menu-error { - color: {$greytext}; + color: rgba({$alphawhite}, 0.5); font-size: {$smallerfontsize}; padding: 18px 15px; } +.phui-profile-menu .phabricator-side-menu .phui-list-item-disabled + .phui-list-item-href, +.phui-profile-menu .phui-list-sidenav .phui-list-item-disabled + .phui-list-item-href { + color: rgba({$alphawhite}, 0.5); +} + .phui-profile-menu .phabricator-side-menu .phui-profile-segment-bar { color: {$menu.profile.text}; font-size: {$smallerfontsize}; diff --git a/webroot/rsrc/css/phui/phui-tag-view.css b/webroot/rsrc/css/phui/phui-tag-view.css index c6bb18ed0a..a68ed19484 100644 --- a/webroot/rsrc/css/phui/phui-tag-view.css +++ b/webroot/rsrc/css/phui/phui-tag-view.css @@ -16,7 +16,7 @@ a.phui-tag-view:hover { .phui-tag-core-closed { text-decoration: line-through; - color: rgba(0,0,0,0.5); + color: rgba({$alphablack},0.5); } .phui-tag-core-closed:hover { diff --git a/webroot/rsrc/css/phui/phui-two-column-view.css b/webroot/rsrc/css/phui/phui-two-column-view.css index 21c4c36393..e08bd5a603 100644 --- a/webroot/rsrc/css/phui/phui-two-column-view.css +++ b/webroot/rsrc/css/phui/phui-two-column-view.css @@ -2,7 +2,7 @@ * @provides phui-two-column-view-css */ -.phui-two-column-view { +.phui-two-column-content { display: table; width: 100%; } diff --git a/webroot/rsrc/css/phui/workboards/phui-workboard-color.css b/webroot/rsrc/css/phui/workboards/phui-workboard-color.css new file mode 100644 index 0000000000..d983622a99 --- /dev/null +++ b/webroot/rsrc/css/phui/workboards/phui-workboard-color.css @@ -0,0 +1,152 @@ +/** + * @provides phui-workboard-color-css + */ + +.phui-workboard-color .phabricator-nav-content .phui-workboard-view-shadow { + background-color: transparent; +} + +.phui-workboard-color .phui-crumbs-view { + background-color: rgba({$alphagrey},.15); + border: none; + color: rgba({$alphawhite},.8); +} + +.phui-workboard-color .phui-crumbs-view a, +.phui-workboard-color .phui-crumbs-view .phui-icon-view { + color: rgba({$alphawhite},.85); +} + +.phui-workboard-color .phui-crumbs-view a:hover, +.phui-workboard-color .phui-crumbs-view a:hover .phui-icon-view { + color: #fff; +} + +.phui-workboard-color .phuix-dropdown-menu { + background-color: rgba({$alphawhite},.9); +} + +.phui-workboard-color .phui-workpanel-view .phui-box-grey { + background-color: rgba({$alphawhite},.6); +} + +body.phui-workboard-color .phui-profile-menu .phabricator-side-menu { + background-color: rgba({$alphagrey},.3); +} + +body.phui-workboard-color .phabricator-side-menu .phui-profile-menu-footer-1 { + background-color: transparent; +} + +.phui-workboard-color .phui-profile-menu .phabricator-side-menu { + box-shadow: none; +} + +.phui-workboard-color-preview { + width: 50px; + height: 50px; + font-size: 34px; +} + +/* Gradients */ + +.phui-workboard-gradient-red { + background-color: #e53935; + background-image: linear-gradient(to left, #e53935, #e35d5b); +} + +.phui-workboard-gradient-orange { + background-color: #f46b45; + background-image: linear-gradient(to left, #f46b45, #eea849); +} + +.phui-workboard-gradient-yellow { + background-color: #FF8008; + background-image: linear-gradient(to left, #FF8008, #FFC837); +} + +.phui-workboard-gradient-green { + background-color: #2fa0ac; + background-image: linear-gradient(90deg, #2fa0ac 0, #58cca6); +} + +.phui-workboard-gradient-blue { + background-color: #73a5c3; + background-image: linear-gradient(90deg, #73a5c3 0, #6875be); +} + +.phui-workboard-gradient-bluegrey { + background-color: #517fa4; + background-image: linear-gradient(to left, #517fa4, #243949); +} + +.phui-workboard-gradient-indigo { + background-color: #4776E6; + background-image: linear-gradient(to left, #4776E6, #8E54E9); +} + +.phui-workboard-gradient-violet { + background-color: #9f73c3; + background-image: linear-gradient(90deg, #9f73c3 0, #6875be); +} + +.phui-workboard-gradient-sky { + background-color: #7474BF; + background-image: linear-gradient(to left, #7474BF, #348AC7); +} + +.phui-workboard-gradient-pink { + background-color: #EA2A90; + background-image: linear-gradient(to left, #EA2A90, #79164B); +} + +.phui-workboard-gradient-grey { + background-color: #283048; + background-image: linear-gradient(to left, #283048, #859398); +} + +/* Colors */ + +.phui-workboard-red { + background-color: {$red}; +} + +.phui-workboard-orange { + background-color: {$orange}; +} + +.phui-workboard-yellow { + background-color: {$yellow}; +} + +.phui-workboard-green { + background-color: {$green}; +} + +.phui-workboard-blue { + background-color: {$blue}; +} + +.phui-workboard-indigo { + background-color: {$indigo}; +} + +.phui-workboard-violet { + background-color: {$violet}; +} + +.phui-workboard-sky { + background-color: {$sky}; +} + +.phui-workboard-pink { + background-color: {$pink}; +} + +.phui-workboard-fire { + background-color: {$fire}; +} + +.phui-workboard-grey { + background-color: {$greytext}; +} diff --git a/webroot/rsrc/css/phui/workboards/phui-workboard.css b/webroot/rsrc/css/phui/workboards/phui-workboard.css index 36913d1663..9ba75661d1 100644 --- a/webroot/rsrc/css/phui/workboards/phui-workboard.css +++ b/webroot/rsrc/css/phui/workboards/phui-workboard.css @@ -18,8 +18,8 @@ bottom: 0; left: 0; right: 0; - background: #fff; padding: 16px; + background-color: #fff; } .device-desktop .page-has-warning .phui-workboard-view-shadow { diff --git a/webroot/rsrc/css/phui/workboards/phui-workcard.css b/webroot/rsrc/css/phui/workboards/phui-workcard.css index d35ac11628..f3d92d63e5 100644 --- a/webroot/rsrc/css/phui/workboards/phui-workcard.css +++ b/webroot/rsrc/css/phui/workboards/phui-workcard.css @@ -42,7 +42,7 @@ } .phui-object-item-disabled.phui-workcard { - background-color: rgba(255,255,255,.67); + background-color: rgba({$alphawhite},.67); } .phui-object-item-disabled.phui-workcard .phui-object-item-link { diff --git a/webroot/rsrc/css/phui/workboards/phui-workpanel.css b/webroot/rsrc/css/phui/workboards/phui-workpanel.css index 77bddf9f0f..899c6f74eb 100644 --- a/webroot/rsrc/css/phui/workboards/phui-workpanel.css +++ b/webroot/rsrc/css/phui/workboards/phui-workpanel.css @@ -36,7 +36,7 @@ } .phui-workpanel-view .phui-box-grey { - background-color: rgba(71,87,120,0.1); + background-color: rgba({$alphablue},0.1); } .phui-workpanel-view.phui-workboard-column-milestone .phui-box-grey { @@ -85,6 +85,7 @@ .phui-workpanel-body .phui-object-item-list-view { min-height: 54px; + background-color: transparent; } .device .aphront-multi-column-outer @@ -105,17 +106,17 @@ .device-desktop .phui-workpanel-body-content::-webkit-scrollbar { height: 8px; width: 8px; - background: rgba(71,87,120,0.2); + background: rgba({$alphablue},0.2); border-radius: 4px; } .device-desktop .phui-workpanel-body-content::-webkit-scrollbar-thumb { - background: rgba(71,87,120,0.4); + background: rgba({$alphablue},0.4); border-radius: 4px; } .project-panel-empty .phui-object-item-list-view { - background: {$sh-indigobackground}; + background: rgba(234, 230, 247, 0.85); border-radius: 3px; margin-bottom: 4px; border: 1px dashed {$sh-indigoborder}; @@ -126,7 +127,7 @@ } .project-panel-empty .phui-object-item-list-view.drag-target-list { - background: rgba(255,255,255,.7); + background: rgba({$alphawhite},.7); } .phui-workpanel-view.project-panel-over-limit .phui-header-header { diff --git a/webroot/rsrc/js/core/TextAreaUtils.js b/webroot/rsrc/js/core/TextAreaUtils.js index f8c84c97a7..8f8a81067b 100644 --- a/webroot/rsrc/js/core/TextAreaUtils.js +++ b/webroot/rsrc/js/core/TextAreaUtils.js @@ -33,7 +33,14 @@ JX.install('TextAreaUtils', { setSelectionRange : function(area, start, end) { if ('setSelectionRange' in area) { + + // Chrome scrolls the textarea to the bottom as a side effect of + // calling focus(), so save the scroll position, focus, then restore + // the scroll position. + var scroll_top = area.scrollTop; area.focus(); + area.scrollTop = scroll_top; + area.setSelectionRange(start, end); } }, diff --git a/webroot/rsrc/js/core/behavior-tooltip.js b/webroot/rsrc/js/core/behavior-tooltip.js index fd88956aef..8b9b8d4f23 100644 --- a/webroot/rsrc/js/core/behavior-tooltip.js +++ b/webroot/rsrc/js/core/behavior-tooltip.js @@ -18,7 +18,7 @@ JX.behavior('phabricator-tooltips', function() { return; } - if (JX.Device.getDevice() != 'desktop') { + if (e.getIsTouchEvent()) { return; }