diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 9cdac5d965..ca33f2d429 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,8 +7,10 @@ */ return array( 'names' => array( - 'core.pkg.css' => 'ad6a3591', - 'core.pkg.js' => '2b8af4e4', + 'conpherence.pkg.css' => '80a3fcb3', + 'conpherence.pkg.js' => '89b4837e', + 'core.pkg.css' => '476e9330', + 'core.pkg.js' => '1d376fa9', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '3fb7f532', 'differential.pkg.js' => '634399e9', @@ -18,7 +20,7 @@ return array( 'maniphest.pkg.js' => '949a7498', 'rsrc/css/aphront/aphront-bars.css' => '231ac33c', 'rsrc/css/aphront/dark-console.css' => 'f54bf286', - 'rsrc/css/aphront/dialog-view.css' => '913c172e', + 'rsrc/css/aphront/dialog-view.css' => '593d3f67', 'rsrc/css/aphront/lightbox-attachment.css' => '7acac05d', 'rsrc/css/aphront/list-filter-view.css' => '5d6f0526', 'rsrc/css/aphront/multi-column.css' => 'fd18389d', @@ -32,7 +34,7 @@ return array( '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' => 'e862571a', + 'rsrc/css/application/base/main-menu-view.css' => 'f03e17be', 'rsrc/css/application/base/notification-menu.css' => 'b3ab500d', 'rsrc/css/application/base/phabricator-application-launch-view.css' => '95351601', 'rsrc/css/application/base/phui-theme.css' => '027ba77e', @@ -41,17 +43,17 @@ return array( 'rsrc/css/application/conduit/conduit-api.css' => '7bc725c4', 'rsrc/css/application/config/config-options.css' => '0ede4c9b', 'rsrc/css/application/config/config-page.css' => '8798e14f', - 'rsrc/css/application/config/config-template.css' => '8e6c6fcd', - 'rsrc/css/application/config/config-welcome.css' => '035aa483', + 'rsrc/css/application/config/config-template.css' => '8f18fa41', 'rsrc/css/application/config/setup-issue.css' => 'f794cfc3', 'rsrc/css/application/config/unhandled-exception.css' => '4c96257a', - 'rsrc/css/application/conpherence/durable-column.css' => '86396117', - 'rsrc/css/application/conpherence/menu.css' => '90bdf85c', - 'rsrc/css/application/conpherence/message-pane.css' => '5c7b7b17', + 'rsrc/css/application/conpherence/durable-column.css' => '194ac487', + 'rsrc/css/application/conpherence/header-pane.css' => '517de9fe', + 'rsrc/css/application/conpherence/menu.css' => '78c7b811', + 'rsrc/css/application/conpherence/message-pane.css' => '8d13ac4d', 'rsrc/css/application/conpherence/notification.css' => '6cdcc253', - 'rsrc/css/application/conpherence/transaction.css' => '85d0974c', - 'rsrc/css/application/conpherence/update.css' => 'faf6be09', - 'rsrc/css/application/conpherence/widget-pane.css' => 'c5b74f9e', + 'rsrc/css/application/conpherence/participant-pane.css' => '7bba0b56', + 'rsrc/css/application/conpherence/transaction.css' => '46253e19', + 'rsrc/css/application/conpherence/update.css' => '53bc527a', 'rsrc/css/application/contentsource/content-source-view.css' => '4b8b05d4', 'rsrc/css/application/countdown/timer.css' => '16c52f5c', 'rsrc/css/application/daemon/bulk-job.css' => 'df9c1d4a', @@ -95,7 +97,7 @@ return array( 'rsrc/css/application/policy/policy.css' => '957ea14c', 'rsrc/css/application/ponder/ponder-view.css' => 'fbd45f96', 'rsrc/css/application/project/project-card-view.css' => '9418c97d', - 'rsrc/css/application/project/project-view.css' => '9ce99f21', + 'rsrc/css/application/project/project-view.css' => '55d99221', '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', @@ -106,9 +108,9 @@ return array( 'rsrc/css/application/tokens/tokens.css' => '3d0f239e', 'rsrc/css/application/uiexample/example.css' => '528b19de', 'rsrc/css/core/core.css' => 'd0801452', - 'rsrc/css/core/remarkup.css' => '5ed06ed8', + 'rsrc/css/core/remarkup.css' => 'cd912f2c', 'rsrc/css/core/syntax.css' => '769d3498', - 'rsrc/css/core/z-index.css' => '2b01a823', + 'rsrc/css/core/z-index.css' => '0d4e5558', 'rsrc/css/diviner/diviner-shared.css' => 'aa3656aa', 'rsrc/css/font/font-aleo.css' => '8bdb2835', 'rsrc/css/font/font-awesome.css' => '2b7ebbcc', @@ -129,9 +131,9 @@ return array( 'rsrc/css/phui/phui-button.css' => '4a5fbe3d', 'rsrc/css/phui/phui-chart.css' => '6bf6f78e', 'rsrc/css/phui/phui-cms.css' => 'be43c8a8', - 'rsrc/css/phui/phui-crumbs-view.css' => '9dac418c', - 'rsrc/css/phui/phui-curtain-view.css' => '7148ae25', - 'rsrc/css/phui/phui-document-pro.css' => 'dc3d46ed', + 'rsrc/css/phui/phui-crumbs-view.css' => '195ac419', + 'rsrc/css/phui/phui-curtain-view.css' => '947bf1a4', + 'rsrc/css/phui/phui-document-pro.css' => 'ca1fed81', 'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf', 'rsrc/css/phui/phui-document.css' => 'c32e8dec', 'rsrc/css/phui/phui-feed-story.css' => 'aa49845d', @@ -142,10 +144,11 @@ return array( 'rsrc/css/phui/phui-header-view.css' => '06385974', 'rsrc/css/phui/phui-hovercard.css' => 'de1a2119', 'rsrc/css/phui/phui-icon-set-selector.css' => '1ab67aad', - 'rsrc/css/phui/phui-icon.css' => 'b1dbd620', + 'rsrc/css/phui/phui-icon.css' => '9bab6f02', 'rsrc/css/phui/phui-image-mask.css' => 'a8498f9c', 'rsrc/css/phui/phui-info-panel.css' => '27ea50a1', 'rsrc/css/phui/phui-info-view.css' => '28efab79', + 'rsrc/css/phui/phui-invisible-character-view.css' => '6993d9f0', 'rsrc/css/phui/phui-list.css' => '9da2aa00', 'rsrc/css/phui/phui-object-box.css' => '6b487c57', 'rsrc/css/phui/phui-object-item-list-view.css' => '87278fa0', @@ -159,12 +162,12 @@ return array( 'rsrc/css/phui/phui-status.css' => 'd5263e49', 'rsrc/css/phui/phui-tag-view.css' => '6bbd83e2', 'rsrc/css/phui/phui-timeline-view.css' => 'bc523970', - 'rsrc/css/phui/phui-two-column-view.css' => '5afdf637', + 'rsrc/css/phui/phui-two-column-view.css' => 'fcfbe347', 'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7', 'rsrc/css/phui/workboards/phui-workboard.css' => 'bda3ef58', 'rsrc/css/phui/workboards/phui-workcard.css' => '0c62d7c5', 'rsrc/css/phui/workboards/phui-workpanel.css' => '92197373', - 'rsrc/css/sprite-login.css' => '60e8560e', + 'rsrc/css/sprite-login.css' => '6dbbbd97', 'rsrc/css/sprite-tokens.css' => '9cdfd599', 'rsrc/css/syntax/syntax-default.css' => '9923583c', 'rsrc/externals/d3/d3.min.js' => 'a11a5ff2', @@ -262,14 +265,79 @@ return array( 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js' => '54f314a0', 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js' => 'b25d5444', 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadStaticSource.js' => '6c0e62fa', - 'rsrc/favicons/apple-touch-icon-120x120.png' => '43742962', - 'rsrc/favicons/apple-touch-icon-152x152.png' => '669eaec3', - 'rsrc/favicons/apple-touch-icon-76x76.png' => 'ecdef672', - 'rsrc/favicons/favicon-128.png' => '47cdff03', - 'rsrc/favicons/favicon-16x16.png' => 'ee2523ac', - 'rsrc/favicons/favicon-32x32.png' => 'b6a8150e', - 'rsrc/favicons/favicon-96x96.png' => '8f7ea177', - 'rsrc/favicons/mask-icon.svg' => '0460cb1f', + 'rsrc/favicons/apple-touch-icon-114x114.png' => '12a24178', + 'rsrc/favicons/apple-touch-icon-120x120.png' => '0d1543c7', + 'rsrc/favicons/apple-touch-icon-144x144.png' => '8043b5a5', + 'rsrc/favicons/apple-touch-icon-152x152.png' => '65905ecd', + 'rsrc/favicons/apple-touch-icon-57x57.png' => '2bfc7b0a', + 'rsrc/favicons/apple-touch-icon-60x60.png' => '8ff52925', + 'rsrc/favicons/apple-touch-icon-72x72.png' => 'a2bb65d6', + 'rsrc/favicons/apple-touch-icon-76x76.png' => '2d061a11', + 'rsrc/favicons/dark/apple-touch-icon-114x114.png' => 'd0c8978c', + 'rsrc/favicons/dark/apple-touch-icon-120x120.png' => '3a618bc0', + 'rsrc/favicons/dark/apple-touch-icon-144x144.png' => '92c1e188', + 'rsrc/favicons/dark/apple-touch-icon-152x152.png' => '7ce7e469', + 'rsrc/favicons/dark/apple-touch-icon-57x57.png' => 'e3f3f38b', + 'rsrc/favicons/dark/apple-touch-icon-60x60.png' => '1e0dcc72', + 'rsrc/favicons/dark/apple-touch-icon-72x72.png' => '7fb599b6', + 'rsrc/favicons/dark/apple-touch-icon-76x76.png' => '91146961', + 'rsrc/favicons/dark/favicon-128.png' => 'd6ac4346', + 'rsrc/favicons/dark/favicon-16x16.png' => '17434bb0', + 'rsrc/favicons/dark/favicon-196x196.png' => '5e06ee72', + 'rsrc/favicons/dark/favicon-32x32.png' => 'bdd7e16b', + 'rsrc/favicons/dark/favicon-96x96.png' => '0cf55978', + 'rsrc/favicons/dark/mstile-144x144.png' => '4dc9d42d', + 'rsrc/favicons/dark/mstile-150x150.png' => '2dc61c90', + 'rsrc/favicons/dark/mstile-310x150.png' => '4fe58ab2', + 'rsrc/favicons/dark/mstile-310x310.png' => 'e62c1677', + 'rsrc/favicons/dark/mstile-70x70.png' => '6d1f41b7', + 'rsrc/favicons/favicon-128.png' => '72f7e812', + 'rsrc/favicons/favicon-16x16.png' => 'fc6275ba', + 'rsrc/favicons/favicon-196x196.png' => '95db275e', + 'rsrc/favicons/favicon-32x32.png' => '5bd18b6c', + 'rsrc/favicons/favicon-96x96.png' => '7242c8e9', + 'rsrc/favicons/mask-icon.svg' => 'e132a80f', + 'rsrc/favicons/mstile-144x144.png' => '310c2ee5', + 'rsrc/favicons/mstile-150x150.png' => '74bf5133', + 'rsrc/favicons/mstile-310x150.png' => '4a49d3ee', + 'rsrc/favicons/mstile-310x310.png' => 'a52ab264', + 'rsrc/favicons/mstile-70x70.png' => '5edce7b8', + 'rsrc/favicons/red/apple-touch-icon-114x114.png' => '91e37d1d', + 'rsrc/favicons/red/apple-touch-icon-120x120.png' => '66687533', + 'rsrc/favicons/red/apple-touch-icon-144x144.png' => 'bc06002c', + 'rsrc/favicons/red/apple-touch-icon-152x152.png' => 'a713de42', + 'rsrc/favicons/red/apple-touch-icon-57x57.png' => '4729688b', + 'rsrc/favicons/red/apple-touch-icon-60x60.png' => '07b9b609', + 'rsrc/favicons/red/apple-touch-icon-72x72.png' => 'b20c3698', + 'rsrc/favicons/red/apple-touch-icon-76x76.png' => 'c6e7dd5c', + 'rsrc/favicons/red/favicon-128.png' => 'e2b2f8fe', + 'rsrc/favicons/red/favicon-16x16.png' => '929fbceb', + 'rsrc/favicons/red/favicon-196x196.png' => '94c089a5', + 'rsrc/favicons/red/favicon-32x32.png' => '5848673e', + 'rsrc/favicons/red/favicon-96x96.png' => '895d54e8', + 'rsrc/favicons/red/mstile-144x144.png' => '448639f5', + 'rsrc/favicons/red/mstile-150x150.png' => 'c2ba45d0', + 'rsrc/favicons/red/mstile-310x150.png' => 'b0e50799', + 'rsrc/favicons/red/mstile-310x310.png' => '2475c5a5', + 'rsrc/favicons/red/mstile-70x70.png' => '49b197e8', + 'rsrc/favicons/yellow/apple-touch-icon-114x114.png' => '5271fb3f', + 'rsrc/favicons/yellow/apple-touch-icon-120x120.png' => '6c3d9bf9', + 'rsrc/favicons/yellow/apple-touch-icon-144x144.png' => '6484472c', + 'rsrc/favicons/yellow/apple-touch-icon-152x152.png' => 'e305dda8', + 'rsrc/favicons/yellow/apple-touch-icon-57x57.png' => 'fa6c43d4', + 'rsrc/favicons/yellow/apple-touch-icon-60x60.png' => '2673f162', + 'rsrc/favicons/yellow/apple-touch-icon-72x72.png' => '3ad8020c', + 'rsrc/favicons/yellow/apple-touch-icon-76x76.png' => '58cffd81', + 'rsrc/favicons/yellow/favicon-128.png' => '3b2f8233', + 'rsrc/favicons/yellow/favicon-16x16.png' => 'f3a90518', + 'rsrc/favicons/yellow/favicon-196x196.png' => '932c7c65', + 'rsrc/favicons/yellow/favicon-32x32.png' => '005c9f92', + 'rsrc/favicons/yellow/favicon-96x96.png' => '3ad9bfa9', + 'rsrc/favicons/yellow/mstile-144x144.png' => 'fc52335c', + 'rsrc/favicons/yellow/mstile-150x150.png' => '9e556f80', + 'rsrc/favicons/yellow/mstile-310x150.png' => '2c915073', + 'rsrc/favicons/yellow/mstile-310x310.png' => 'ee49978d', + 'rsrc/favicons/yellow/mstile-70x70.png' => '85c7c939', 'rsrc/image/BFCFDA.png' => 'd5ec91f4', 'rsrc/image/actions/edit.png' => '2fc41442', 'rsrc/image/avatar.png' => 'e132bb6a', @@ -343,8 +411,8 @@ return array( 'rsrc/image/phrequent_active.png' => 'a466a8ed', 'rsrc/image/phrequent_inactive.png' => 'bfc15a69', 'rsrc/image/resize.png' => 'fd476de4', - 'rsrc/image/sprite-login-X2.png' => 'e3991e37', - 'rsrc/image/sprite-login.png' => '03d5af29', + 'rsrc/image/sprite-login-X2.png' => '4abee916', + 'rsrc/image/sprite-login.png' => '2b9663fd', 'rsrc/image/sprite-tokens-X2.png' => '804a5232', 'rsrc/image/sprite-tokens.png' => 'b41d03da', 'rsrc/image/texture/card-gradient.png' => '815f26e8', @@ -371,10 +439,11 @@ return array( 'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => '01774ab2', 'rsrc/js/application/conpherence/behavior-drag-and-drop-photo.js' => 'cf86d16a', 'rsrc/js/application/conpherence/behavior-durable-column.js' => 'd3506890', - 'rsrc/js/application/conpherence/behavior-menu.js' => '1d45c74d', + 'rsrc/js/application/conpherence/behavior-menu.js' => '9eb55204', + 'rsrc/js/application/conpherence/behavior-participant-pane.js' => '8604caa8', 'rsrc/js/application/conpherence/behavior-pontificate.js' => '21ba5861', 'rsrc/js/application/conpherence/behavior-quicksand-blacklist.js' => '7927a7d3', - 'rsrc/js/application/conpherence/behavior-widget-pane.js' => 'a8458711', + 'rsrc/js/application/conpherence/behavior-toggle-widget.js' => '9bdbbab0', 'rsrc/js/application/countdown/timer.js' => 'e4cc26b3', 'rsrc/js/application/daemon/behavior-bulk-job-reload.js' => 'edf8a145', 'rsrc/js/application/dashboard/behavior-dashboard-async-panel.js' => '469c0d9e', @@ -438,7 +507,7 @@ return array( 'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e2e0a072', 'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08', 'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f', - 'rsrc/js/application/transactions/behavior-comment-actions.js' => '06460e71', + 'rsrc/js/application/transactions/behavior-comment-actions.js' => '0300eae6', 'rsrc/js/application/transactions/behavior-reorder-configs.js' => 'd7a74243', 'rsrc/js/application/transactions/behavior-reorder-fields.js' => 'b59e1e96', 'rsrc/js/application/transactions/behavior-show-older-transactions.js' => '94c65b72', @@ -534,7 +603,7 @@ return array( 'almanac-css' => 'dbb9b3af', 'aphront-bars' => '231ac33c', 'aphront-dark-console-css' => 'f54bf286', - 'aphront-dialog-view-css' => '913c172e', + 'aphront-dialog-view-css' => '593d3f67', 'aphront-list-filter-view-css' => '5d6f0526', 'aphront-multi-column-view-css' => 'fd18389d', 'aphront-panel-view-css' => '8427b78d', @@ -549,15 +618,15 @@ return array( 'conduit-api-css' => '7bc725c4', 'config-options-css' => '0ede4c9b', 'config-page-css' => '8798e14f', - 'config-welcome-css' => '035aa483', - 'conpherence-durable-column-view' => '86396117', - 'conpherence-menu-css' => '90bdf85c', - 'conpherence-message-pane-css' => '5c7b7b17', + 'conpherence-durable-column-view' => '194ac487', + 'conpherence-header-pane-css' => '517de9fe', + 'conpherence-menu-css' => '78c7b811', + 'conpherence-message-pane-css' => '8d13ac4d', 'conpherence-notification-css' => '6cdcc253', + 'conpherence-participant-pane-css' => '7bba0b56', 'conpherence-thread-manager' => '01774ab2', - 'conpherence-transaction-css' => '85d0974c', - 'conpherence-update-css' => 'faf6be09', - 'conpherence-widget-pane-css' => 'c5b74f9e', + 'conpherence-transaction-css' => '46253e19', + 'conpherence-update-css' => '53bc527a', 'd3' => 'a11a5ff2', 'differential-changeset-view-css' => '9ef7d354', 'differential-core-view-css' => '5b7b8ff4', @@ -596,12 +665,12 @@ return array( 'javelin-behavior-bulk-job-reload' => 'edf8a145', 'javelin-behavior-calendar-month-view' => 'fe33e256', 'javelin-behavior-choose-control' => '327a00d1', - 'javelin-behavior-comment-actions' => '06460e71', + 'javelin-behavior-comment-actions' => '0300eae6', 'javelin-behavior-config-reorder-fields' => 'b6993408', 'javelin-behavior-conpherence-drag-and-drop-photo' => 'cf86d16a', - 'javelin-behavior-conpherence-menu' => '1d45c74d', + 'javelin-behavior-conpherence-menu' => '9eb55204', + 'javelin-behavior-conpherence-participant-pane' => '8604caa8', 'javelin-behavior-conpherence-pontificate' => '21ba5861', - 'javelin-behavior-conpherence-widget-pane' => 'a8458711', 'javelin-behavior-countdown-timer' => 'e4cc26b3', 'javelin-behavior-dark-console' => 'f411b6ae', 'javelin-behavior-dashboard-async-panel' => '469c0d9e', @@ -707,6 +776,7 @@ return array( 'javelin-behavior-test-payment-form' => 'fc91ab6c', 'javelin-behavior-time-typeahead' => '522431f7', 'javelin-behavior-toggle-class' => '92b9ec77', + 'javelin-behavior-toggle-widget' => '9bdbbab0', 'javelin-behavior-typeahead-browse' => '635de1ec', 'javelin-behavior-typeahead-search' => '93d0c9e3', 'javelin-behavior-view-placeholder' => '47830651', @@ -779,14 +849,14 @@ return array( 'phabricator-dashboard-css' => 'bc6f2127', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => '5a13c79f', - 'phabricator-fatal-config-template-css' => '8e6c6fcd', + 'phabricator-fatal-config-template-css' => '8f18fa41', 'phabricator-feed-css' => 'ecd4ec57', 'phabricator-file-upload' => '680ea2c8', 'phabricator-filetree-view-css' => 'fccf9f82', 'phabricator-flag-css' => '5337623f', 'phabricator-keyboard-shortcut' => '1ae869f2', 'phabricator-keyboard-shortcut-manager' => '4a021c10', - 'phabricator-main-menu-view' => 'e862571a', + 'phabricator-main-menu-view' => 'f03e17be', 'phabricator-nav-view-css' => 'b29426e9', 'phabricator-notification' => 'ccf1cbf8', 'phabricator-notification-css' => '3f6c89c9', @@ -794,7 +864,7 @@ return array( 'phabricator-object-selector-css' => '85ee8ce6', 'phabricator-phtize' => 'd254d646', 'phabricator-prefab' => 'cfd23f37', - 'phabricator-remarkup-css' => '5ed06ed8', + 'phabricator-remarkup-css' => 'cd912f2c', 'phabricator-search-results-css' => '7dea472c', 'phabricator-shaped-request' => '7cbe244b', 'phabricator-slowvote-css' => 'a94b7230', @@ -814,7 +884,7 @@ return array( 'phabricator-uiexample-reactor-select' => 'a155550f', 'phabricator-uiexample-reactor-sendclass' => '1def2711', 'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee', - 'phabricator-zindex-css' => '2b01a823', + 'phabricator-zindex-css' => '0d4e5558', 'phame-css' => '8efb0729', 'pholio-css' => 'ca89d380', 'pholio-edit-css' => '07676f51', @@ -836,11 +906,11 @@ return array( 'phui-calendar-month-css' => '8e10e92c', 'phui-chart-css' => '6bf6f78e', 'phui-cms-css' => 'be43c8a8', - 'phui-crumbs-view-css' => '9dac418c', - 'phui-curtain-view-css' => '7148ae25', + 'phui-crumbs-view-css' => '195ac419', + 'phui-curtain-view-css' => '947bf1a4', 'phui-document-summary-view-css' => '9ca48bdf', 'phui-document-view-css' => 'c32e8dec', - 'phui-document-view-pro-css' => 'dc3d46ed', + 'phui-document-view-pro-css' => 'ca1fed81', 'phui-feed-story-css' => 'aa49845d', 'phui-font-icon-base-css' => '870a7360', 'phui-fontkit-css' => '9cda225e', @@ -851,11 +921,12 @@ return array( 'phui-hovercard' => '1bd28176', 'phui-hovercard-view-css' => 'de1a2119', 'phui-icon-set-selector-css' => '1ab67aad', - 'phui-icon-view-css' => 'b1dbd620', + 'phui-icon-view-css' => '9bab6f02', 'phui-image-mask-css' => 'a8498f9c', 'phui-info-panel-css' => '27ea50a1', 'phui-info-view-css' => '28efab79', 'phui-inline-comment-view-css' => '5953c28e', + 'phui-invisible-character-view-css' => '6993d9f0', 'phui-list-view-css' => '9da2aa00', 'phui-object-box-css' => '6b487c57', 'phui-object-item-list-view-css' => '87278fa0', @@ -870,7 +941,7 @@ return array( 'phui-tag-view-css' => '6bbd83e2', 'phui-theme-css' => '027ba77e', 'phui-timeline-view-css' => 'bc523970', - 'phui-two-column-view-css' => '5afdf637', + 'phui-two-column-view-css' => 'fcfbe347', 'phui-workboard-color-css' => 'ac6fe6a7', 'phui-workboard-view-css' => 'bda3ef58', 'phui-workcard-view-css' => '0c62d7c5', @@ -886,13 +957,13 @@ return array( 'policy-transaction-detail-css' => '82100a43', 'ponder-view-css' => 'fbd45f96', 'project-card-view-css' => '9418c97d', - 'project-view-css' => '9ce99f21', + 'project-view-css' => '55d99221', 'releeph-core' => '9b3c5733', 'releeph-preview-branch' => 'b7a6f4a5', 'releeph-request-differential-create-dialog' => '8d8b92cd', 'releeph-request-typeahead-css' => '667a48ae', 'setup-issue-css' => 'f794cfc3', - 'sprite-login-css' => '60e8560e', + 'sprite-login-css' => '6dbbbd97', 'sprite-tokens-css' => '9cdfd599', 'syntax-default-css' => '9923583c', 'syntax-highlighting-css' => '769d3498', @@ -933,6 +1004,15 @@ return array( 'javelin-dom', 'phabricator-keyboard-shortcut', ), + '0300eae6' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-workflow', + 'javelin-dom', + 'phuix-form-control-view', + 'phuix-icon-view', + 'javelin-behavior-phabricator-gesture', + ), '05270951' => array( 'javelin-util', 'javelin-magical-init', @@ -947,15 +1027,6 @@ return array( 'aphront-typeahead-control-css', 'phui-tag-view-css', ), - '06460e71' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-workflow', - 'javelin-dom', - 'phuix-form-control-view', - 'phuix-icon-view', - 'javelin-behavior-phabricator-gesture', - ), '065227cc' => array( 'javelin-behavior', 'javelin-dom', @@ -1067,20 +1138,6 @@ return array( 'javelin-request', 'javelin-uri', ), - '1d45c74d' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-workflow', - 'javelin-behavior-device', - 'javelin-history', - 'javelin-vector', - 'javelin-scrollbar', - 'phabricator-title', - 'phabricator-shaped-request', - 'conpherence-thread-manager', - ), '1def2711' => array( 'javelin-install', 'javelin-dom', @@ -1559,6 +1616,15 @@ return array( '85ee8ce6' => array( 'aphront-dialog-view-css', ), + '8604caa8' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-stratcom', + 'javelin-workflow', + 'javelin-util', + 'phabricator-notification', + 'conpherence-thread-manager', + ), '8694b1df' => array( 'javelin-behavior', 'javelin-dom', @@ -1674,6 +1740,27 @@ return array( 'phabricator-phtize', 'changeset-view-manager', ), + '9bdbbab0' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-util', + 'javelin-workflow', + 'javelin-stratcom', + ), + '9eb55204' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-workflow', + 'javelin-behavior-device', + 'javelin-history', + 'javelin-vector', + 'javelin-scrollbar', + 'phabricator-title', + 'phabricator-shaped-request', + 'conpherence-thread-manager', + ), '9ef7d354' => array( 'phui-inline-comment-view-css', ), @@ -1721,19 +1808,6 @@ return array( 'javelin-stratcom', 'javelin-dom', ), - 'a8458711' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-stratcom', - 'javelin-workflow', - 'javelin-util', - 'phabricator-notification', - 'javelin-behavior-device', - 'phuix-dropdown-menu', - 'phuix-action-list-view', - 'phuix-action-view', - 'conpherence-thread-manager', - ), 'a8d8459d' => array( 'javelin-behavior', 'javelin-dom', @@ -2096,9 +2170,6 @@ return array( 'e6e25838' => array( 'javelin-install', ), - 'e862571a' => array( - 'phui-theme-css', - ), 'e9581f08' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2134,6 +2205,9 @@ return array( 'javelin-workflow', 'javelin-json', ), + 'f03e17be' => array( + 'phui-theme-css', + ), 'f411b6ae' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2228,10 +2302,28 @@ return array( ), ), 'packages' => array( + 'conpherence.pkg.css' => array( + 'conpherence-durable-column-view', + 'conpherence-menu-css', + 'conpherence-message-pane-css', + 'conpherence-notification-css', + 'conpherence-transaction-css', + 'conpherence-update-css', + 'conpherence-participant-pane-css', + 'conpherence-header-pane-css', + ), + 'conpherence.pkg.js' => array( + 'javelin-behavior-conpherence-drag-and-drop-photo', + 'javelin-behavior-conpherence-menu', + 'javelin-behavior-conpherence-participant-pane', + 'javelin-behavior-conpherence-pontificate', + 'javelin-behavior-toggle-widget', + ), 'core.pkg.css' => array( 'phabricator-core-css', 'phabricator-zindex-css', 'phui-button-css', + 'phui-theme-css', 'phabricator-standard-page-view', 'aphront-dialog-view-css', 'phui-form-view-css', @@ -2268,18 +2360,24 @@ return array( 'phui-tag-view-css', 'phui-list-view-css', 'font-fontawesome', + 'font-lato', + 'font-aleo', 'phui-font-icon-base-css', + 'phui-fontkit-css', 'phui-box-css', 'phui-object-box-css', 'phui-timeline-view-css', + 'phui-two-column-view-css', + 'phui-curtain-view-css', + 'sprite-login-css', 'sprite-tokens-css', 'tokens-css', + 'auth-css', 'phui-status-list-view-css', 'phui-feed-story-css', 'phabricator-feed-css', 'phabricator-dashboard-css', 'aphront-multi-column-view-css', - 'conpherence-durable-column-view', ), 'core.pkg.js' => array( 'javelin-util', @@ -2316,6 +2414,7 @@ return array( 'phuix-dropdown-menu', 'phuix-action-list-view', 'phuix-action-view', + 'phuix-icon-view', 'phabricator-phtize', 'javelin-behavior-phabricator-oncopy', 'phabricator-tooltip', diff --git a/resources/celerity/packages.php b/resources/celerity/packages.php index 66015f6263..6e553a8c6e 100644 --- a/resources/celerity/packages.php +++ b/resources/celerity/packages.php @@ -36,6 +36,7 @@ return array( 'phuix-dropdown-menu', 'phuix-action-list-view', 'phuix-action-view', + 'phuix-icon-view', 'phabricator-phtize', 'javelin-behavior-phabricator-oncopy', 'phabricator-tooltip', @@ -88,6 +89,7 @@ return array( 'phabricator-core-css', 'phabricator-zindex-css', 'phui-button-css', + 'phui-theme-css', 'phabricator-standard-page-view', 'aphront-dialog-view-css', 'phui-form-view-css', @@ -128,19 +130,43 @@ return array( 'phui-list-view-css', 'font-fontawesome', + 'font-lato', + 'font-aleo', 'phui-font-icon-base-css', + 'phui-fontkit-css', 'phui-box-css', 'phui-object-box-css', 'phui-timeline-view-css', + 'phui-two-column-view-css', + 'phui-curtain-view-css', + + 'sprite-login-css', 'sprite-tokens-css', 'tokens-css', - 'phui-status-list-view-css', + 'auth-css', + 'phui-status-list-view-css', 'phui-feed-story-css', 'phabricator-feed-css', 'phabricator-dashboard-css', 'aphront-multi-column-view-css', + ), + 'conpherence.pkg.css' => array( 'conpherence-durable-column-view', + 'conpherence-menu-css', + 'conpherence-message-pane-css', + 'conpherence-notification-css', + 'conpherence-transaction-css', + 'conpherence-update-css', + 'conpherence-participant-pane-css', + 'conpherence-header-pane-css', + ), + 'conpherence.pkg.js' => array( + 'javelin-behavior-conpherence-drag-and-drop-photo', + 'javelin-behavior-conpherence-menu', + 'javelin-behavior-conpherence-participant-pane', + 'javelin-behavior-conpherence-pontificate', + 'javelin-behavior-toggle-widget', ), 'differential.pkg.css' => array( 'differential-core-view-css', diff --git a/resources/sprite/login_1x/Slack.png b/resources/sprite/login_1x/Slack.png new file mode 100644 index 0000000000..411bf73c13 Binary files /dev/null and b/resources/sprite/login_1x/Slack.png differ diff --git a/resources/sprite/login_2x/Slack.png b/resources/sprite/login_2x/Slack.png new file mode 100644 index 0000000000..163d96cea0 Binary files /dev/null and b/resources/sprite/login_2x/Slack.png differ diff --git a/resources/sprite/manifest/login.json b/resources/sprite/manifest/login.json index a75d2fa69e..f62a0a8ea0 100644 --- a/resources/sprite/manifest/login.json +++ b/resources/sprite/manifest/login.json @@ -91,6 +91,11 @@ "rule": ".login-Phabricator", "hash": "d0f830803593bbcc025d7d5a29ee3ecd" }, + "login-Slack": { + "name": "login-Slack", + "rule": ".login-Slack", + "hash": "fe0df2df040032b949aa05948b6bd986" + }, "login-Stripe": { "name": "login-Stripe", "rule": ".login-Stripe", diff --git a/resources/sql/autopatches/20160913.conpherence.topic.1.sql b/resources/sql/autopatches/20160913.conpherence.topic.1.sql new file mode 100644 index 0000000000..186a5b5c58 --- /dev/null +++ b/resources/sql/autopatches/20160913.conpherence.topic.1.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_conpherence.conpherence_thread + ADD topic VARCHAR(255) NOT NULL COLLATE {$COLLATE_TEXT}; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 02b4dc5b92..dafd4928e7 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -300,10 +300,11 @@ phutil_register_library_map(array( 'ConpherenceNewRoomController' => 'applications/conpherence/controller/ConpherenceNewRoomController.php', 'ConpherenceNotificationPanelController' => 'applications/conpherence/controller/ConpherenceNotificationPanelController.php', 'ConpherenceParticipant' => 'applications/conpherence/storage/ConpherenceParticipant.php', + 'ConpherenceParticipantController' => 'applications/conpherence/controller/ConpherenceParticipantController.php', 'ConpherenceParticipantCountQuery' => 'applications/conpherence/query/ConpherenceParticipantCountQuery.php', 'ConpherenceParticipantQuery' => 'applications/conpherence/query/ConpherenceParticipantQuery.php', + 'ConpherenceParticipantView' => 'applications/conpherence/view/ConpherenceParticipantView.php', 'ConpherenceParticipationStatus' => 'applications/conpherence/constants/ConpherenceParticipationStatus.php', - 'ConpherencePeopleWidgetView' => 'applications/conpherence/view/ConpherencePeopleWidgetView.php', 'ConpherencePicCropControl' => 'applications/conpherence/view/ConpherencePicCropControl.php', 'ConpherenceQueryThreadConduitAPIMethod' => 'applications/conpherence/conduit/ConpherenceQueryThreadConduitAPIMethod.php', 'ConpherenceQueryTransactionConduitAPIMethod' => 'applications/conpherence/conduit/ConpherenceQueryTransactionConduitAPIMethod.php', @@ -329,9 +330,6 @@ phutil_register_library_map(array( 'ConpherenceUpdateController' => 'applications/conpherence/controller/ConpherenceUpdateController.php', 'ConpherenceUpdateThreadConduitAPIMethod' => 'applications/conpherence/conduit/ConpherenceUpdateThreadConduitAPIMethod.php', 'ConpherenceViewController' => 'applications/conpherence/controller/ConpherenceViewController.php', - 'ConpherenceWidgetConfigConstants' => 'applications/conpherence/constants/ConpherenceWidgetConfigConstants.php', - 'ConpherenceWidgetController' => 'applications/conpherence/controller/ConpherenceWidgetController.php', - 'ConpherenceWidgetView' => 'applications/conpherence/view/ConpherenceWidgetView.php', 'DarkConsoleController' => 'applications/console/controller/DarkConsoleController.php', 'DarkConsoleCore' => 'applications/console/core/DarkConsoleCore.php', 'DarkConsoleDataController' => 'applications/console/controller/DarkConsoleDataController.php', @@ -1652,6 +1650,8 @@ phutil_register_library_map(array( 'PHUIInfoPanelExample' => 'applications/uiexample/examples/PHUIInfoPanelExample.php', 'PHUIInfoPanelView' => 'view/phui/PHUIInfoPanelView.php', 'PHUIInfoView' => 'view/form/PHUIInfoView.php', + 'PHUIInvisibleCharacterTestCase' => 'view/phui/__tests__/PHUIInvisibleCharacterTestCase.php', + 'PHUIInvisibleCharacterView' => 'view/phui/PHUIInvisibleCharacterView.php', 'PHUIListExample' => 'applications/uiexample/examples/PHUIListExample.php', 'PHUIListItemView' => 'view/phui/PHUIListItemView.php', 'PHUIListView' => 'view/phui/PHUIListView.php', @@ -2044,6 +2044,7 @@ phutil_register_library_map(array( 'PhabricatorCalendarEventEditor' => 'applications/calendar/editor/PhabricatorCalendarEventEditor.php', 'PhabricatorCalendarEventEmailCommand' => 'applications/calendar/command/PhabricatorCalendarEventEmailCommand.php', 'PhabricatorCalendarEventEndDateTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventEndDateTransaction.php', + 'PhabricatorCalendarEventExportController' => 'applications/calendar/controller/PhabricatorCalendarEventExportController.php', 'PhabricatorCalendarEventFrequencyTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventFrequencyTransaction.php', 'PhabricatorCalendarEventFulltextEngine' => 'applications/calendar/search/PhabricatorCalendarEventFulltextEngine.php', 'PhabricatorCalendarEventHeraldAdapter' => 'applications/calendar/herald/PhabricatorCalendarEventHeraldAdapter.php', @@ -2211,12 +2212,12 @@ phutil_register_library_map(array( 'PhabricatorConfigTransactionQuery' => 'applications/config/query/PhabricatorConfigTransactionQuery.php', 'PhabricatorConfigValidationException' => 'applications/config/exception/PhabricatorConfigValidationException.php', 'PhabricatorConfigVersionController' => 'applications/config/controller/PhabricatorConfigVersionController.php', - 'PhabricatorConfigWelcomeController' => 'applications/config/controller/PhabricatorConfigWelcomeController.php', 'PhabricatorConpherenceApplication' => 'applications/conpherence/application/PhabricatorConpherenceApplication.php', 'PhabricatorConpherenceColumnVisibleSetting' => 'applications/settings/setting/PhabricatorConpherenceColumnVisibleSetting.php', 'PhabricatorConpherenceNotificationsSetting' => 'applications/settings/setting/PhabricatorConpherenceNotificationsSetting.php', 'PhabricatorConpherencePreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php', 'PhabricatorConpherenceThreadPHIDType' => 'applications/conpherence/phid/PhabricatorConpherenceThreadPHIDType.php', + 'PhabricatorConpherenceWidgetVisibleSetting' => 'applications/settings/setting/PhabricatorConpherenceWidgetVisibleSetting.php', 'PhabricatorConsoleApplication' => 'applications/console/application/PhabricatorConsoleApplication.php', 'PhabricatorConsoleContentSource' => 'infrastructure/contentsource/PhabricatorConsoleContentSource.php', 'PhabricatorContentSource' => 'infrastructure/contentsource/PhabricatorContentSource.php', @@ -2647,7 +2648,6 @@ phutil_register_library_map(array( 'PhabricatorGuideModule' => 'applications/guides/module/PhabricatorGuideModule.php', 'PhabricatorGuideModuleController' => 'applications/guides/controller/PhabricatorGuideModuleController.php', 'PhabricatorGuideQuickStartModule' => 'applications/guides/module/PhabricatorGuideQuickStartModule.php', - 'PhabricatorGuideWelcomeModule' => 'applications/guides/module/PhabricatorGuideWelcomeModule.php', 'PhabricatorHTTPParameterTypeTableView' => 'applications/config/view/PhabricatorHTTPParameterTypeTableView.php', 'PhabricatorHandleList' => 'applications/phid/handle/pool/PhabricatorHandleList.php', 'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/PhabricatorHandleObjectSelectorDataView.php', @@ -2687,6 +2687,7 @@ phutil_register_library_map(array( 'PhabricatorImageMacroRemarkupRule' => 'applications/macro/markup/PhabricatorImageMacroRemarkupRule.php', 'PhabricatorImageTransformer' => 'applications/files/PhabricatorImageTransformer.php', 'PhabricatorImagemagickSetupCheck' => 'applications/config/check/PhabricatorImagemagickSetupCheck.php', + 'PhabricatorInFlightErrorView' => 'applications/config/view/PhabricatorInFlightErrorView.php', 'PhabricatorIndexEngine' => 'applications/search/index/PhabricatorIndexEngine.php', 'PhabricatorIndexEngineExtension' => 'applications/search/index/PhabricatorIndexEngineExtension.php', 'PhabricatorIndexEngineExtensionModule' => 'applications/search/index/PhabricatorIndexEngineExtensionModule.php', @@ -2708,6 +2709,7 @@ phutil_register_library_map(array( 'PhabricatorJiraIssueHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorJiraIssueHasObjectEdgeType.php', 'PhabricatorJumpNavHandler' => 'applications/search/engine/PhabricatorJumpNavHandler.php', 'PhabricatorKeyValueDatabaseCache' => 'applications/cache/PhabricatorKeyValueDatabaseCache.php', + 'PhabricatorKeyboardRemarkupRule' => 'infrastructure/markup/rule/PhabricatorKeyboardRemarkupRule.php', 'PhabricatorKeyring' => 'applications/files/keyring/PhabricatorKeyring.php', 'PhabricatorKeyringConfigOptionType' => 'applications/files/keyring/PhabricatorKeyringConfigOptionType.php', 'PhabricatorLDAPAuthProvider' => 'applications/auth/provider/PhabricatorLDAPAuthProvider.php', @@ -3591,6 +3593,7 @@ phutil_register_library_map(array( 'PhabricatorShowFiletreeSetting' => 'applications/settings/setting/PhabricatorShowFiletreeSetting.php', 'PhabricatorSimpleEditType' => 'applications/transactions/edittype/PhabricatorSimpleEditType.php', 'PhabricatorSite' => 'aphront/site/PhabricatorSite.php', + 'PhabricatorSlackAuthProvider' => 'applications/auth/provider/PhabricatorSlackAuthProvider.php', 'PhabricatorSlowvoteApplication' => 'applications/slowvote/application/PhabricatorSlowvoteApplication.php', 'PhabricatorSlowvoteChoice' => 'applications/slowvote/storage/PhabricatorSlowvoteChoice.php', 'PhabricatorSlowvoteCloseController' => 'applications/slowvote/controller/PhabricatorSlowvoteCloseController.php', @@ -4764,10 +4767,11 @@ phutil_register_library_map(array( 'ConpherenceNewRoomController' => 'ConpherenceController', 'ConpherenceNotificationPanelController' => 'ConpherenceController', 'ConpherenceParticipant' => 'ConpherenceDAO', + 'ConpherenceParticipantController' => 'ConpherenceController', 'ConpherenceParticipantCountQuery' => 'PhabricatorOffsetPagedQuery', 'ConpherenceParticipantQuery' => 'PhabricatorOffsetPagedQuery', + 'ConpherenceParticipantView' => 'AphrontView', 'ConpherenceParticipationStatus' => 'ConpherenceConstants', - 'ConpherencePeopleWidgetView' => 'ConpherenceWidgetView', 'ConpherencePicCropControl' => 'AphrontFormControl', 'ConpherenceQueryThreadConduitAPIMethod' => 'ConpherenceConduitAPIMethod', 'ConpherenceQueryTransactionConduitAPIMethod' => 'ConpherenceConduitAPIMethod', @@ -4799,9 +4803,6 @@ phutil_register_library_map(array( 'ConpherenceUpdateController' => 'ConpherenceController', 'ConpherenceUpdateThreadConduitAPIMethod' => 'ConpherenceConduitAPIMethod', 'ConpherenceViewController' => 'ConpherenceController', - 'ConpherenceWidgetConfigConstants' => 'ConpherenceConstants', - 'ConpherenceWidgetController' => 'ConpherenceController', - 'ConpherenceWidgetView' => 'AphrontView', 'DarkConsoleController' => 'PhabricatorController', 'DarkConsoleCore' => 'Phobject', 'DarkConsoleDataController' => 'PhabricatorController', @@ -6318,6 +6319,8 @@ phutil_register_library_map(array( 'PHUIInfoPanelExample' => 'PhabricatorUIExample', 'PHUIInfoPanelView' => 'AphrontView', 'PHUIInfoView' => 'AphrontView', + 'PHUIInvisibleCharacterTestCase' => 'PhabricatorTestCase', + 'PHUIInvisibleCharacterView' => 'AphrontView', 'PHUIListExample' => 'PhabricatorUIExample', 'PHUIListItemView' => 'AphrontTagView', 'PHUIListView' => 'AphrontTagView', @@ -6779,6 +6782,7 @@ phutil_register_library_map(array( 'PhabricatorCalendarEventEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorCalendarEventEmailCommand' => 'MetaMTAEmailTransactionCommand', 'PhabricatorCalendarEventEndDateTransaction' => 'PhabricatorCalendarEventDateTransaction', + 'PhabricatorCalendarEventExportController' => 'PhabricatorCalendarController', 'PhabricatorCalendarEventFrequencyTransaction' => 'PhabricatorCalendarEventTransactionType', 'PhabricatorCalendarEventFulltextEngine' => 'PhabricatorFulltextEngine', 'PhabricatorCalendarEventHeraldAdapter' => 'HeraldAdapter', @@ -6967,12 +6971,12 @@ phutil_register_library_map(array( 'PhabricatorConfigTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorConfigValidationException' => 'Exception', 'PhabricatorConfigVersionController' => 'PhabricatorConfigController', - 'PhabricatorConfigWelcomeController' => 'PhabricatorConfigController', 'PhabricatorConpherenceApplication' => 'PhabricatorApplication', 'PhabricatorConpherenceColumnVisibleSetting' => 'PhabricatorInternalSetting', 'PhabricatorConpherenceNotificationsSetting' => 'PhabricatorSelectSetting', 'PhabricatorConpherencePreferencesSettingsPanel' => 'PhabricatorEditEngineSettingsPanel', 'PhabricatorConpherenceThreadPHIDType' => 'PhabricatorPHIDType', + 'PhabricatorConpherenceWidgetVisibleSetting' => 'PhabricatorInternalSetting', 'PhabricatorConsoleApplication' => 'PhabricatorApplication', 'PhabricatorConsoleContentSource' => 'PhabricatorContentSource', 'PhabricatorContentSource' => 'Phobject', @@ -7467,7 +7471,6 @@ phutil_register_library_map(array( 'PhabricatorGuideModule' => 'Phobject', 'PhabricatorGuideModuleController' => 'PhabricatorGuideController', 'PhabricatorGuideQuickStartModule' => 'PhabricatorGuideModule', - 'PhabricatorGuideWelcomeModule' => 'PhabricatorGuideModule', 'PhabricatorHTTPParameterTypeTableView' => 'AphrontView', 'PhabricatorHandleList' => array( 'Phobject', @@ -7512,6 +7515,7 @@ phutil_register_library_map(array( 'PhabricatorImageMacroRemarkupRule' => 'PhutilRemarkupRule', 'PhabricatorImageTransformer' => 'Phobject', 'PhabricatorImagemagickSetupCheck' => 'PhabricatorSetupCheck', + 'PhabricatorInFlightErrorView' => 'AphrontView', 'PhabricatorIndexEngine' => 'Phobject', 'PhabricatorIndexEngineExtension' => 'Phobject', 'PhabricatorIndexEngineExtensionModule' => 'PhabricatorConfigModule', @@ -7533,6 +7537,7 @@ phutil_register_library_map(array( 'PhabricatorJiraIssueHasObjectEdgeType' => 'PhabricatorEdgeType', 'PhabricatorJumpNavHandler' => 'Phobject', 'PhabricatorKeyValueDatabaseCache' => 'PhutilKeyValueCache', + 'PhabricatorKeyboardRemarkupRule' => 'PhutilRemarkupRule', 'PhabricatorKeyring' => 'Phobject', 'PhabricatorKeyringConfigOptionType' => 'PhabricatorConfigJSONOptionType', 'PhabricatorLDAPAuthProvider' => 'PhabricatorAuthProvider', @@ -8598,6 +8603,7 @@ phutil_register_library_map(array( 'PhabricatorShowFiletreeSetting' => 'PhabricatorSelectSetting', 'PhabricatorSimpleEditType' => 'PhabricatorEditType', 'PhabricatorSite' => 'AphrontSite', + 'PhabricatorSlackAuthProvider' => 'PhabricatorOAuth2AuthProvider', 'PhabricatorSlowvoteApplication' => 'PhabricatorApplication', 'PhabricatorSlowvoteChoice' => 'PhabricatorSlowvoteDAO', 'PhabricatorSlowvoteCloseController' => 'PhabricatorSlowvoteController', @@ -9357,8 +9363,9 @@ phutil_register_library_map(array( 'PhabricatorFlaggableInterface', 'PhabricatorTokenReceiverInterface', 'PhabricatorDestructibleInterface', - 'PhabricatorApplicationTransactionInterface', 'PhabricatorFulltextInterface', + 'PhabricatorProjectInterface', + 'PhabricatorApplicationTransactionInterface', ), 'PhrictionDocumentAuthorHeraldField' => 'PhrictionDocumentHeraldField', 'PhrictionDocumentContentHeraldField' => 'PhrictionDocumentHeraldField', diff --git a/src/applications/audit/editor/PhabricatorAuditEditor.php b/src/applications/audit/editor/PhabricatorAuditEditor.php index f9524084a0..a6ef866c93 100644 --- a/src/applications/audit/editor/PhabricatorAuditEditor.php +++ b/src/applications/audit/editor/PhabricatorAuditEditor.php @@ -886,9 +886,8 @@ final class PhabricatorAuditEditor protected function buildHeraldAdapter( PhabricatorLiskDAO $object, array $xactions) { - return id(new HeraldCommitAdapter()) - ->setCommit($object); + ->setObject($object); } protected function didApplyHeraldRules( diff --git a/src/applications/auth/controller/PhabricatorAuthRegisterController.php b/src/applications/auth/controller/PhabricatorAuthRegisterController.php index 1d036b13a9..3e4135fff5 100644 --- a/src/applications/auth/controller/PhabricatorAuthRegisterController.php +++ b/src/applications/auth/controller/PhabricatorAuthRegisterController.php @@ -89,13 +89,14 @@ final class PhabricatorAuthRegisterController // user expectation and it's not clear the cases it enables are valuable. // See discussion in T3472. if (!PhabricatorUserEmail::isAllowedAddress($default_email)) { + $debug_email = new PHUIInvisibleCharacterView($default_email); return $this->renderError( array( pht( 'The account you are attempting to register with has an invalid '. 'email address (%s). This Phabricator install only allows '. 'registration with specific email addresses:', - $default_email), + $debug_email), phutil_tag('br'), phutil_tag('br'), PhabricatorUserEmail::describeAllowedAddresses(), diff --git a/src/applications/auth/provider/PhabricatorSlackAuthProvider.php b/src/applications/auth/provider/PhabricatorSlackAuthProvider.php new file mode 100644 index 0000000000..f1e607d757 --- /dev/null +++ b/src/applications/auth/provider/PhabricatorSlackAuthProvider.php @@ -0,0 +1,36 @@ +getLoginURI()); + + return pht( + "To configure Slack OAuth, create a new application here:". + "\n\n". + "https://api.slack.com/docs/sign-in-with-slack#create_slack_app". + "\n\n". + "When creating your application, use these settings:". + "\n\n". + " - **Redirect URI:** Set this to: `%s`". + "\n\n". + "After completing configuration, copy the **Client ID** and ". + "**Client Secret** to the fields above. (You may need to generate the ". + "client secret by clicking 'New Secret' first.)", + $login_uri); + } + + protected function newOAuthAdapter() { + return new PhutilSlackAuthAdapter(); + } + + protected function getLoginIcon() { + return 'Slack'; + } + +} diff --git a/src/applications/cache/PhabricatorCaches.php b/src/applications/cache/PhabricatorCaches.php index e9baa299a2..c9bd304ccf 100644 --- a/src/applications/cache/PhabricatorCaches.php +++ b/src/applications/cache/PhabricatorCaches.php @@ -143,6 +143,39 @@ final class PhabricatorCaches extends Phobject { } +/* -( Server State Cache )------------------------------------------------- */ + + + /** + * Highly specialized cache for storing server process state. + * + * We use this cache to track initial steps in the setup phase, before + * configuration is loaded. + * + * This cache does NOT use the cache namespace (it must be accessed before + * we build configuration), and is global across all instances on the host. + * + * @return PhutilKeyValueCacheStack Best available server state cache stack. + * @task setup + */ + public static function getServerStateCache() { + static $cache; + if (!$cache) { + $caches = self::buildSetupCaches('phabricator-server'); + + // NOTE: We are NOT adding a cache namespace here! This cache is shared + // across all instances on the host. + + $caches = self::addProfilerToCaches($caches); + $cache = id(new PhutilKeyValueCacheStack()) + ->setCaches($caches); + + } + return $cache; + } + + + /* -( Setup Cache )-------------------------------------------------------- */ @@ -163,7 +196,7 @@ final class PhabricatorCaches extends Phobject { public static function getSetupCache() { static $cache; if (!$cache) { - $caches = self::buildSetupCaches(); + $caches = self::buildSetupCaches('phabricator-setup'); $cache = self::newStackFromCaches($caches); } return $cache; @@ -173,7 +206,7 @@ final class PhabricatorCaches extends Phobject { /** * @task setup */ - private static function buildSetupCaches() { + private static function buildSetupCaches($cache_name) { // If this is the CLI, just build a setup cache. if (php_sapi_name() == 'cli') { return array(); @@ -188,7 +221,7 @@ final class PhabricatorCaches extends Phobject { // If we don't have APC, build a poor approximation on disk. This is still // much better than nothing; some setup steps are quite slow. - $disk_path = self::getSetupCacheDiskCachePath(); + $disk_path = self::getSetupCacheDiskCachePath($cache_name); if ($disk_path) { $disk = new PhutilOnDiskKeyValueCache(); $disk->setCacheFile($disk_path); @@ -205,7 +238,7 @@ final class PhabricatorCaches extends Phobject { /** * @task setup */ - private static function getSetupCacheDiskCachePath() { + private static function getSetupCacheDiskCachePath($name) { // The difficulty here is in choosing a path which will change on server // restart (we MUST have this property), but as rarely as possible // otherwise (we desire this property to give the cache the best hit rate @@ -230,7 +263,7 @@ final class PhabricatorCaches extends Phobject { $tmp_dir = sys_get_temp_dir(); - $tmp_path = $tmp_dir.DIRECTORY_SEPARATOR.'phabricator-setup'; + $tmp_path = $tmp_dir.DIRECTORY_SEPARATOR.$name; if (!file_exists($tmp_path)) { @mkdir($tmp_path); } diff --git a/src/applications/calendar/application/PhabricatorCalendarApplication.php b/src/applications/calendar/application/PhabricatorCalendarApplication.php index 8dd68c42e0..e948d70198 100644 --- a/src/applications/calendar/application/PhabricatorCalendarApplication.php +++ b/src/applications/calendar/application/PhabricatorCalendarApplication.php @@ -59,6 +59,8 @@ final class PhabricatorCalendarApplication extends PhabricatorApplication { => 'PhabricatorCalendarEventCancelController', '(?Pjoin|decline|accept)/(?P[1-9]\d*)/' => 'PhabricatorCalendarEventJoinController', + 'export/(?P[1-9]\d*)/(?P[^/]*)' + => 'PhabricatorCalendarEventExportController', ), ), ); diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventExportController.php b/src/applications/calendar/controller/PhabricatorCalendarEventExportController.php new file mode 100644 index 0000000000..8163e4abeb --- /dev/null +++ b/src/applications/calendar/controller/PhabricatorCalendarEventExportController.php @@ -0,0 +1,40 @@ +getViewer(); + $id = $request->getURIData('id'); + + $event = id(new PhabricatorCalendarEventQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->executeOne(); + if (!$event) { + return new Aphront404Response(); + } + + $file_name = $event->getICSFilename(); + $event_node = $event->newIntermediateEventNode($viewer); + + $document_node = id(new PhutilCalendarDocumentNode()) + ->appendChild($event_node); + + $root_node = id(new PhutilCalendarRootNode()) + ->appendChild($document_node); + + $ics_data = id(new PhutilICSWriter()) + ->writeICSDocument($root_node); + + return id(new AphrontFileResponse()) + ->setDownload($file_name) + ->setMimeType('text/calendar') + ->setContent($ics_data); + } + +} diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php index 9d1c5b6349..57792e6717 100644 --- a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php +++ b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php @@ -193,6 +193,15 @@ final class PhabricatorCalendarEventViewController ->setWorkflow(true)); } + $ics_name = $event->getICSFilename(); + $export_uri = $this->getApplicationURI("event/export/{$id}/{$ics_name}"); + + $curtain->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Export as .ics')) + ->setIcon('fa-download') + ->setHref($export_uri)); + return $curtain; } @@ -278,7 +287,14 @@ final class PhabricatorCalendarEventViewController $parent = $event->getParentEvent(); } - $next_uri = $parent->getURI().'/'.($sequence + 1); + if ($parent->isValidSequenceIndex($viewer, $sequence + 1)) { + $next_uri = $parent->getURI().'/'.($sequence + 1); + $has_next = true; + } else { + $next_uri = null; + $has_next = false; + } + if ($sequence) { if ($sequence > 1) { $previous_uri = $parent->getURI().'/'.($sequence - 1); @@ -302,6 +318,7 @@ final class PhabricatorCalendarEventViewController ->setTag('a') ->setIcon('fa-chevron-right') ->setHref($next_uri) + ->setDisabled(!$has_next) ->setText(pht('Next')); $header @@ -450,9 +467,11 @@ final class PhabricatorCalendarEventViewController 'it.')); } - $instance = $event->newStub($viewer, $sequence); + if (!$event->isValidSequenceIndex($viewer, $sequence)) { + return null; + } - return $instance; + return $event->newStub($viewer, $sequence); } private function buildSubheaderView(PhabricatorCalendarEvent $event) { diff --git a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php index 5498efadd3..d87746f347 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php +++ b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php @@ -285,6 +285,8 @@ final class PhabricatorCalendarEventEditor pht('EVENT DETAIL'), PhabricatorEnv::getProductionURI('/E'.$object->getID())); + $ics_attachment = $this->newICSAttachment($object); + $body->addAttachment($ics_attachment); return $body; } @@ -303,5 +305,27 @@ final class PhabricatorCalendarEventEditor ->setObject($object); } + private function newICSAttachment( + PhabricatorCalendarEvent $event) { + $actor = $this->getActor(); + + $event_node = $event->newIntermediateEventNode($actor); + + $document_node = id(new PhutilCalendarDocumentNode()) + ->appendChild($event_node); + + $root_node = id(new PhutilCalendarRootNode()) + ->appendChild($document_node); + + $ics_data = id(new PhutilICSWriter()) + ->writeICSDocument($root_node); + + $ics_attachment = new PhabricatorMetaMTAAttachment( + $ics_data, + $event->getICSFilename(), + 'text/calendar'); + + return $ics_attachment; + } } diff --git a/src/applications/calendar/storage/PhabricatorCalendarEvent.php b/src/applications/calendar/storage/PhabricatorCalendarEvent.php index 6e53751117..63e5f2082a 100644 --- a/src/applications/calendar/storage/PhabricatorCalendarEvent.php +++ b/src/applications/calendar/storage/PhabricatorCalendarEvent.php @@ -137,7 +137,8 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO ); // Read these fields from the parent event instead of this event. For - // example, we want any changes to the parent event's name to + // example, we want any changes to the parent event's name to apply to + // the child. if (isset($inherit[$field])) { if ($this->getIsStub()) { // TODO: This should be unconditional, but the execution order of @@ -171,33 +172,66 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO ->setName($parent->getName()) ->setDescription($parent->getDescription()); - $frequency = $parent->getFrequencyUnit(); - $modify_key = '+'.$this->getSequenceIndex().' '.$frequency; + $sequence = $this->getSequenceIndex(); + $duration = $this->getDuration(); + $epochs = $parent->getSequenceIndexEpochs($actor, $sequence, $duration); - $date = $parent->getDateFrom(); - $date_time = PhabricatorTime::getDateTimeFromEpoch($date, $actor); + $this + ->setDateFrom($epochs['dateFrom']) + ->setDateTo($epochs['dateTo']) + ->setAllDayDateFrom($epochs['allDayDateFrom']) + ->setAllDayDateTo($epochs['allDayDateTo']); + + return $this; + } + + public function isValidSequenceIndex(PhabricatorUser $viewer, $sequence) { + try { + $this->getSequenceIndexEpochs($viewer, $sequence, $this->getDuration()); + return true; + } catch (Exception $ex) { + return false; + } + } + + private function getSequenceIndexEpochs( + PhabricatorUser $viewer, + $sequence, + $duration) { + + $frequency = $this->getFrequencyUnit(); + $modify_key = '+'.$sequence.' '.$frequency; + + $date = $this->getDateFrom(); + $date_time = PhabricatorTime::getDateTimeFromEpoch($date, $viewer); $date_time->modify($modify_key); $date = $date_time->format('U'); - $duration = $this->getDuration(); + $end_date = $this->getRecurrenceEndDate(); + if ($end_date && $date > $end_date) { + throw new Exception( + pht( + 'Sequence "%s" is invalid for this event: it would occur after '. + 'the event stops repeating.', + $sequence)); + } $utc = new DateTimeZone('UTC'); - $allday_from = $parent->getAllDayDateFrom(); + $allday_from = $this->getAllDayDateFrom(); $allday_date = new DateTime('@'.$allday_from, $utc); $allday_date->setTimeZone($utc); $allday_date->modify($modify_key); $allday_min = $allday_date->format('U'); - $allday_duration = ($parent->getAllDayDateTo() - $allday_from); + $allday_duration = ($this->getAllDayDateTo() - $allday_from); - $this - ->setDateFrom($date) - ->setDateTo($date + $duration) - ->setAllDayDateFrom($allday_min) - ->setAllDayDateTo($allday_min + $allday_duration); - - return $this; + return array( + 'dateFrom' => $date, + 'dateTo' => $date + $duration, + 'allDayDateFrom' => $allday_min, + 'allDayDateTo' => $allday_min + $allday_duration, + ); } public function newStub(PhabricatorUser $actor, $sequence) { @@ -591,6 +625,101 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO return null; } + public function getICSFilename() { + return $this->getMonogram().'.ics'; + } + + public function newIntermediateEventNode(PhabricatorUser $viewer) { + $base_uri = new PhutilURI(PhabricatorEnv::getProductionURI('/')); + $domain = $base_uri->getDomain(); + + $uid = $this->getPHID().'@'.$domain; + + $created = $this->getDateCreated(); + $created = PhutilCalendarAbsoluteDateTime::newFromEpoch($created); + + $modified = $this->getDateModified(); + $modified = PhutilCalendarAbsoluteDateTime::newFromEpoch($modified); + + $date_start = $this->getDateFrom(); + $date_start = PhutilCalendarAbsoluteDateTime::newFromEpoch($date_start); + + $date_end = $this->getDateTo(); + $date_end = PhutilCalendarAbsoluteDateTime::newFromEpoch($date_end); + + if ($this->getIsAllDay()) { + $date_start->setIsAllDay(true); + $date_end->setIsAllDay(true); + } + + $host_phid = $this->getHostPHID(); + + $invitees = $this->getInvitees(); + foreach ($invitees as $key => $invitee) { + if ($invitee->isUninvited()) { + unset($invitees[$key]); + } + } + + $phids = array(); + $phids[] = $host_phid; + foreach ($invitees as $invitee) { + $phids[] = $invitee->getInviteePHID(); + } + + $handles = $viewer->loadHandles($phids); + + $host_handle = $handles[$host_phid]; + $host_name = $host_handle->getFullName(); + $host_uri = $host_handle->getURI(); + $host_uri = PhabricatorEnv::getURI($host_uri); + + $organizer = id(new PhutilCalendarUserNode()) + ->setName($host_name) + ->setURI($host_uri); + + $attendees = array(); + foreach ($invitees as $invitee) { + $invitee_phid = $invitee->getInviteePHID(); + $invitee_handle = $handles[$invitee_phid]; + $invitee_name = $invitee_handle->getFullName(); + $invitee_uri = $invitee_handle->getURI(); + $invitee_uri = PhabricatorEnv::getURI($invitee_uri); + + switch ($invitee->getStatus()) { + case PhabricatorCalendarEventInvitee::STATUS_ATTENDING: + $status = PhutilCalendarUserNode::STATUS_ACCEPTED; + break; + case PhabricatorCalendarEventInvitee::STATUS_DECLINED: + $status = PhutilCalendarUserNode::STATUS_DECLINED; + break; + case PhabricatorCalendarEventInvitee::STATUS_INVITED: + default: + $status = PhutilCalendarUserNode::STATUS_INVITED; + break; + } + + $attendees[] = id(new PhutilCalendarUserNode()) + ->setName($invitee_name) + ->setURI($invitee_uri) + ->setStatus($status); + } + + $node = id(new PhutilCalendarEventNode()) + ->setUID($uid) + ->setName($this->getName()) + ->setDescription($this->getDescription()) + ->setCreatedDateTime($created) + ->setModifiedDateTime($modified) + ->setStartDateTime($date_start) + ->setEndDateTime($date_end) + ->setOrganizer($organizer) + ->setAttendees($attendees); + + return $node; + } + + /* -( Markup Interface )--------------------------------------------------- */ diff --git a/src/applications/celerity/postprocessor/CelerityHighContrastPostprocessor.php b/src/applications/celerity/postprocessor/CelerityHighContrastPostprocessor.php index cce3e91473..e1a8471c6c 100644 --- a/src/applications/celerity/postprocessor/CelerityHighContrastPostprocessor.php +++ b/src/applications/celerity/postprocessor/CelerityHighContrastPostprocessor.php @@ -20,6 +20,7 @@ final class CelerityHighContrastPostprocessor 'thinblueborder' => '#BFCFDA', 'lightblueborder' => '#8C98B8', 'blueborder' => '#626E82', + 'timeline' => '#8C98B8', 'lightgreyborder' => '#555', 'greyborder' => '#333', diff --git a/src/applications/conduit/parametertype/ConduitParameterType.php b/src/applications/conduit/parametertype/ConduitParameterType.php index 6468b099a0..011401433e 100644 --- a/src/applications/conduit/parametertype/ConduitParameterType.php +++ b/src/applications/conduit/parametertype/ConduitParameterType.php @@ -38,6 +38,9 @@ abstract class ConduitParameterType extends Phobject { return $this->getParameterValue($request, $key); } + final public function getKeys($key) { + return $this->getParameterKeys($key); + } final public function getDefaultValue() { return $this->getParameterDefault(); @@ -86,6 +89,10 @@ abstract class ConduitParameterType extends Phobject { return $request[$key]; } + protected function getParameterKeys($key) { + return array($key); + } + abstract protected function getParameterTypeName(); diff --git a/src/applications/config/application/PhabricatorConfigApplication.php b/src/applications/config/application/PhabricatorConfigApplication.php index 869170ccbb..dda50a2b73 100644 --- a/src/applications/config/application/PhabricatorConfigApplication.php +++ b/src/applications/config/application/PhabricatorConfigApplication.php @@ -44,7 +44,6 @@ final class PhabricatorConfigApplication extends PhabricatorApplication { 'edit/(?P[\w\.\-]+)/' => 'PhabricatorConfigEditController', 'group/(?P[^/]+)/' => 'PhabricatorConfigGroupController', 'version/' => 'PhabricatorConfigVersionController', - 'welcome/' => 'PhabricatorConfigWelcomeController', 'database/'. '(?:(?P[^/]+)/'. '(?:(?P[^/]+)/'. diff --git a/src/applications/config/check/PhabricatorSetupCheck.php b/src/applications/config/check/PhabricatorSetupCheck.php index ad779ed63f..7947f5aa79 100644 --- a/src/applications/config/check/PhabricatorSetupCheck.php +++ b/src/applications/config/check/PhabricatorSetupCheck.php @@ -74,6 +74,9 @@ abstract class PhabricatorSetupCheck extends Phobject { $cache = PhabricatorCaches::getSetupCache(); $cache->setKey('phabricator.setup.issue-keys', $keys); + $server_cache = PhabricatorCaches::getServerStateCache(); + $server_cache->setKey('phabricator.in-flight', 1); + if ($update_database) { $db_cache = new PhabricatorKeyValueDatabaseCache(); try { @@ -192,6 +195,22 @@ abstract class PhabricatorSetupCheck extends Phobject { } } + /** + * Test if we've survived through setup on at least one normal request + * without fataling. + * + * If we've made it through setup without hitting any fatals, we switch + * to render a more friendly error page when encountering issues like + * database connection failures. This gives users a smoother experience in + * the face of intermittent failures. + * + * @return bool True if we've made it through setup since the last restart. + */ + final public static function isInFlight() { + $cache = PhabricatorCaches::getServerStateCache(); + return (bool)$cache->getKey('phabricator.in-flight'); + } + final public static function loadAllChecks() { return id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__) diff --git a/src/applications/config/controller/PhabricatorConfigClusterRepositoriesController.php b/src/applications/config/controller/PhabricatorConfigClusterRepositoriesController.php index a61f0e3535..b3fd9915b1 100644 --- a/src/applications/config/controller/PhabricatorConfigClusterRepositoriesController.php +++ b/src/applications/config/controller/PhabricatorConfigClusterRepositoriesController.php @@ -153,6 +153,14 @@ final class PhabricatorConfigClusterRepositoriesController $versions = idx($repository_versions, $repository_phid, array()); + // Filter out any versions for devices which are no longer active. + foreach ($versions as $key => $version) { + $version_device_phid = $version->getDevicePHID(); + if (empty($active_devices[$version_device_phid])) { + unset($versions[$key]); + } + } + $leaders = 0; foreach ($versions as $version) { if ($version->getRepositoryVersion() == $leader_version) { diff --git a/src/applications/config/controller/PhabricatorConfigController.php b/src/applications/config/controller/PhabricatorConfigController.php index b60418de08..5ad0ecbbf8 100644 --- a/src/applications/config/controller/PhabricatorConfigController.php +++ b/src/applications/config/controller/PhabricatorConfigController.php @@ -7,33 +7,47 @@ abstract class PhabricatorConfigController extends PhabricatorController { } public function buildSideNavView($filter = null, $for_app = false) { - $user = $this->getRequest()->getUser(); + $guide_href = new PhutilURI('/guides/'); $nav = new AphrontSideNavFilterView(); $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); $nav->addLabel(pht('Configuration')); - $nav->addFilter('/', pht('Core Settings')); - $nav->addFilter('application/', pht('Application Settings')); - $nav->addFilter('history/', pht('Settings History')); - $nav->addFilter('version/', pht('Version Information')); - $nav->addFilter('all/', pht('All Settings')); + $nav->addFilter('/', + pht('Core Settings'), null, 'fa-gear'); + $nav->addFilter('application/', + pht('Application Settings'), null, 'fa-globe'); + $nav->addFilter('history/', + pht('Settings History'), null, 'fa-history'); + $nav->addFilter('version/', + pht('Version Information'), null, 'fa-download'); + $nav->addFilter('all/', + pht('All Settings'), null, 'fa-list-ul'); $nav->addLabel(pht('Setup')); - $nav->addFilter('issue/', pht('Setup Issues')); - $nav->addFilter('welcome/', pht('Installation Guide')); + $nav->addFilter('issue/', + pht('Setup Issues'), null, 'fa-warning'); + $nav->addFilter(null, + pht('Installation Guide'), $guide_href, 'fa-book'); $nav->addLabel(pht('Database')); - $nav->addFilter('database/', pht('Database Status')); - $nav->addFilter('dbissue/', pht('Database Issues')); + $nav->addFilter('database/', + pht('Database Status'), null, 'fa-heartbeat'); + $nav->addFilter('dbissue/', + pht('Database Issues'), null, 'fa-exclamation-circle'); $nav->addLabel(pht('Cache')); - $nav->addFilter('cache/', pht('Cache Status')); + $nav->addFilter('cache/', + pht('Cache Status'), null, 'fa-home'); $nav->addLabel(pht('Cluster')); - $nav->addFilter('cluster/databases/', pht('Database Servers')); - $nav->addFilter('cluster/notifications/', pht('Notification Servers')); - $nav->addFilter('cluster/repositories/', pht('Repository Servers')); + $nav->addFilter('cluster/databases/', + pht('Database Servers'), null, 'fa-database'); + $nav->addFilter('cluster/notifications/', + pht('Notification Servers'), null, 'fa-bell-o'); + $nav->addFilter('cluster/repositories/', + pht('Repository Servers'), null, 'fa-code'); $nav->addLabel(pht('Modules')); $modules = PhabricatorConfigModule::getAllModules(); foreach ($modules as $key => $module) { - $nav->addFilter('module/'.$key.'/', $module->getModuleName()); + $nav->addFilter('module/'.$key.'/', + $module->getModuleName(), null, 'fa-puzzle-piece'); } return $nav; diff --git a/src/applications/config/controller/PhabricatorConfigWelcomeController.php b/src/applications/config/controller/PhabricatorConfigWelcomeController.php deleted file mode 100644 index 010f3f7a5a..0000000000 --- a/src/applications/config/controller/PhabricatorConfigWelcomeController.php +++ /dev/null @@ -1,411 +0,0 @@ -getViewer(); - - $nav = $this->buildSideNavView(); - $nav->selectFilter('welcome/'); - - $title = pht('Installation Guide'); - - $header = id(new PHUIHeaderView()) - ->setHeader($title) - ->setProfileHeader(true); - - $crumbs = $this - ->buildApplicationCrumbs() - ->addTextCrumb($title) - ->setBorder(true); - - $content = id(new PhabricatorConfigPageView()) - ->setHeader($header) - ->setContent($this->buildWelcomeScreen($request)); - - return $this->newPage() - ->setTitle($title) - ->setCrumbs($crumbs) - ->setNavigation($nav) - ->appendChild($content) - ->addClass('white-background'); - } - - public function buildWelcomeScreen(AphrontRequest $request) { - $viewer = $request->getUser(); - $this->requireResource('config-welcome-css'); - - $content = pht( - "=== Install Phabricator ===\n\n". - "You have successfully installed Phabricator. This screen will guide ". - "you through configuration and orientation. ". - "These steps are optional, and you can go through them in any order. ". - "If you want to get back to this screen later on, you can find it in ". - "the **Config** application under **Welcome Screen**."); - - $setup = array(); - - $setup[] = $this->newItem( - $request, - 'fa-check-square-o green', - $content); - - $issues_resolved = !PhabricatorSetupCheck::getOpenSetupIssueKeys(); - - $setup_href = PhabricatorEnv::getURI('/config/issue/'); - if ($issues_resolved) { - $content = pht( - "=== Resolve Setup Issues ===\n\n". - "You've resolved (or ignored) all outstanding setup issues. ". - "You can review issues in the **Config** application, under ". - "**[[ %s | Setup Issues ]]**.", - $setup_href); - $icon = 'fa-check-square-o green'; - } else { - $content = pht( - "=== Resolve Setup Issues ===\n\n". - "You have some unresolved setup issues to take care of. Click ". - "the link in the yellow banner at the top of the screen to see ". - "them, or find them in the **Config** application under ". - "**[[ %s | Setup Issues ]]**. ". - "Although most setup issues should be resolved, sometimes an issue ". - "is not applicable to an install. ". - "If you don't intend to fix a setup issue (or don't want to fix ". - "it for now), you can use the \"Ignore\" action to mark it as ". - "something you don't plan to deal with.", - $setup_href); - $icon = 'fa-warning red'; - } - - $setup[] = $this->newItem( - $request, - $icon, - $content); - - $configs = id(new PhabricatorAuthProviderConfigQuery()) - ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->execute(); - - $auth_href = PhabricatorEnv::getURI('/auth/'); - $have_auth = (bool)$configs; - if ($have_auth) { - $content = pht( - "=== Login and Registration ===\n\n". - "You've configured at least one authentication provider, so users ". - "can register or log in. ". - "To configure more providers or adjust settings, use the ". - "**[[ %s | Auth Application ]]**.", - $auth_href); - $icon = 'fa-check-square-o green'; - } else { - $content = pht( - "=== Login and Registration ===\n\n". - "You haven't configured any authentication providers yet. ". - "Authentication providers allow users to register accounts and ". - "log in to Phabricator. You can configure Phabricator to accept ". - "credentials like username and password, LDAP, or Google OAuth. ". - "You can configure authentication using the ". - "**[[ %s | Auth Application ]]**.", - $auth_href); - $icon = 'fa-warning red'; - } - - $setup[] = $this->newItem( - $request, - $icon, - $content); - - $config_href = PhabricatorEnv::getURI('/config/'); - - // Just load any config value at all; if one exists the install has figured - // out how to configure things. - $have_config = (bool)id(new PhabricatorConfigEntry())->loadAllWhere( - '1 = 1 LIMIT 1'); - - if ($have_config) { - $content = pht( - "=== Configure Phabricator Settings ===\n\n". - "You've configured at least one setting from the web interface. ". - "To configure more settings later, use the ". - "**[[ %s | Config Application ]]**.", - $config_href); - $icon = 'fa-check-square-o green'; - } else { - $content = pht( - "=== Configure Phabricator Settings ===\n\n". - 'Many aspects of Phabricator are configurable. To explore and '. - 'adjust settings, use the **[[ %s | Config Application ]]**.', - $config_href); - $icon = 'fa-info-circle'; - } - - $setup[] = $this->newItem( - $request, - $icon, - $content); - - $settings_href = PhabricatorEnv::getURI('/settings/'); - - $preferences = id(new PhabricatorUserPreferencesQuery()) - ->setViewer($viewer) - ->withUsers(array($viewer)) - ->executeOne(); - - $have_settings = ($preferences && $preferences->getPreferences()); - - if ($have_settings) { - $content = pht( - "=== Adjust Account Settings ===\n\n". - "You've adjusted at least one setting on your account. ". - "To make more adjustments, visit the ". - "**[[ %s | Settings Application ]]**.", - $settings_href); - $icon = 'fa-check-square-o green'; - } else { - $content = pht( - "=== Adjust Account Settings ===\n\n". - 'You can configure settings for your account by clicking the '. - 'wrench icon in the main menu bar, or visiting the '. - '**[[ %s | Settings Application ]]** directly.', - $settings_href); - $icon = 'fa-info-circle'; - } - - $setup[] = $this->newItem( - $request, - $icon, - $content); - - $dashboard_href = PhabricatorEnv::getURI('/dashboard/'); - $have_dashboard = (bool)PhabricatorDashboardInstall::getDashboard( - $viewer, - PhabricatorHomeApplication::DASHBOARD_DEFAULT, - 'PhabricatorHomeApplication'); - if ($have_dashboard) { - $content = pht( - "=== Customize Home Page ===\n\n". - "You've installed a default dashboard to replace this welcome screen ". - "on the home page. ". - "You can still visit the welcome screen here at any time if you ". - "have steps you want to complete later, or if you feel lonely. ". - "If you've changed your mind about the dashboard you installed, ". - "you can install a different default dashboard with the ". - "**[[ %s | Dashboards Application ]]**.", - $dashboard_href); - $icon = 'fa-check-square-o green'; - } else { - $content = pht( - "=== Customize Home Page ===\n\n". - "When you're done setting things up, you can create a custom ". - "dashboard and install it. Your dashboard will replace this ". - "welcome screen on the Phabricator home page. ". - "Dashboards can show users the information that's most important to ". - "your organization. You can configure them to display things like: ". - "a custom welcome message, a feed of recent activity, or a list of ". - "open tasks, waiting reviews, recent commits, and so on. ". - "After you install a default dashboard, it will replace this page. ". - "You can find this page later by visiting the **Config** ". - "application, under **Welcome Page**. ". - "To get started building a dashboard, use the ". - "**[[ %s | Dashboards Application ]]**. ", - $dashboard_href); - $icon = 'fa-info-circle'; - } - - $setup[] = $this->newItem( - $request, - $icon, - $content); - - $apps_href = PhabricatorEnv::getURI('/applications/'); - $content = pht( - "=== Explore Applications ===\n\n". - "Phabricator is a large suite of applications that work together to ". - "help you develop software, manage tasks, and communicate. A few of ". - "the most commonly used applications are pinned to the left navigation ". - "bar by default.\n\n". - "To explore all of the Phabricator applications, adjust settings, or ". - "uninstall applications you don't plan to use, visit the ". - "**[[ %s | Applications Application ]]**. You can also click the ". - "**Applications** button in the left navigation menu, or search for an ". - "application by name in the main menu bar. ", - $apps_href); - - $explore = array(); - $explore[] = $this->newItem( - $request, - 'fa-globe', - $content); - - // TODO: Restore some sort of "Support" link here, but just nuke it for - // now as we figure stuff out. - - $differential_uri = PhabricatorEnv::getURI('/differential/'); - $differential_create_uri = PhabricatorEnv::getURI( - '/differential/diff/create/'); - $differential_all_uri = PhabricatorEnv::getURI('/differential/query/all/'); - - $differential_user_guide = PhabricatorEnv::getDoclink( - 'Differential User Guide'); - $differential_vs_uri = PhabricatorEnv::getDoclink( - 'User Guide: Review vs Audit'); - - $quick = array(); - $quick[] = $this->newItem( - $request, - 'fa-gear', - pht( - "=== Quick Start: Code Review ===\n\n". - "Review code with **[[ %s | Differential ]]**. ". - "Engineers can use Differential to share, review, and approve ". - "changes to source code. ". - "To get started with code review:\n\n". - " - **[[ %s | Create a Revision ]]** //(Copy and paste a diff from ". - " the command line into the web UI to quickly get a feel for ". - " review.)//\n". - " - **[[ %s | View All Revisions ]]**\n\n". - "For more information, see these articles in the documentation:\n\n". - " - **[[ %s | Differential User Guide ]]**, for a general overview ". - " of Differential.\n". - " - **[[ %s | User Guide: Review vs Audit ]]**, for a discussion ". - " of different code review workflows.", - $differential_uri, - $differential_create_uri, - $differential_all_uri, - $differential_user_guide, - $differential_vs_uri)); - - - $maniphest_uri = PhabricatorEnv::getURI('/maniphest/'); - $maniphest_create_uri = PhabricatorEnv::getURI('/maniphest/task/edit/'); - $maniphest_all_uri = PhabricatorEnv::getURI('/maniphest/query/all/'); - $quick[] = $this->newItem( - $request, - 'fa-anchor', - pht( - "=== Quick Start: Bugs and Tasks ===\n\n". - "Track bugs and tasks in Phabricator with ". - "**[[ %s | Maniphest ]]**. ". - "Users in all roles can use Maniphest to manage current and ". - "planned work and to track bugs and issues. ". - "To get started with bugs and tasks:\n\n". - " - **[[ %s | Create a Task ]]**\n". - " - **[[ %s | View All Tasks ]]**\n", - $maniphest_uri, - $maniphest_create_uri, - $maniphest_all_uri)); - - - $pholio_uri = PhabricatorEnv::getURI('/pholio/'); - $pholio_create_uri = PhabricatorEnv::getURI('/pholio/new/'); - $pholio_all_uri = PhabricatorEnv::getURI('/pholio/query/all/'); - - $quick[] = $this->newItem( - $request, - 'fa-camera-retro', - pht( - "=== Quick Start: Design Review ===\n\n". - "Review proposed designs with **[[ %s | Pholio ]]**. ". - "Designers can use Pholio to share images of what they're working on ". - "and show off things they've made. ". - "To get started with design review:\n\n". - " - **[[ %s | Create a Mock ]]**\n". - " - **[[ %s | View All Mocks ]]**", - $pholio_uri, - $pholio_create_uri, - $pholio_all_uri)); - - - $diffusion_uri = PhabricatorEnv::getURI('/diffusion/edit/'); - $diffusion_create_uri = PhabricatorEnv::getURI('/diffusion/create/'); - $diffusion_all_uri = PhabricatorEnv::getURI('/diffusion/query/all/'); - - $diffusion_user_guide = PhabricatorEnv::getDoclink('Diffusion User Guide'); - $diffusion_setup_guide = PhabricatorEnv::getDoclink( - 'Diffusion User Guide: Repository Hosting'); - - $quick[] = $this->newItem( - $request, - 'fa-code', - pht( - "=== Quick Start: Repositories ===\n\n". - "Manage and browse source code repositories with ". - "**[[ %s | Diffusion ]]**. ". - "Engineers can use Diffusion to browse and audit source code. ". - "You can configure Phabricator to host repositories, or have it ". - "track existing repositories hosted elsewhere (like GitHub, ". - "Bitbucket, or an internal server). ". - "To get started with repositories:\n\n". - " - **[[ %s | Create a New Repository ]]**\n". - " - **[[ %s | View All Repositories ]]**\n\n". - "For more information, see these articles in the documentation:\n\n". - " - **[[ %s | Diffusion User Guide ]]**, for a general overview of ". - " Diffusion.\n". - " - **[[ %s | Diffusion User Guide: Repository Hosting ]]**, ". - " for instructions on configuring repository hosting.\n\n". - "Phabricator supports Git, Mercurial and Subversion.", - $diffusion_uri, - $diffusion_create_uri, - $diffusion_all_uri, - $diffusion_user_guide, - $diffusion_setup_guide)); - - $setup_header = new PHUIRemarkupView( - $viewer, pht('=Setup and Configuration')); - - $explore_header = new PHUIRemarkupView( - $viewer, pht('=Explore Phabricator')); - - $quick_header = new PHUIRemarkupView( - $viewer, pht('=Quick Start Guide')); - - $document = id(new PHUIDocumentViewPro()) - ->setFluid(true) - ->appendChild($setup_header) - ->appendChild($setup) - ->appendChild($explore_header) - ->appendChild($explore) - ->appendChild($quick_header) - ->appendChild($quick); - - return id(new PHUIBoxView()) - ->appendChild($document); - } - - private function newItem(AphrontRequest $request, $icon, $content) { - $viewer = $request->getUser(); - - $icon = id(new PHUIIconView()) - ->setIcon($icon.' fa-2x'); - - $content = new PHUIRemarkupView($viewer, $content); - - $icon = phutil_tag( - 'div', - array( - 'class' => 'config-welcome-icon', - ), - $icon); - - $content = phutil_tag( - 'div', - array( - 'class' => 'config-welcome-content', - ), - $content); - - $view = phutil_tag( - 'div', - array( - 'class' => 'config-welcome-box grouped', - ), - array( - $icon, - $content, - )); - - return $view; - } - -} diff --git a/src/applications/config/response/PhabricatorConfigResponse.php b/src/applications/config/response/PhabricatorConfigResponse.php index d4d2effdf5..588f60255f 100644 --- a/src/applications/config/response/PhabricatorConfigResponse.php +++ b/src/applications/config/response/PhabricatorConfigResponse.php @@ -25,11 +25,21 @@ final class PhabricatorConfigResponse extends AphrontStandaloneHTMLResponse { } protected function getResponseBodyClass() { - return 'setup-fatal'; + if (PhabricatorSetupCheck::isInFlight()) { + return 'setup-fatal in-flight'; + } else { + return 'setup-fatal'; + } } protected function getResponseBody() { - return $this->view->render(); + $view = $this->view; + + if (PhabricatorSetupCheck::isInFlight()) { + return $view->renderInFlight(); + } else { + return $view->render(); + } } protected function buildPlainTextResponseString() { diff --git a/src/applications/config/view/PhabricatorInFlightErrorView.php b/src/applications/config/view/PhabricatorInFlightErrorView.php new file mode 100644 index 0000000000..2f1ad7bc33 --- /dev/null +++ b/src/applications/config/view/PhabricatorInFlightErrorView.php @@ -0,0 +1,41 @@ +message = $message; + return $this; + } + + public function getMessage() { + return $this->message; + } + + public function render() { + return phutil_tag( + 'div', + array( + 'class' => 'in-flight-error-detail', + ), + array( + phutil_tag( + 'h1', + array( + 'class' => 'in-flight-error-title', + ), + pht('A Troublesome Encounter!')), + phutil_tag( + 'div', + array( + 'class' => 'in-flight-error-body', + ), + pht( + 'Woe! This request had its journey cut short by unexpected '. + 'circumstances (%s).', + $this->getMessage())), + )); + } + +} diff --git a/src/applications/config/view/PhabricatorSetupIssueView.php b/src/applications/config/view/PhabricatorSetupIssueView.php index a2a703c436..ca6397133d 100644 --- a/src/applications/config/view/PhabricatorSetupIssueView.php +++ b/src/applications/config/view/PhabricatorSetupIssueView.php @@ -13,6 +13,14 @@ final class PhabricatorSetupIssueView extends AphrontView { return $this->issue; } + public function renderInFlight() { + $issue = $this->getIssue(); + + return id(new PhabricatorInFlightErrorView()) + ->setMessage($issue->getName()) + ->render(); + } + public function render() { $issue = $this->getIssue(); diff --git a/src/applications/conpherence/application/PhabricatorConpherenceApplication.php b/src/applications/conpherence/application/PhabricatorConpherenceApplication.php index 8defc4d2be..4bc4ab70e1 100644 --- a/src/applications/conpherence/application/PhabricatorConpherenceApplication.php +++ b/src/applications/conpherence/application/PhabricatorConpherenceApplication.php @@ -42,7 +42,7 @@ final class PhabricatorConpherenceApplication extends PhabricatorApplication { 'search/(?:query/(?P[^/]+)/)?' => 'ConpherenceRoomListController', 'panel/' => 'ConpherenceNotificationPanelController', - 'widget/(?P[1-9]\d*)/' => 'ConpherenceWidgetController', + 'participant/(?P[1-9]\d*)/' => 'ConpherenceParticipantController', 'update/(?P[1-9]\d*)/' => 'ConpherenceUpdateController', ), ); diff --git a/src/applications/conpherence/conduit/ConpherenceCreateThreadConduitAPIMethod.php b/src/applications/conpherence/conduit/ConpherenceCreateThreadConduitAPIMethod.php index 30540bf242..85f450a648 100644 --- a/src/applications/conpherence/conduit/ConpherenceCreateThreadConduitAPIMethod.php +++ b/src/applications/conpherence/conduit/ConpherenceCreateThreadConduitAPIMethod.php @@ -13,8 +13,9 @@ final class ConpherenceCreateThreadConduitAPIMethod protected function defineParamTypes() { return array( - 'title' => 'optional string', - 'message' => 'required string', + 'title' => 'required string', + 'topic' => 'optional string', + 'message' => 'optional string', 'participantPHIDs' => 'required list', ); } @@ -27,8 +28,8 @@ final class ConpherenceCreateThreadConduitAPIMethod return array( 'ERR_EMPTY_PARTICIPANT_PHIDS' => pht( 'You must specify participant phids.'), - 'ERR_EMPTY_MESSAGE' => pht( - 'You must specify a message.'), + 'ERR_EMPTY_TITLE' => pht( + 'You must specify a title.'), ); } @@ -36,19 +37,21 @@ final class ConpherenceCreateThreadConduitAPIMethod $participant_phids = $request->getValue('participantPHIDs', array()); $message = $request->getValue('message'); $title = $request->getValue('title'); + $topic = $request->getValue('topic'); list($errors, $conpherence) = ConpherenceEditor::createThread( $request->getUser(), $participant_phids, $title, $message, - $request->newContentSource()); + $request->newContentSource(), + $topic); if ($errors) { foreach ($errors as $error_code) { switch ($error_code) { - case ConpherenceEditor::ERROR_EMPTY_MESSAGE: - throw new ConduitException('ERR_EMPTY_MESSAGE'); + case ConpherenceEditor::ERROR_EMPTY_TITLE: + throw new ConduitException('ERR_EMPTY_TITLE'); break; case ConpherenceEditor::ERROR_EMPTY_PARTICIPANTS: throw new ConduitException('ERR_EMPTY_PARTICIPANT_PHIDS'); diff --git a/src/applications/conpherence/constants/ConpherenceWidgetConfigConstants.php b/src/applications/conpherence/constants/ConpherenceWidgetConfigConstants.php deleted file mode 100644 index 36d4f7d617..0000000000 --- a/src/applications/conpherence/constants/ConpherenceWidgetConfigConstants.php +++ /dev/null @@ -1,48 +0,0 @@ - self::UPDATE_URI, - 'widgetRegistry' => self::getWidgetRegistry(), - ); - } - - public static function getWidgetRegistry() { - return array( - 'conpherence-message-pane' => array( - 'name' => pht('Thread'), - 'icon' => 'fa-comment', - 'deviceOnly' => true, - 'hasCreate' => false, - ), - 'widgets-people' => array( - 'name' => pht('Participants'), - 'icon' => 'fa-users', - 'deviceOnly' => false, - 'hasCreate' => true, - 'createData' => array( - 'refreshFromResponse' => true, - 'action' => ConpherenceUpdateActions::ADD_PERSON, - 'customHref' => null, - ), - ), - 'widgets-settings' => array( - 'name' => pht('Notifications'), - 'icon' => 'fa-wrench', - 'deviceOnly' => false, - 'hasCreate' => false, - ), - 'widgets-edit' => array( - 'name' => pht('Edit Room'), - 'icon' => 'fa-pencil', - 'deviceOnly' => false, - 'hasCreate' => false, - ), - ); - } - -} diff --git a/src/applications/conpherence/controller/ConpherenceController.php b/src/applications/conpherence/controller/ConpherenceController.php index 8faae68546..c89923ba83 100644 --- a/src/applications/conpherence/controller/ConpherenceController.php +++ b/src/applications/conpherence/controller/ConpherenceController.php @@ -14,80 +14,96 @@ abstract class ConpherenceController extends PhabricatorController { public function buildApplicationMenu() { $nav = new PHUIListView(); + $conpherence = $this->conpherence; + // Local Links + if ($conpherence) { + $nav->addMenuItem( + id(new PHUIListItemView()) + ->setName(pht('Edit Room')) + ->setType(PHUIListItemView::TYPE_LINK) + ->setHref( + $this->getApplicationURI('update/'.$conpherence->getID()).'/') + ->setWorkflow(true)); + + $nav->addMenuItem( + id(new PHUIListItemView()) + ->setName(pht('Add Participants')) + ->setType(PHUIListItemView::TYPE_LINK) + ->setHref('#') + ->addSigil('conpherence-widget-adder') + ->setMetadata(array('widget' => 'widgets-people'))); + } + + // Global Links + $nav->newLabel(pht('Conpherence')); $nav->newLink( pht('New Room'), $this->getApplicationURI('new/')); - - $nav->addMenuItem( - id(new PHUIListItemView()) - ->setName(pht('Add Participants')) - ->setType(PHUIListItemView::TYPE_LINK) - ->setHref('#') - ->addSigil('conpherence-widget-adder') - ->setMetadata(array('widget' => 'widgets-people'))); + $nav->newLink( + pht('Search Rooms'), + $this->getApplicationURI('search/')); return $nav; } - protected function buildApplicationCrumbs() { - return $this->buildConpherenceApplicationCrumbs(); - } - - protected function buildConpherenceApplicationCrumbs($is_rooms = false) { - $crumbs = parent::buildApplicationCrumbs(); - - if ($is_rooms) { - $crumbs - ->addAction( - id(new PHUIListItemView()) - ->setName(pht('New Room')) - ->setHref($this->getApplicationURI('new/')) - ->setIcon('fa-plus-square') - ->setWorkflow(true)); - } else { - $crumbs - ->addAction( - id(new PHUIListItemView()) - ->setName(pht('New Room')) - ->setHref($this->getApplicationURI('new/')) - ->setIcon('fa-plus-square') - ->setWorkflow(true)) - ->addAction( - id(new PHUIListItemView()) - ->setName(pht('Room')) - ->setHref('#') - ->setIcon('fa-bars') - ->setStyle('display: none;') - ->addClass('device-widgets-selector') - ->addSigil('device-widgets-selector')); - } - return $crumbs; - } - protected function buildHeaderPaneContent( ConpherenceThread $conpherence, array $policy_objects) { assert_instances_of($policy_objects, 'PhabricatorPolicy'); + $viewer = $this->getViewer(); + $header = null; - $crumbs = $this->buildApplicationCrumbs(); - $data = $conpherence->getDisplayData($this->getViewer()); - $crumbs->addCrumb( - id(new PHUICrumbView()) - ->setName($data['title']) - ->setHref('/'.$conpherence->getMonogram())); + if ($conpherence->getID()) { + $data = $conpherence->getDisplayData($this->getViewer()); + $header = id(new PHUIHeaderView()) + ->setHeader($data['title']) + ->setSubheader($data['topic']) + ->addClass((!$data['topic']) ? 'conpherence-no-topic' : null); - return hsprintf( - '%s', - array( - phutil_tag( - 'div', - array( - 'class' => 'header-loading-mask', - ), - ''), - $crumbs, - )); + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $conpherence, + PhabricatorPolicyCapability::CAN_EDIT); + + $header->addActionItem( + id(new PHUIIconCircleView()) + ->setHref( + $this->getApplicationURI('update/'.$conpherence->getID()).'/') + ->setIcon('fa-pencil') + ->addClass('hide-on-device') + ->setColor('violet') + ->setWorkflow(true)); + + $header->addActionItem( + id(new PHUIIconCircleView()) + ->setHref( + $this->getApplicationURI('update/'.$conpherence->getID()).'/'. + '?action='.ConpherenceUpdateActions::NOTIFICATIONS) + ->setIcon('fa-gear') + ->addClass('hide-on-device') + ->setColor('pink') + ->setWorkflow(true)); + + $widget_key = PhabricatorConpherenceWidgetVisibleSetting::SETTINGKEY; + $widget_view = (bool)$viewer->getUserSetting($widget_key, false); + + Javelin::initBehavior( + 'toggle-widget', + array( + 'show' => (int)$widget_view, + 'settingsURI' => '/settings/adjust/?key='.$widget_key, + )); + + $header->addActionItem( + id(new PHUIIconCircleView()) + ->addSigil('conpherence-widget-toggle') + ->setIcon('fa-group') + ->setHref('#') + ->addClass('conpherence-participant-toggle')); + } + + return $header; } } diff --git a/src/applications/conpherence/controller/ConpherenceNewRoomController.php b/src/applications/conpherence/controller/ConpherenceNewRoomController.php index 40dd6b2572..25ae99a468 100644 --- a/src/applications/conpherence/controller/ConpherenceNewRoomController.php +++ b/src/applications/conpherence/controller/ConpherenceNewRoomController.php @@ -16,16 +16,19 @@ final class ConpherenceNewRoomController extends ConpherenceController { $editor = new ConpherenceEditor(); $xactions = array(); + $xactions[] = id(new ConpherenceTransaction()) + ->setTransactionType(ConpherenceTransaction::TYPE_TITLE) + ->setNewValue($request->getStr('title')); + $participants = $request->getArr('participants'); $participants[] = $user->getPHID(); $participants = array_unique($participants); $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS) ->setNewValue(array('+' => $participants)); - $xactions[] = id(new ConpherenceTransaction()) - ->setTransactionType(ConpherenceTransaction::TYPE_TITLE) - ->setNewValue($request->getStr('title')); + ->setTransactionType(ConpherenceTransaction::TYPE_TOPIC) + ->setNewValue($request->getStr('topic')); $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) ->setNewValue($request->getStr('viewPolicy')); @@ -93,6 +96,11 @@ final class ConpherenceNewRoomController extends ConpherenceController { ->setLabel(pht('Name')) ->setName('title') ->setValue($request->getStr('title'))) + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel(pht('Topic')) + ->setName('topic') + ->setValue($request->getStr('topic'))) ->appendChild( id(new AphrontFormTokenizerControl()) ->setName('participants') diff --git a/src/applications/conpherence/controller/ConpherenceParticipantController.php b/src/applications/conpherence/controller/ConpherenceParticipantController.php new file mode 100644 index 0000000000..a82439f311 --- /dev/null +++ b/src/applications/conpherence/controller/ConpherenceParticipantController.php @@ -0,0 +1,38 @@ +getViewer(); + + $conpherence_id = $request->getURIData('id'); + if (!$conpherence_id) { + return new Aphront404Response(); + } + + $conpherence = id(new ConpherenceThreadQuery()) + ->setViewer($viewer) + ->withIDs(array($conpherence_id)) + ->needParticipants(true) + ->executeOne(); + + if (!$conpherence) { + return new Aphront404Response(); + } + + $uri = $this->getApplicationURI('update/'.$conpherence->getID().'/'); + $content = id(new ConpherenceParticipantView()) + ->setUser($this->getViewer()) + ->setConpherence($conpherence) + ->setUpdateURI($uri); + + $content = array('widgets' => $content); + + return id(new AphrontAjaxResponse())->setContent($content); + } + +} diff --git a/src/applications/conpherence/controller/ConpherenceRoomListController.php b/src/applications/conpherence/controller/ConpherenceRoomListController.php index 704f210781..0e23c89a27 100644 --- a/src/applications/conpherence/controller/ConpherenceRoomListController.php +++ b/src/applications/conpherence/controller/ConpherenceRoomListController.php @@ -18,10 +18,6 @@ final class ConpherenceRoomListController extends ConpherenceController { return $this->delegateToController($controller); } - protected function buildApplicationCrumbs() { - return $this->buildConpherenceApplicationCrumbs($is_rooms = true); - } - public function buildApplicationMenu() { return $this->buildRoomsSideNavView(true)->getMenu(); } diff --git a/src/applications/conpherence/controller/ConpherenceUpdateController.php b/src/applications/conpherence/controller/ConpherenceUpdateController.php index 426377b9d9..f8602a15cf 100644 --- a/src/applications/conpherence/controller/ConpherenceUpdateController.php +++ b/src/applications/conpherence/controller/ConpherenceUpdateController.php @@ -107,7 +107,7 @@ final class ConpherenceUpdateController break; case ConpherenceUpdateActions::REMOVE_PERSON: if (!$request->isContinueRequest()) { - // do nothing; we'll display a confirmation dialogue instead + // do nothing; we'll display a confirmation dialog instead break; } $person_phid = $request->getStr('remove_person'); @@ -127,22 +127,16 @@ final class ConpherenceUpdateController } $participant->setSettings(array('notifications' => $notifications)); $participant->save(); + return id(new AphrontRedirectResponse()) + ->setURI('/'.$conpherence->getMonogram()); - $label = PhabricatorConpherenceNotificationsSetting::getSettingLabel( - $notifications); - - $result = pht( - 'Updated notification settings to "%s".', - $label); - - return id(new AphrontAjaxResponse()) - ->setContent($result); break; case ConpherenceUpdateActions::METADATA: $top = $request->getInt('image_y'); $left = $request->getInt('image_x'); $file_id = $request->getInt('file_id'); $title = $request->getStr('title'); + $topic = $request->getStr('topic'); if ($file_id) { $orig_file = id(new PhabricatorFileQuery()) ->setViewer($user) @@ -190,9 +184,13 @@ final class ConpherenceUpdateController ->setNewValue($image_phid); } $title = $request->getStr('title'); + $topic = $request->getStr('topic'); $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType(ConpherenceTransaction::TYPE_TITLE) ->setNewValue($title); + $xactions[] = id(new ConpherenceTransaction()) + ->setTransactionType(ConpherenceTransaction::TYPE_TOPIC) + ->setNewValue($topic); $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) ->setNewValue($request->getStr('viewPolicy')); @@ -269,29 +267,97 @@ final class ConpherenceUpdateController } switch ($action) { + case ConpherenceUpdateActions::NOTIFICATIONS: + $dialog = $this->renderPreferencesDialog($conpherence); + break; case ConpherenceUpdateActions::ADD_PERSON: - $dialogue = $this->renderAddPersonDialogue($conpherence); + $dialog = $this->renderAddPersonDialog($conpherence); break; case ConpherenceUpdateActions::REMOVE_PERSON: - $dialogue = $this->renderRemovePersonDialogue($conpherence); + $dialog = $this->renderRemovePersonDialog($conpherence); break; case ConpherenceUpdateActions::METADATA: default: - $dialogue = $this->renderMetadataDialogue($conpherence, $error_view); + $dialog = $this->renderMetadataDialog($conpherence, $error_view); break; } - return id(new AphrontDialogResponse()) - ->setDialog($dialogue + return + $dialog ->setUser($user) ->setWidth(AphrontDialogView::WIDTH_FORM) ->setSubmitURI($this->getApplicationURI('update/'.$conpherence_id.'/')) ->addSubmitButton() - ->addCancelButton($this->getApplicationURI($conpherence->getID().'/'))); + ->addCancelButton($this->getApplicationURI($conpherence->getID().'/')); } - private function renderAddPersonDialogue( + private function renderPreferencesDialog( + ConpherenceThread $conpherence) { + + $request = $this->getRequest(); + $user = $request->getUser(); + + $participant = $conpherence->getParticipantIfExists($user->getPHID()); + if (!$participant) { + $can_join = PhabricatorPolicyFilter::hasCapability( + $user, + $conpherence, + PhabricatorPolicyCapability::CAN_JOIN); + if ($can_join) { + $text = pht( + 'Notification settings are available after joining the room.'); + } else if ($user->isLoggedIn()) { + $text = pht( + 'Notification settings not applicable to rooms you can not join.'); + } else { + $text = pht( + 'Notification settings are available after logging in and joining '. + 'the room.'); + } + return id(new AphrontDialogView()) + ->setTitle(pht('Room Preferences')) + ->appendParagraph($text); + } + + $notification_key = PhabricatorConpherenceNotificationsSetting::SETTINGKEY; + $notification_default = $user->getUserSetting($notification_key); + + $settings = $participant->getSettings(); + $notifications = idx( + $settings, + 'notifications', + $notification_default); + + $form = id(new AphrontFormView()) + ->setUser($user) + ->setFullWidth(true) + ->appendControl( + id(new AphrontFormRadioButtonControl()) + ->addButton( + PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL, + PhabricatorConpherenceNotificationsSetting::getSettingLabel( + PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL), + '') + ->addButton( + PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY, + PhabricatorConpherenceNotificationsSetting::getSettingLabel( + PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY), + '') + ->setName('notifications') + ->setValue($notifications)); + + return id(new AphrontDialogView()) + ->setTitle(pht('Room Preferences')) + ->addHiddenInput('action', 'notifications') + ->addHiddenInput( + 'latest_transaction_id', + $request->getInt('latest_transaction_id')) + ->appendForm($form); + + } + + private function renderAddPersonDialog( ConpherenceThread $conpherence) { $request = $this->getRequest(); @@ -322,7 +388,7 @@ final class ConpherenceUpdateController return $view; } - private function renderRemovePersonDialogue( + private function renderRemovePersonDialog( ConpherenceThread $conpherence) { $request = $this->getRequest(); @@ -405,7 +471,7 @@ final class ConpherenceUpdateController return $dialog; } - private function renderMetadataDialogue( + private function renderMetadataDialog( ConpherenceThread $conpherence, $error_view) { @@ -419,7 +485,12 @@ final class ConpherenceUpdateController id(new AphrontFormTextControl()) ->setLabel(pht('Title')) ->setName('title') - ->setValue($conpherence->getTitle())); + ->setValue($conpherence->getTitle())) + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel(pht('Topic')) + ->setName('topic') + ->setValue($conpherence->getTopic())); $nopic = $this->getRequest()->getExists('nopic'); $image = $conpherence->getImage(ConpherenceImageData::SIZE_ORIG); @@ -526,7 +597,7 @@ final class ConpherenceUpdateController ->setAfterTransactionID($latest_transaction_id) ->needCropPics(true) ->needParticipantCache($need_participant_cache) - ->needWidgetData($need_widget_data) + ->needParticipants(true) ->needTransactions($need_transactions) ->withIDs(array($conpherence_id)) ->executeOne(); @@ -548,11 +619,10 @@ final class ConpherenceUpdateController $rendered_transactions = idx($data, 'transactions'); $new_latest_transaction_id = idx($data, 'latest_transaction_id'); - $widget_uri = $this->getApplicationURI('update/'.$conpherence->getID().'/'); + $update_uri = $this->getApplicationURI('update/'.$conpherence->getID().'/'); $nav_item = null; $header = null; $people_widget = null; - $file_widget = null; if (!$minimal_display) { switch ($action) { case ConpherenceUpdateActions::METADATA: @@ -571,10 +641,10 @@ final class ConpherenceUpdateController $nav_item = hsprintf('%s', $nav_item); break; case ConpherenceUpdateActions::ADD_PERSON: - $people_widget = id(new ConpherencePeopleWidgetView()) + $people_widget = id(new ConpherenceParticipantView()) ->setUser($user) ->setConpherence($conpherence) - ->setUpdateURI($widget_uri); + ->setUpdateURI($update_uri); $people_widget = hsprintf('%s', $people_widget->render()); break; case ConpherenceUpdateActions::REMOVE_PERSON: @@ -595,7 +665,6 @@ final class ConpherenceUpdateController 'nav_item' => $nav_item, 'conpherence_phid' => $conpherence->getPHID(), 'header' => $header, - 'file_widget' => $file_widget, 'people_widget' => $people_widget, 'aphlictDropdownData' => array( $dropdown_query->getNotificationData(), diff --git a/src/applications/conpherence/controller/ConpherenceViewController.php b/src/applications/conpherence/controller/ConpherenceViewController.php index ea0fa7a295..e13d020626 100644 --- a/src/applications/conpherence/controller/ConpherenceViewController.php +++ b/src/applications/conpherence/controller/ConpherenceViewController.php @@ -9,12 +9,6 @@ final class ConpherenceViewController extends return true; } - protected function buildApplicationCrumbs() { - $crumbs = $this->buildConpherenceApplicationCrumbs(); - $crumbs->setBorder(true); - return $crumbs; - } - public function handleRequest(AphrontRequest $request) { $user = $request->getUser(); @@ -160,7 +154,7 @@ final class ConpherenceViewController extends $button_text = pht('Send'); } else if ($user->isLoggedIn()) { $action = ConpherenceUpdateActions::JOIN_ROOM; - $button_text = pht('Join'); + $button_text = pht('Join Room'); } else { // user not logged in so give them a login button. $login_href = id(new PhutilURI('/auth/start/')) diff --git a/src/applications/conpherence/controller/ConpherenceWidgetController.php b/src/applications/conpherence/controller/ConpherenceWidgetController.php deleted file mode 100644 index 3e749c6cf2..0000000000 --- a/src/applications/conpherence/controller/ConpherenceWidgetController.php +++ /dev/null @@ -1,188 +0,0 @@ -getRequest(); - $user = $request->getUser(); - - $conpherence_id = $request->getURIData('id'); - if (!$conpherence_id) { - return new Aphront404Response(); - } - $conpherence = id(new ConpherenceThreadQuery()) - ->setViewer($user) - ->withIDs(array($conpherence_id)) - ->needWidgetData(true) - ->executeOne(); - if (!$conpherence) { - return new Aphront404Response(); - } - $this->setConpherence($conpherence); - - switch ($request->getStr('widget')) { - case 'widgets-people': - $content = $this->renderPeopleWidgetPaneContent(); - break; - case 'widgets-settings': - $content = $this->renderSettingsWidgetPaneContent(); - break; - default: - $widgets = $this->renderWidgetPaneContent(); - $content = $widgets; - break; - } - return id(new AphrontAjaxResponse())->setContent($content); - } - - private function renderWidgetPaneContent() { - $conpherence = $this->getConpherence(); - - $widgets = array(); - $new_icon = id(new PHUIIconView()) - ->setIcon('fa-plus') - ->setHref($this->getWidgetURI()) - ->setMetadata(array('widget' => null)) - ->addSigil('conpherence-widget-adder'); - $header = javelin_tag( - 'a', - array( - 'href' => '#', - 'sigil' => 'widgets-selector', - ), - pht('Participants')); - - $widgets[] = phutil_tag( - 'div', - array( - 'class' => 'widgets-header', - ), - id(new PHUIHeaderView()) - ->setHeader($header) - ->addActionItem($new_icon)); - $user = $this->getRequest()->getUser(); - // now the widget bodies - $widgets[] = javelin_tag( - 'div', - array( - 'class' => 'widgets-body', - 'id' => 'widgets-people', - 'sigil' => 'widgets-people', - ), - $this->renderPeopleWidgetPaneContent()); - $widgets[] = phutil_tag( - 'div', - array( - 'class' => 'widgets-body', - 'id' => 'widgets-settings', - 'style' => 'display: none', - ), - $this->renderSettingsWidgetPaneContent()); - $widgets[] = phutil_tag( - 'div', - array( - 'class' => 'widgets-body', - 'id' => 'widgets-edit', - 'style' => 'display: none', - )); - - // without this implosion we get "," between each element in our widgets - // array - return array('widgets' => phutil_implode_html('', $widgets)); - } - - private function renderPeopleWidgetPaneContent() { - return id(new ConpherencePeopleWidgetView()) - ->setUser($this->getViewer()) - ->setConpherence($this->getConpherence()) - ->setUpdateURI($this->getWidgetURI()); - } - - - private function renderSettingsWidgetPaneContent() { - $viewer = $this->getViewer(); - $conpherence = $this->getConpherence(); - $participant = $conpherence->getParticipantIfExists($viewer->getPHID()); - if (!$participant) { - $can_join = PhabricatorPolicyFilter::hasCapability( - $viewer, - $conpherence, - PhabricatorPolicyCapability::CAN_JOIN); - if ($can_join) { - $text = pht( - 'Notification settings are available after joining the room.'); - } else if ($viewer->isLoggedIn()) { - $text = pht( - 'Notification settings not applicable to rooms you can not join.'); - } else { - $text = pht( - 'Notification settings are available after logging in and joining '. - 'the room.'); - } - return phutil_tag( - 'div', - array( - 'class' => 'no-settings', - ), - $text); - } - $notification_key = PhabricatorConpherenceNotificationsSetting::SETTINGKEY; - $notification_default = $viewer->getUserSetting($notification_key); - - $settings = $participant->getSettings(); - $notifications = idx( - $settings, - 'notifications', - $notification_default); - $options = id(new AphrontFormRadioButtonControl()) - ->addButton( - PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL, - PhabricatorConpherenceNotificationsSetting::getSettingLabel( - PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL), - '') - ->addButton( - PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY, - PhabricatorConpherenceNotificationsSetting::getSettingLabel( - PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY), - '') - ->setName('notifications') - ->setValue($notifications); - - $layout = array( - $options, - phutil_tag( - 'input', - array( - 'type' => 'hidden', - 'name' => 'action', - 'value' => 'notifications', - )), - phutil_tag( - 'button', - array( - 'type' => 'submit', - 'class' => 'notifications-update', - ), - pht('Save')), - ); - - return phabricator_form( - $viewer, - array( - 'method' => 'POST', - 'action' => $this->getWidgetURI(), - 'sigil' => 'notifications-update', - ), - $layout); - } - - private function getWidgetURI() { - $conpherence = $this->getConpherence(); - return $this->getApplicationURI('update/'.$conpherence->getID().'/'); - } - -} diff --git a/src/applications/conpherence/editor/ConpherenceEditor.php b/src/applications/conpherence/editor/ConpherenceEditor.php index 77dcf84eec..b7b37a3e6d 100644 --- a/src/applications/conpherence/editor/ConpherenceEditor.php +++ b/src/applications/conpherence/editor/ConpherenceEditor.php @@ -18,7 +18,8 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor { array $participant_phids, $title, $message, - PhabricatorContentSource $source) { + PhabricatorContentSource $source, + $topic) { $conpherence = ConpherenceThread::initializeNewRoom($creator); $files = array(); @@ -59,6 +60,11 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor { ->setTransactionType(ConpherenceTransaction::TYPE_TITLE) ->setNewValue($title); } + if (strlen($topic)) { + $xactions[] = id(new ConpherenceTransaction()) + ->setTransactionType(ConpherenceTransaction::TYPE_TOPIC) + ->setNewValue($topic); + } $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) @@ -118,6 +124,7 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor { $types[] = PhabricatorTransactions::TYPE_COMMENT; $types[] = ConpherenceTransaction::TYPE_TITLE; + $types[] = ConpherenceTransaction::TYPE_TOPIC; $types[] = ConpherenceTransaction::TYPE_PARTICIPANTS; $types[] = ConpherenceTransaction::TYPE_FILES; $types[] = ConpherenceTransaction::TYPE_PICTURE; @@ -136,6 +143,8 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor { switch ($xaction->getTransactionType()) { case ConpherenceTransaction::TYPE_TITLE: return $object->getTitle(); + case ConpherenceTransaction::TYPE_TOPIC: + return $object->getTopic(); case ConpherenceTransaction::TYPE_PICTURE: return $object->getImagePHID(ConpherenceImageData::SIZE_ORIG); case ConpherenceTransaction::TYPE_PICTURE_CROP: @@ -156,6 +165,7 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor { switch ($xaction->getTransactionType()) { case ConpherenceTransaction::TYPE_TITLE: + case ConpherenceTransaction::TYPE_TOPIC: case ConpherenceTransaction::TYPE_PICTURE_CROP: return $xaction->getNewValue(); case ConpherenceTransaction::TYPE_PICTURE: @@ -250,6 +260,9 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor { case ConpherenceTransaction::TYPE_TITLE: $object->setTitle($xaction->getNewValue()); break; + case ConpherenceTransaction::TYPE_TOPIC: + $object->setTopic($xaction->getNewValue()); + break; case ConpherenceTransaction::TYPE_PICTURE: $object->setImagePHID( $xaction->getNewValue(), @@ -484,6 +497,7 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor { PhabricatorPolicyCapability::CAN_VIEW); break; case ConpherenceTransaction::TYPE_TITLE: + case ConpherenceTransaction::TYPE_TOPIC: PhabricatorPolicyFilter::requireCapability( $this->requireActor(), $object, @@ -609,6 +623,17 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor { protected function shouldPublishFeedStory( PhabricatorLiskDAO $object, array $xactions) { + + foreach ($xactions as $xaction) { + switch ($xaction->getTransactionType()) { + case ConpherenceTransaction::TYPE_TITLE: + case ConpherenceTransaction::TYPE_TOPIC: + case ConpherenceTransaction::TYPE_PICTURE: + return true; + default: + return false; + } + } return false; } diff --git a/src/applications/conpherence/query/ConpherenceThreadQuery.php b/src/applications/conpherence/query/ConpherenceThreadQuery.php index 6a70faf29f..dc1e761c43 100644 --- a/src/applications/conpherence/query/ConpherenceThreadQuery.php +++ b/src/applications/conpherence/query/ConpherenceThreadQuery.php @@ -9,7 +9,6 @@ final class ConpherenceThreadQuery private $ids; private $participantPHIDs; private $needParticipants; - private $needWidgetData; private $needCropPics; private $needOrigPics; private $needTransactions; @@ -35,11 +34,6 @@ final class ConpherenceThreadQuery return $this; } - public function needWidgetData($need_widget_data) { - $this->needWidgetData = $need_widget_data; - return $this; - } - public function needCropPics($need) { $this->needCropPics = $need; return $this; @@ -116,18 +110,15 @@ final class ConpherenceThreadQuery if ($this->needParticipantCache) { $this->loadCoreHandles($conpherences, 'getRecentParticipantPHIDs'); } - if ($this->needWidgetData || $this->needParticipants) { + if ($this->needParticipants) { $this->loadCoreHandles($conpherences, 'getParticipantPHIDs'); } if ($this->needTransactions) { $this->loadTransactionsAndHandles($conpherences); } - if ($this->needFilePHIDs || $this->needWidgetData) { + if ($this->needFilePHIDs) { $this->loadFilePHIDs($conpherences); } - if ($this->needWidgetData) { - $this->loadWidgetData($conpherences); - } if ($this->needOrigPics || $this->needCropPics) { $this->initImages($conpherences); } @@ -297,101 +288,6 @@ final class ConpherenceThreadQuery return $this; } - private function loadWidgetData(array $conpherences) { - $participant_phids = array(); - $file_phids = array(); - foreach ($conpherences as $conpherence) { - $participant_phids[] = array_keys($conpherence->getParticipants()); - $file_phids[] = $conpherence->getFilePHIDs(); - } - $participant_phids = array_mergev($participant_phids); - $file_phids = array_mergev($file_phids); - - $epochs = CalendarTimeUtil::getCalendarEventEpochs( - $this->getViewer()); - $start_epoch = $epochs['start_epoch']; - $end_epoch = $epochs['end_epoch']; - - $events = array(); - if ($participant_phids) { - // TODO: All of this Calendar code is probably extra-broken, but none - // of it is currently reachable in the UI. - $events = id(new PhabricatorCalendarEventQuery()) - ->setViewer($this->getViewer()) - ->withInvitedPHIDs($participant_phids) - ->withIsCancelled(false) - ->withDateRange($start_epoch, $end_epoch) - ->execute(); - $events = mpull($events, null, 'getPHID'); - } - - $invitees = array(); - foreach ($events as $event_phid => $event) { - foreach ($event->getInvitees() as $invitee) { - $invitees[$invitee->getInviteePHID()][$event_phid] = true; - } - } - - // attached files - $files = array(); - $file_author_phids = array(); - $authors = array(); - if ($file_phids) { - $files = id(new PhabricatorFileQuery()) - ->setViewer($this->getViewer()) - ->withPHIDs($file_phids) - ->execute(); - $files = mpull($files, null, 'getPHID'); - $file_author_phids = mpull($files, 'getAuthorPHID', 'getPHID'); - $authors = id(new PhabricatorHandleQuery()) - ->setViewer($this->getViewer()) - ->withPHIDs($file_author_phids) - ->execute(); - $authors = mpull($authors, null, 'getPHID'); - } - - foreach ($conpherences as $phid => $conpherence) { - $participant_phids = array_keys($conpherence->getParticipants()); - $widget_data = array(); - - $event_phids = array(); - $participant_invites = array_select_keys($invitees, $participant_phids); - foreach ($participant_invites as $invite_set) { - $event_phids += $invite_set; - } - $thread_events = array_select_keys($events, array_keys($event_phids)); - $thread_events = msort($thread_events, 'getDateFrom'); - $widget_data['events'] = $thread_events; - - $conpherence_files = array(); - $files_authors = array(); - foreach ($conpherence->getFilePHIDs() as $curr_phid) { - $curr_file = idx($files, $curr_phid); - if (!$curr_file) { - // this file was deleted or user doesn't have permission to see it - // this is generally weird - continue; - } - $conpherence_files[$curr_phid] = $curr_file; - // some files don't have authors so be careful - $current_author = null; - $current_author_phid = idx($file_author_phids, $curr_phid); - if ($current_author_phid) { - $current_author = $authors[$current_author_phid]; - } - $files_authors[$curr_phid] = $current_author; - } - $widget_data += array( - 'files' => $conpherence_files, - 'files_authors' => $files_authors, - ); - - $conpherence->attachWidgetData($widget_data); - } - - return $this; - } - private function loadOrigPics(array $conpherences) { return $this->loadPics( $conpherences, diff --git a/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php b/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php index 92e5da59b1..f2c172b0f2 100644 --- a/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php +++ b/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php @@ -58,12 +58,12 @@ final class ConpherenceThreadSearchEngine protected function getBuiltinQueryNames() { $names = array(); + $names['all'] = pht('All Rooms'); + if ($this->requireViewer()->isLoggedIn()) { $names['participant'] = pht('Joined Rooms'); } - $names['all'] = pht('All Rooms'); - return $names; } diff --git a/src/applications/conpherence/storage/ConpherenceThread.php b/src/applications/conpherence/storage/ConpherenceThread.php index 376a83899f..ec8449f87f 100644 --- a/src/applications/conpherence/storage/ConpherenceThread.php +++ b/src/applications/conpherence/storage/ConpherenceThread.php @@ -8,6 +8,7 @@ final class ConpherenceThread extends ConpherenceDAO PhabricatorDestructibleInterface { protected $title; + protected $topic; protected $imagePHIDs = array(); protected $messageCount; protected $recentParticipantPHIDs = array(); @@ -20,7 +21,6 @@ final class ConpherenceThread extends ConpherenceDAO private $transactions = self::ATTACHABLE; private $handles = self::ATTACHABLE; private $filePHIDs = self::ATTACHABLE; - private $widgetData = self::ATTACHABLE; private $images = self::ATTACHABLE; public static function initializeNewRoom(PhabricatorUser $sender) { @@ -29,6 +29,7 @@ final class ConpherenceThread extends ConpherenceDAO return id(new ConpherenceThread()) ->setMessageCount(0) ->setTitle('') + ->setTopic('') ->attachParticipants(array()) ->attachFilePHIDs(array()) ->attachImages(array()) @@ -46,6 +47,7 @@ final class ConpherenceThread extends ConpherenceDAO ), self::CONFIG_COLUMN_SCHEMA => array( 'title' => 'text255?', + 'topic' => 'text255', 'messageCount' => 'uint64', 'mailKey' => 'text20', 'joinPolicy' => 'policy', @@ -164,14 +166,6 @@ final class ConpherenceThread extends ConpherenceDAO return $this->assertAttached($this->filePHIDs); } - public function attachWidgetData(array $widget_data) { - $this->widgetData = $widget_data; - return $this; - } - public function getWidgetData() { - return $this->assertAttached($this->widgetData); - } - public function loadImageURI($size) { $file = $this->getImage($size); @@ -342,9 +336,11 @@ final class ConpherenceThread extends ConpherenceDAO $unread_count = $this->getMessageCount() - $user_seen_count; $title = $this->getDisplayTitle($viewer); + $topic = $this->getTopic(); return array( 'title' => $title, + 'topic' => $topic, 'subtitle' => $subtitle, 'unread_count' => $unread_count, 'epoch' => $this->getDateModified(), diff --git a/src/applications/conpherence/storage/ConpherenceTransaction.php b/src/applications/conpherence/storage/ConpherenceTransaction.php index 062b9e4a9f..1090be43c8 100644 --- a/src/applications/conpherence/storage/ConpherenceTransaction.php +++ b/src/applications/conpherence/storage/ConpherenceTransaction.php @@ -4,6 +4,7 @@ final class ConpherenceTransaction extends PhabricatorApplicationTransaction { const TYPE_FILES = 'files'; const TYPE_TITLE = 'title'; + const TYPE_TOPIC = 'topic'; const TYPE_PARTICIPANTS = 'participants'; const TYPE_DATE_MARKER = 'date-marker'; const TYPE_PICTURE = 'picture'; @@ -39,6 +40,7 @@ final class ConpherenceTransaction extends PhabricatorApplicationTransaction { case self::TYPE_PARTICIPANTS: return ($old === null); case self::TYPE_TITLE: + case self::TYPE_TOPIC: case self::TYPE_PICTURE: case self::TYPE_DATE_MARKER: return false; @@ -59,6 +61,7 @@ final class ConpherenceTransaction extends PhabricatorApplicationTransaction { switch ($this->getTransactionType()) { case self::TYPE_TITLE: + case self::TYPE_TOPIC: case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY: case PhabricatorTransactions::TYPE_JOIN_POLICY: @@ -120,6 +123,55 @@ final class ConpherenceTransaction extends PhabricatorApplicationTransaction { return parent::getTitle(); } + public function getTitleForFeed() { + $author_phid = $this->getAuthorPHID(); + $object_phid = $this->getObjectPHID(); + + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $type = $this->getTransactionType(); + switch ($type) { + case self::TYPE_TITLE: + if (strlen($old) && strlen($new)) { + return pht( + '%s renamed %s from "%s" to "%s".', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid), + $old, + $new); + } else { + return pht( + '%s created the room %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + } + break; + break; + case self::TYPE_TOPIC: + if (strlen($new)) { + return pht( + '%s set the topic of %s to "%s".', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid), + $new); + } else if (strlen($old)) { + return pht( + '%s deleted the topic in %s', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + } + break; + case self::TYPE_PICTURE: + return pht( + '%s updated the room image for %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + break; + } + return parent::getTitleForFeed(); + } + private function getRoomTitle() { $author_phid = $this->getAuthorPHID(); @@ -147,6 +199,20 @@ final class ConpherenceTransaction extends PhabricatorApplicationTransaction { } return $title; break; + case self::TYPE_TOPIC: + if ($new) { + $title = pht( + '%s set the topic of this room to "%s".', + $this->renderHandleLink($author_phid), + $new); + } else if ($old) { + $title = pht( + '%s deleted the room topic "%s"', + $this->renderHandleLink($author_phid), + $old); + } + return $title; + break; case self::TYPE_PICTURE: return pht( '%s updated the room image.', diff --git a/src/applications/conpherence/view/ConpherenceDurableColumnView.php b/src/applications/conpherence/view/ConpherenceDurableColumnView.php index d265b37ff7..bf454c882a 100644 --- a/src/applications/conpherence/view/ConpherenceDurableColumnView.php +++ b/src/applications/conpherence/view/ConpherenceDurableColumnView.php @@ -126,7 +126,6 @@ final class ConpherenceDurableColumnView extends AphrontTagView { $classes = array(); $classes[] = 'conpherence-durable-column-header'; $classes[] = 'phabricator-main-menu-background'; - $classes[] = 'sprite-main-header'; $loading_mask = phutil_tag( 'div', @@ -141,12 +140,16 @@ final class ConpherenceDurableColumnView extends AphrontTagView { 'class' => implode(' ', $classes), ), $this->buildHeader()); - $icon_bar = phutil_tag( - 'div', - array( - 'class' => 'conpherence-durable-column-icon-bar', - ), - $this->buildIconBar()); + + $icon_bar = null; + if ($this->conpherences) { + $icon_bar = phutil_tag( + 'div', + array( + 'class' => 'conpherence-durable-column-icon-bar', + ), + $this->buildIconBar()); + } $transactions = $this->buildTransactions(); @@ -283,66 +286,60 @@ final class ConpherenceDurableColumnView extends AphrontTagView { private function buildHeader() { $conpherence = $this->getSelectedConpherence(); - if (!$conpherence) { + $bubble_id = celerity_generate_unique_node_id(); + $dropdown_id = celerity_generate_unique_node_id(); - $header = null; - $settings_button = null; - $settings_menu = null; + $settings_list = new PHUIListView(); + $header_actions = $this->getHeaderActionsConfig($conpherence); + foreach ($header_actions as $action) { + $settings_list->addMenuItem( + id(new PHUIListItemView()) + ->setHref($action['href']) + ->setName($action['name']) + ->setIcon($action['icon']) + ->setDisabled($action['disabled']) + ->addSigil('conpherence-durable-column-header-action') + ->setMetadata(array( + 'action' => $action['key'], + ))); + } - } else { + $settings_menu = phutil_tag( + 'div', + array( + 'id' => $dropdown_id, + 'class' => 'phabricator-main-menu-dropdown phui-list-sidenav '. + 'conpherence-settings-dropdown', + 'sigil' => 'phabricator-notification-menu', + 'style' => 'display: none', + ), + $settings_list); - $bubble_id = celerity_generate_unique_node_id(); - $dropdown_id = celerity_generate_unique_node_id(); + Javelin::initBehavior( + 'aphlict-dropdown', + array( + 'bubbleID' => $bubble_id, + 'dropdownID' => $dropdown_id, + 'local' => true, + 'containerDivID' => 'conpherence-durable-column', + )); - $settings_list = new PHUIListView(); - $header_actions = $this->getHeaderActionsConfig($conpherence); - foreach ($header_actions as $action) { - $settings_list->addMenuItem( - id(new PHUIListItemView()) - ->setHref($action['href']) - ->setName($action['name']) - ->setIcon($action['icon']) - ->setDisabled($action['disabled']) - ->addSigil('conpherence-durable-column-header-action') - ->setMetadata(array( - 'action' => $action['key'], - ))); - } - - $settings_menu = phutil_tag( - 'div', - array( - 'id' => $dropdown_id, - 'class' => 'phabricator-main-menu-dropdown phui-list-sidenav '. - 'conpherence-settings-dropdown', - 'sigil' => 'phabricator-notification-menu', - 'style' => 'display: none', - ), - $settings_list); - - Javelin::initBehavior( - 'aphlict-dropdown', - array( - 'bubbleID' => $bubble_id, - 'dropdownID' => $dropdown_id, - 'local' => true, - 'containerDivID' => 'conpherence-durable-column', - )); - - $item = id(new PHUIListItemView()) - ->setName(pht('Room Actions')) - ->setIcon('fa-bars') - ->addClass('core-menu-item') - ->addSigil('conpherence-settings-menu') - ->setID($bubble_id) - ->setHref('#') - ->setAural(pht('Room Actions')) - ->setOrder(300); - $settings_button = id(new PHUIListView()) - ->addMenuItem($item) - ->addClass('phabricator-dark-menu') - ->addClass('phabricator-application-menu'); + $item = id(new PHUIListItemView()) + ->setName(pht('Room Actions')) + ->setIcon('fa-bars') + ->addClass('core-menu-item') + ->addSigil('conpherence-settings-menu') + ->setID($bubble_id) + ->setHref('#') + ->setAural(pht('Room Actions')) + ->setOrder(300); + $settings_button = id(new PHUIListView()) + ->addMenuItem($item) + ->addClass('phabricator-dark-menu') + ->addClass('phabricator-application-menu'); + $header = null; + if ($conpherence) { $data = $conpherence->getDisplayData($this->getUser()); $header = phutil_tag( 'span', @@ -351,7 +348,7 @@ final class ConpherenceDurableColumnView extends AphrontTagView { $this->getPolicyIcon($conpherence, $this->getPolicyObjects()), $data['title'], )); - } + } return phutil_tag( @@ -372,42 +369,46 @@ final class ConpherenceDurableColumnView extends AphrontTagView { )); } - private function getHeaderActionsConfig(ConpherenceThread $conpherence) { - $can_edit = PhabricatorPolicyFilter::hasCapability( - $this->getUser(), - $conpherence, - PhabricatorPolicyCapability::CAN_EDIT); + private function getHeaderActionsConfig($conpherence) { - return array( - array( + $actions = array(); + if ($conpherence) { + $can_edit = PhabricatorPolicyFilter::hasCapability( + $this->getUser(), + $conpherence, + PhabricatorPolicyCapability::CAN_EDIT); + $actions[] = array( 'name' => pht('Add Participants'), 'disabled' => !$can_edit, 'href' => '/conpherence/update/'.$conpherence->getID().'/', 'icon' => 'fa-plus', 'key' => ConpherenceUpdateActions::ADD_PERSON, - ), - array( + ); + $actions[] = array( 'name' => pht('Edit Room'), 'disabled' => !$can_edit, 'href' => '/conpherence/update/'.$conpherence->getID().'/?nopic', 'icon' => 'fa-pencil', 'key' => ConpherenceUpdateActions::METADATA, - ), - array( + ); + $actions[] = array( 'name' => pht('View in Conpherence'), 'disabled' => false, 'href' => '/'.$conpherence->getMonogram(), 'icon' => 'fa-comments', 'key' => 'go_conpherence', - ), - array( - 'name' => pht('Hide Column'), - 'disabled' => false, - 'href' => '#', - 'icon' => 'fa-times', - 'key' => 'hide_column', - ), + ); + } + + $actions[] = array( + 'name' => pht('Hide Column'), + 'disabled' => false, + 'href' => '#', + 'icon' => 'fa-times', + 'key' => 'hide_column', ); + + return $actions; } private function buildTransactions() { diff --git a/src/applications/conpherence/view/ConpherenceLayoutView.php b/src/applications/conpherence/view/ConpherenceLayoutView.php index 62ca1bb6fa..2b6ed5e0d2 100644 --- a/src/applications/conpherence/view/ConpherenceLayoutView.php +++ b/src/applications/conpherence/view/ConpherenceLayoutView.php @@ -55,17 +55,24 @@ final class ConpherenceLayoutView extends AphrontView { return $this; } + public function getWidgetColumnVisible() { + $widget_key = PhabricatorConpherenceWidgetVisibleSetting::SETTINGKEY; + $user = $this->getUser(); + return (bool)$user->getUserSetting($widget_key, false); + } + public function render() { require_celerity_resource('conpherence-menu-css'); require_celerity_resource('conpherence-message-pane-css'); - require_celerity_resource('conpherence-widget-pane-css'); + require_celerity_resource('conpherence-participant-pane-css'); - $layout_id = celerity_generate_unique_node_id(); + $layout_id = 'conpherence-main-layout'; $selected_id = null; $selected_thread_id = null; $selected_thread_phid = null; $can_edit_selected = null; + $nux = null; if ($this->thread) { $selected_id = $this->thread->getPHID().'-nav-item'; $selected_thread_id = $this->thread->getID(); @@ -74,6 +81,8 @@ final class ConpherenceLayoutView extends AphrontView { $this->getUser(), $this->thread, PhabricatorPolicyCapability::CAN_EDIT); + } else { + $nux = $this->buildNUXView(); } $this->initBehavior('conpherence-menu', array( @@ -90,14 +99,16 @@ final class ConpherenceLayoutView extends AphrontView { 'hasWidgets' => false, )); - $class = null; + $classes = array(); if (!$this->getUser()->isLoggedIn()) { - $class = 'conpherence-logged-out'; + $classes[] = 'conpherence-logged-out'; } - $this->initBehavior( - 'conpherence-widget-pane', - ConpherenceWidgetConfigConstants::getWidgetPaneBehaviorConfig()); + if (!$this->getWidgetColumnVisible()) { + $classes[] = 'hide-widgets'; + } + + $this->initBehavior('conpherence-participant-pane'); return javelin_tag( 'div', @@ -105,7 +116,7 @@ final class ConpherenceLayoutView extends AphrontView { 'id' => $layout_id, 'sigil' => 'conpherence-layout', 'class' => 'conpherence-layout '. - $class. + implode(' ', $classes). ' conpherence-role-'.$this->role, ), array( @@ -138,28 +149,13 @@ final class ConpherenceLayoutView extends AphrontView { 'sigil' => 'conpherence-no-threads', 'style' => 'display: none;', ), - array( - phutil_tag( - 'div', - array( - 'class' => 'text', - ), - pht('You are not in any rooms yet.')), - javelin_tag( - 'a', - array( - 'href' => '/conpherence/new/', - 'class' => 'button grey', - 'sigil' => 'workflow', - ), - pht('Create a Room')), - )), + $nux), javelin_tag( 'div', array( - 'class' => 'conpherence-widget-pane', - 'id' => 'conpherence-widget-pane', - 'sigil' => 'conpherence-widget-pane', + 'class' => 'conpherence-participant-pane', + 'id' => 'conpherence-participant-pane', + 'sigil' => 'conpherence-participant-pane', ), array( phutil_tag( @@ -209,4 +205,55 @@ final class ConpherenceLayoutView extends AphrontView { )); } + private function buildNUXView() { + $viewer = $this->getViewer(); + + $engine = new ConpherenceThreadSearchEngine(); + $engine->setViewer($viewer); + $saved = $engine->buildSavedQueryFromBuiltin('all'); + $query = $engine->buildQueryFromSavedQuery($saved); + $pager = $engine->newPagerForSavedQuery($saved); + $pager->setPageSize(10); + $results = $engine->executeQuery($query, $pager); + $view = $engine->renderResults($results, $saved); + + $create_button = id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('New Room')) + ->setHref('/conpherence/new/') + ->setWorkflow(true) + ->setColor(PHUIButtonView::GREEN); + + if ($results) { + $create_button->setIcon('fa-comments'); + + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Joinable Rooms')) + ->addActionLink($create_button); + + $box = id(new PHUIObjectBoxView()) + ->setHeader($header) + ->setObjectList($view->getObjectList()); + if ($viewer->isLoggedIn()) { + $info = id(new PHUIInfoView()) + ->appendChild(pht('You have not joined any rooms yet.')) + ->setSeverity(PHUIInfoView::SEVERITY_NOTICE); + $box->setInfoView($info); + } + + return $box; + } else { + + $view = id(new PHUIBigInfoView()) + ->setIcon('fa-comments') + ->setTitle(pht('Welcome to Conpherence')) + ->setDescription( + pht('Conpherence lets you create public or private rooms to '. + 'communicate with others.')) + ->addAction($create_button); + + return $view; + } + } + } diff --git a/src/applications/conpherence/view/ConpherenceMenuItemView.php b/src/applications/conpherence/view/ConpherenceMenuItemView.php index 2b5fbcdc20..cc2047b157 100644 --- a/src/applications/conpherence/view/ConpherenceMenuItemView.php +++ b/src/applications/conpherence/view/ConpherenceMenuItemView.php @@ -63,9 +63,11 @@ final class ConpherenceMenuItemView extends AphrontTagView { } protected function getTagAttributes() { - $classes = array('conpherence-menu-item-view'); + $classes = array(); + $classes[] = 'conpherence-menu-item-view'; + $classes[] = 'phui-list-item-href'; return array( - 'class' => $classes, + 'class' => implode(' ', $classes), 'href' => $this->href, ); } diff --git a/src/applications/conpherence/view/ConpherencePeopleWidgetView.php b/src/applications/conpherence/view/ConpherenceParticipantView.php similarity index 58% rename from src/applications/conpherence/view/ConpherencePeopleWidgetView.php rename to src/applications/conpherence/view/ConpherenceParticipantView.php index 0771e4f4a1..912a3b8b21 100644 --- a/src/applications/conpherence/view/ConpherencePeopleWidgetView.php +++ b/src/applications/conpherence/view/ConpherenceParticipantView.php @@ -1,19 +1,34 @@ conpherence = $conpherence; + return $this; + } + + public function setUpdateURI($uri) { + $this->updateURI = $uri; + return $this; + } public function render() { - $conpherence = $this->getConpherence(); - $widget_data = $conpherence->getWidgetData(); - $viewer = $this->getUser(); + $conpherence = $this->conpherence; + $viewer = $this->getViewer(); $participants = $conpherence->getParticipants(); + $count = new PhutilNumber(count($participants)); $handles = $conpherence->getHandles(); + $handles = array_intersect_key($handles, $participants); $head_handles = array_select_keys($handles, array($viewer->getPHID())); $handle_list = mpull($handles, 'getName'); natcasesort($handle_list); $handles = mpull($handles, null, 'getName'); $handles = array_select_keys($handles, $handle_list); + $head_handles = mpull($head_handles, null, 'getName'); $handles = $head_handles + $handles; @@ -28,7 +43,8 @@ final class ConpherencePeopleWidgetView extends ConpherenceWidgetView { if (($user_phid == $viewer->getPHID()) || $can_edit) { $icon = id(new PHUIIconView()) - ->setIcon('fa-times lightbluetext'); + ->setIcon('fa-times') + ->addClass('lightbluetext'); $remove_html = javelin_tag( 'a', array( @@ -67,7 +83,30 @@ final class ConpherencePeopleWidgetView extends ConpherenceWidgetView { )); } - return $body; + $new_icon = id(new PHUIIconView()) + ->setIcon('fa-plus-square') + ->setHref($this->updateURI) + ->setMetadata(array('widget' => null)) + ->addSigil('conpherence-widget-adder'); + + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Participants (%d)', $count)) + ->addClass('widgets-header') + ->addActionItem($new_icon); + + $content = javelin_tag( + 'div', + array( + 'class' => 'widgets-body', + 'id' => 'widgets-people', + 'sigil' => 'widgets-people', + ), + array( + $header, + $body, + )); + + return $content; } } diff --git a/src/applications/conpherence/view/ConpherenceThreadListView.php b/src/applications/conpherence/view/ConpherenceThreadListView.php index a4f4a7de6d..21a4ed1030 100644 --- a/src/applications/conpherence/view/ConpherenceThreadListView.php +++ b/src/applications/conpherence/view/ConpherenceThreadListView.php @@ -31,6 +31,9 @@ final class ConpherenceThreadListView extends AphrontView { $this->addRoomsToMenu($menu, $this->threads, $policy_objects); + $menu = phutil_tag_div('phabricator-side-menu', $menu); + $menu = phutil_tag_div('phui-basic-nav', $menu); + return $menu; } @@ -96,14 +99,7 @@ final class ConpherenceThreadListView extends AphrontView { array $rooms, array $policy_objects) { - $header = $this->renderMenuItemHeader( - pht('Rooms'), - 'conpherence-room-list-header'); - $header->appendChild( - id(new PHUIIconView()) - ->setIcon('fa-search') - ->setHref('/conpherence/search/') - ->setText(pht('Search'))); + $header = $this->renderMenuItemHeader(); $menu->addMenuItem($header); if (empty($rooms)) { @@ -191,11 +187,53 @@ final class ConpherenceThreadListView extends AphrontView { return $menu; } - private function renderMenuItemHeader($title, $class = null) { + private function renderMenuItemHeader() { + $rooms = phutil_tag( + 'a', + array( + 'class' => 'room-list-href', + 'href' => '/conpherence/search/', + ), + pht('Rooms')); + + $new_icon = id(new PHUIIconView()) + ->setIcon('fa-plus-square') + ->addSigil('has-tooltip') + ->setHref('/conpherence/new/') + ->setWorkflow(true) + ->setMetaData(array( + 'tip' => pht('New Room'), + )); + + $search_icon = id(new PHUIIconView()) + ->setIcon('fa-search') + ->addSigil('has-tooltip') + ->setHref('/conpherence/search/') + ->setMetaData(array( + 'tip' => pht('Search Rooms'), + )); + + $icons = phutil_tag( + 'span', + array( + 'class' => 'room-list-icons', + ), + array( + $new_icon, + $search_icon, + )); + + $new_icon = id(new PHUIIconView()) + ->setIcon('fa-plus-square') + ->setHref('/conpherence/new/') + ->setWorkflow(true); + + $custom = phutil_tag_div('grouped', array($rooms, $icons)); + $item = id(new PHUIListItemView()) - ->setType(PHUIListItemView::TYPE_LABEL) - ->setName($title) - ->addClass($class); + ->setType(PHUIListItemView::TYPE_CUSTOM) + ->setName($custom) + ->addClass('conpherence-room-list-header'); return $item; } diff --git a/src/applications/conpherence/view/ConpherenceTransactionView.php b/src/applications/conpherence/view/ConpherenceTransactionView.php index fa94b26ba6..467ddf1411 100644 --- a/src/applications/conpherence/view/ConpherenceTransactionView.php +++ b/src/applications/conpherence/view/ConpherenceTransactionView.php @@ -133,16 +133,6 @@ final class ConpherenceTransactionView extends AphrontView { $transaction = $this->getConpherenceTransaction(); $info = array(); - if ($this->getFullDisplay() && $transaction->getContentSource()) { - $content_source = id(new PhabricatorContentSourceView()) - ->setContentSource($transaction->getContentSource()) - ->setUser($viewer) - ->render(); - if ($content_source) { - $info[] = $content_source; - } - } - Javelin::initBehavior('phabricator-tooltips'); $tip = phabricator_datetime($transaction->getDateCreated(), $viewer); $label = phabricator_time($transaction->getDateCreated(), $viewer); @@ -184,8 +174,6 @@ final class ConpherenceTransactionView extends AphrontView { $label); } - $info = phutil_implode_html(" \xC2\xB7 ", $info); - return phutil_tag( 'span', array( @@ -243,6 +231,7 @@ final class ConpherenceTransactionView extends AphrontView { $content = $transaction->getTitle(); break; case ConpherenceTransaction::TYPE_TITLE: + case ConpherenceTransaction::TYPE_TOPIC: case ConpherenceTransaction::TYPE_PICTURE: case ConpherenceTransaction::TYPE_PICTURE_CROP: case ConpherenceTransaction::TYPE_PARTICIPANTS: diff --git a/src/applications/conpherence/view/ConpherenceWidgetView.php b/src/applications/conpherence/view/ConpherenceWidgetView.php deleted file mode 100644 index b3e704dcd9..0000000000 --- a/src/applications/conpherence/view/ConpherenceWidgetView.php +++ /dev/null @@ -1,24 +0,0 @@ -updateURI = $update_uri; - return $this; - } - public function getUpdateURI() { - return $this->updateURI; - } - - public function setConpherence(ConpherenceThread $conpherence) { - $this->conpherence = $conpherence; - return $this; - } - public function getConpherence() { - return $this->conpherence; - } - -} diff --git a/src/applications/differential/editor/DifferentialTransactionEditor.php b/src/applications/differential/editor/DifferentialTransactionEditor.php index 7eaf5ef52d..3e9fe2b99f 100644 --- a/src/applications/differential/editor/DifferentialTransactionEditor.php +++ b/src/applications/differential/editor/DifferentialTransactionEditor.php @@ -1511,13 +1511,6 @@ final class DifferentialTransactionEditor $packages = PhabricatorOwnersPackage::loadAffectedPackages( $repository, $this->affectedPaths); - - foreach ($packages as $key => $package) { - if ($package->isArchived()) { - unset($packages[$key]); - } - } - if (!$packages) { return array(); } diff --git a/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php b/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php index 2aed146cbf..53fd62fe23 100644 --- a/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php +++ b/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php @@ -29,7 +29,7 @@ final class HeraldDifferentialRevisionAdapter 'Test rules which run when a revision is created or updated.'); } - public function newTestAdapter($object) { + public function newTestAdapter(PhabricatorUser $viewer, $object) { return self::newLegacyAdapter( $object, $object->loadActiveDiff()); diff --git a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php index 29059c283f..ad5aaeb4eb 100644 --- a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php @@ -105,7 +105,11 @@ final class DiffusionBrowseQueryConduitAPIMethod $count = 0; $results = array(); - foreach (explode("\0", rtrim($stdout)) as $line) { + $lines = empty($stdout) + ? array() + : explode("\0", rtrim($stdout)); + + foreach ($lines as $line) { // NOTE: Limit to 5 components so we parse filenames with spaces in them // correctly. // NOTE: The output uses a mixture of tabs and one-or-more spaces to diff --git a/src/applications/diffusion/herald/HeraldCommitAdapter.php b/src/applications/diffusion/herald/HeraldCommitAdapter.php index 759b8afa6f..7b4d26b77c 100644 --- a/src/applications/diffusion/herald/HeraldCommitAdapter.php +++ b/src/applications/diffusion/herald/HeraldCommitAdapter.php @@ -8,7 +8,6 @@ final class HeraldCommitAdapter protected $revision; protected $commit; - protected $commitData; private $commitDiff; protected $affectedPaths; @@ -35,6 +34,23 @@ final class HeraldCommitAdapter 'Test rules which run after a commit is discovered and imported.'); } + public function newTestAdapter(PhabricatorUser $viewer, $object) { + $object = id(new DiffusionCommitQuery()) + ->setViewer($viewer) + ->withPHIDs(array($object->getPHID())) + ->needCommitData(true) + ->executeOne(); + if (!$object) { + throw new Exception( + pht( + 'Failed to reload commit ("%s") to fetch commit data.', + $object->getPHID())); + } + + return id(clone $this) + ->setObject($object); + } + protected function initializeNewAdapter() { $this->commit = $this->newObject(); } @@ -100,33 +116,6 @@ final class HeraldCommitAdapter return pht('This rule can trigger for **repositories** and **projects**.'); } - public function setCommit(PhabricatorRepositoryCommit $commit) { - $viewer = PhabricatorUser::getOmnipotentUser(); - - $repository = id(new PhabricatorRepositoryQuery()) - ->setViewer($viewer) - ->withIDs(array($commit->getRepositoryID())) - ->executeOne(); - if (!$repository) { - throw new Exception(pht('Unable to load repository!')); - } - - $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere( - 'commitID = %d', - $commit->getID()); - if (!$data) { - throw new Exception(pht('Unable to load commit data!')); - } - - $this->commit = clone $commit; - $this->commit->attachRepository($repository); - $this->commit->attachCommitData($data); - - $this->commitData = $data; - - return $this; - } - public function getHeraldName() { return $this->commit->getMonogram(); } @@ -171,7 +160,10 @@ final class HeraldCommitAdapter public function loadDifferentialRevision() { if ($this->affectedRevision === null) { $this->affectedRevision = false; - $data = $this->commitData; + + $commit = $this->getObject(); + $data = $commit->getCommitData(); + $revision_id = $data->getCommitDetail('differential.revisionID'); if ($revision_id) { // NOTE: The Herald rule owner might not actually have access to diff --git a/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php b/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php index 6fa0bac9e8..3be69d356d 100644 --- a/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php +++ b/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php @@ -104,7 +104,7 @@ final class DiffusionRepositoryClusterEngine extends Phobject { // We identify all the cluster leaders and reset their version to 0. // We identify all the cluster followers and demote them. - // This allows the cluter to start over again at version 0 but keep the + // This allows the cluster to start over again at version 0 but keep the // same leaders. if ($versions) { diff --git a/src/applications/doorkeeper/bridge/DoorkeeperBridge.php b/src/applications/doorkeeper/bridge/DoorkeeperBridge.php index b09a1933c1..4a4ee2667b 100644 --- a/src/applications/doorkeeper/bridge/DoorkeeperBridge.php +++ b/src/applications/doorkeeper/bridge/DoorkeeperBridge.php @@ -48,4 +48,32 @@ abstract class DoorkeeperBridge extends Phobject { return null; } + final protected function saveExternalObject( + DoorkeeperObjectRef $ref, + DoorkeeperExternalObject $obj) { + + $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); + try { + $obj->save(); + } catch (AphrontDuplicateKeyQueryException $ex) { + + // In various cases, we may race another process importing the same + // data. If we do, we'll collide on the object key. Load the object + // the other process created and use that. + $obj = id(new DoorkeeperExternalObjectQuery()) + ->setViewer($this->getViewer()) + ->withObjectKeys(array($ref->getObjectKey())) + ->executeOne(); + if (!$obj) { + throw new PhutilProxyException( + pht('Failed to load external object after collision.'), + $ex); + } + + $ref->attachExternalObject($obj); + } + unset($unguarded); + } + + } diff --git a/src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php b/src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php index 4e32239793..ec604e158e 100644 --- a/src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php +++ b/src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php @@ -113,10 +113,7 @@ final class DoorkeeperBridgeAsana extends DoorkeeperBridge { } $this->fillObjectFromData($obj, $result); - - $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); - $obj->save(); - unset($unguarded); + $this->saveExternalObject($ref, $obj); } } diff --git a/src/applications/doorkeeper/bridge/DoorkeeperBridgeGitHubIssue.php b/src/applications/doorkeeper/bridge/DoorkeeperBridgeGitHubIssue.php index 0558b7d8f6..b5d7ddeea5 100644 --- a/src/applications/doorkeeper/bridge/DoorkeeperBridgeGitHubIssue.php +++ b/src/applications/doorkeeper/bridge/DoorkeeperBridgeGitHubIssue.php @@ -78,10 +78,7 @@ final class DoorkeeperBridgeGitHubIssue $obj = $ref->getExternalObject(); $this->fillObjectFromData($obj, $result); - - $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); - $obj->save(); - unset($unguarded); + $this->saveExternalObject($ref, $obj); } } diff --git a/src/applications/doorkeeper/bridge/DoorkeeperBridgeGitHubUser.php b/src/applications/doorkeeper/bridge/DoorkeeperBridgeGitHubUser.php index 3470894e5c..ef31826eb3 100644 --- a/src/applications/doorkeeper/bridge/DoorkeeperBridgeGitHubUser.php +++ b/src/applications/doorkeeper/bridge/DoorkeeperBridgeGitHubUser.php @@ -101,10 +101,7 @@ final class DoorkeeperBridgeGitHubUser $obj = $ref->getExternalObject(); $this->fillObjectFromData($obj, $spec); - - $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); - $obj->save(); - unset($unguarded); + $this->saveExternalObject($ref, $obj); } } diff --git a/src/applications/doorkeeper/bridge/DoorkeeperBridgeJIRA.php b/src/applications/doorkeeper/bridge/DoorkeeperBridgeJIRA.php index c9b40d2e72..199f049dce 100644 --- a/src/applications/doorkeeper/bridge/DoorkeeperBridgeJIRA.php +++ b/src/applications/doorkeeper/bridge/DoorkeeperBridgeJIRA.php @@ -104,10 +104,7 @@ final class DoorkeeperBridgeJIRA extends DoorkeeperBridge { } $this->fillObjectFromData($obj, $result); - - $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); - $obj->save(); - unset($unguarded); + $this->saveExternalObject($ref, $obj); } } diff --git a/src/applications/draft/storage/PhabricatorVersionedDraft.php b/src/applications/draft/storage/PhabricatorVersionedDraft.php index 2840a4249c..0daf13e8c5 100644 --- a/src/applications/draft/storage/PhabricatorVersionedDraft.php +++ b/src/applications/draft/storage/PhabricatorVersionedDraft.php @@ -55,11 +55,27 @@ final class PhabricatorVersionedDraft extends PhabricatorDraftDAO { return $draft; } - return id(new PhabricatorVersionedDraft()) - ->setObjectPHID($object_phid) - ->setAuthorPHID($viewer_phid) - ->setVersion((int)$version) - ->save(); + try { + return id(new self()) + ->setObjectPHID($object_phid) + ->setAuthorPHID($viewer_phid) + ->setVersion((int)$version) + ->save(); + } catch (AphrontDuplicateKeyQueryException $ex) { + $duplicate_exception = $ex; + } + + // In rare cases we can race ourselves, and at one point there was a bug + // which caused the browser to submit two preview requests at exactly + // the same time. If the insert failed with a duplicate key exception, + // try to load the colliding row to recover from it. + + $draft = self::loadDraft($object_phid, $viewer_phid); + if ($draft) { + return $draft; + } + + throw $duplicate_exception; } public static function purgeDrafts( diff --git a/src/applications/guides/application/PhabricatorGuideApplication.php b/src/applications/guides/application/PhabricatorGuideApplication.php index f60b66bfb2..ad353a3c3e 100644 --- a/src/applications/guides/application/PhabricatorGuideApplication.php +++ b/src/applications/guides/application/PhabricatorGuideApplication.php @@ -18,10 +18,6 @@ final class PhabricatorGuideApplication extends PhabricatorApplication { return 'fa-map-o'; } - public function isPrototype() { - return true; - } - public function getApplicationGroup() { return self::GROUP_UTILITIES; } diff --git a/src/applications/guides/controller/PhabricatorGuideController.php b/src/applications/guides/controller/PhabricatorGuideController.php index e7584cea00..fe5f71d97b 100644 --- a/src/applications/guides/controller/PhabricatorGuideController.php +++ b/src/applications/guides/controller/PhabricatorGuideController.php @@ -8,7 +8,7 @@ abstract class PhabricatorGuideController extends PhabricatorController { $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); $nav->addLabel(pht('Guides')); - $modules = PhabricatorGuideModule::getAllModules(); + $modules = PhabricatorGuideModule::getEnabledModules(); foreach ($modules as $key => $module) { $nav->addFilter($key.'/', $module->getModuleName()); } diff --git a/src/applications/guides/controller/PhabricatorGuideModuleController.php b/src/applications/guides/controller/PhabricatorGuideModuleController.php index c971195e60..bc34b650ff 100644 --- a/src/applications/guides/controller/PhabricatorGuideModuleController.php +++ b/src/applications/guides/controller/PhabricatorGuideModuleController.php @@ -7,10 +7,11 @@ final class PhabricatorGuideModuleController $viewer = $this->getViewer(); $key = $request->getURIData('module'); + $all_modules = PhabricatorGuideModule::getEnabledModules(); + if (!$key) { - $key = 'welcome'; + $key = key($all_modules); } - $all_modules = PhabricatorGuideModule::getAllModules(); $nav = $this->buildSideNavView(); $nav->selectFilter($key.'/'); diff --git a/src/applications/guides/module/PhabricatorGuideInstallModule.php b/src/applications/guides/module/PhabricatorGuideInstallModule.php index 819cbf363e..7a46b20027 100644 --- a/src/applications/guides/module/PhabricatorGuideInstallModule.php +++ b/src/applications/guides/module/PhabricatorGuideInstallModule.php @@ -14,6 +14,13 @@ final class PhabricatorGuideInstallModule extends PhabricatorGuideModule { return 20; } + public function getIsModuleEnabled() { + if (PhabricatorEnv::getEnvConfig('cluster.instance')) { + return false; + } + return true; + } + public function renderModuleStatus(AphrontRequest $request) { $viewer = $request->getViewer(); @@ -25,13 +32,11 @@ final class PhabricatorGuideInstallModule extends PhabricatorGuideModule { if ($issues_resolved) { $icon = 'fa-check'; $icon_bg = 'bg-green'; - $skip = null; $description = pht( "You've resolved (or ignored) all outstanding setup issues."); } else { $icon = 'fa-warning'; $icon_bg = 'bg-red'; - $skip = '#'; $description = pht('You have some unresolved setup issues to take care of.'); } @@ -41,7 +46,6 @@ final class PhabricatorGuideInstallModule extends PhabricatorGuideModule { ->setHref($href) ->setIcon($icon) ->setIconBackground($icon_bg) - ->setSkipHref($skip) ->setDescription($description); $guide_items->addItem($item); @@ -55,13 +59,11 @@ final class PhabricatorGuideInstallModule extends PhabricatorGuideModule { if ($have_auth) { $icon = 'fa-check'; $icon_bg = 'bg-green'; - $skip = null; $description = pht( "You've configured at least one authentication provider."); } else { $icon = 'fa-key'; $icon_bg = 'bg-sky'; - $skip = '#'; $description = pht( 'Authentication providers allow users to register accounts and '. 'log in to Phabricator.'); @@ -72,7 +74,6 @@ final class PhabricatorGuideInstallModule extends PhabricatorGuideModule { ->setHref($href) ->setIcon($icon) ->setIconBackground($icon_bg) - ->setSkipHref($skip) ->setDescription($description); $guide_items->addItem($item); @@ -88,13 +89,11 @@ final class PhabricatorGuideInstallModule extends PhabricatorGuideModule { if ($have_config) { $icon = 'fa-check'; $icon_bg = 'bg-green'; - $skip = null; $description = pht( "You've configured at least one setting from the web interface."); } else { $icon = 'fa-sliders'; $icon_bg = 'bg-sky'; - $skip = '#'; $description = pht( 'Learn how to configure mail and other options in Phabricator.'); } @@ -104,7 +103,6 @@ final class PhabricatorGuideInstallModule extends PhabricatorGuideModule { ->setHref($href) ->setIcon($icon) ->setIconBackground($icon_bg) - ->setSkipHref($skip) ->setDescription($description); $guide_items->addItem($item); @@ -120,13 +118,11 @@ final class PhabricatorGuideInstallModule extends PhabricatorGuideModule { if ($have_settings) { $icon = 'fa-check'; $icon_bg = 'bg-green'; - $skip = null; $description = pht( "You've adjusted at least one setting on your account."); } else { $icon = 'fa-wrench'; $icon_bg = 'bg-sky'; - $skip = '#'; $description = pht( 'Configure account settings for all users, or just yourself'); } @@ -136,25 +132,21 @@ final class PhabricatorGuideInstallModule extends PhabricatorGuideModule { ->setHref($href) ->setIcon($icon) ->setIconBackground($icon_bg) - ->setSkipHref($skip) ->setDescription($description); $guide_items->addItem($item); $title = pht('Notification Server'); - $href = PhabricatorEnv::getURI('/config/notifications/'); - // TODO: Wire up a notifications check - $have_notifications = false; + $href = PhabricatorEnv::getURI('/config/edit/notification.servers/'); + $have_notifications = PhabricatorEnv::getEnvConfig('notification.servers'); if ($have_notifications) { $icon = 'fa-check'; $icon_bg = 'bg-green'; - $skip = null; $description = pht( "You've set up a real-time notification server."); } else { $icon = 'fa-bell'; $icon_bg = 'bg-sky'; - $skip = '#'; $description = pht( 'Phabricator can deliver notifications in real-time with WebSockets.'); } @@ -164,12 +156,23 @@ final class PhabricatorGuideInstallModule extends PhabricatorGuideModule { ->setHref($href) ->setIcon($icon) ->setIconBackground($icon_bg) - ->setSkipHref($skip) ->setDescription($description); $guide_items->addItem($item); - return $guide_items; + $intro = pht( + 'Phabricator has been successfully installed. These next guides will '. + 'take you through configuration and new user orientation. '. + 'These steps are optional, and you can go through them in any order. '. + 'If you want to get back to this guide later on, you can find it in '. + '{icon globe} **Applications** under {icon map-o} **Guides**.'); + + $intro = new PHUIRemarkupView($viewer, $intro); + + $intro = id(new PHUIDocumentViewPro()) + ->appendChild($intro); + + return array($intro, $guide_items); } diff --git a/src/applications/guides/module/PhabricatorGuideModule.php b/src/applications/guides/module/PhabricatorGuideModule.php index 5ae7b6bf16..dcac14c0f5 100644 --- a/src/applications/guides/module/PhabricatorGuideModule.php +++ b/src/applications/guides/module/PhabricatorGuideModule.php @@ -5,6 +5,7 @@ abstract class PhabricatorGuideModule extends Phobject { abstract public function getModuleKey(); abstract public function getModuleName(); abstract public function getModulePosition(); + abstract public function getIsModuleEnabled(); abstract public function renderModuleStatus(AphrontRequest $request); final public static function getAllModules() { @@ -15,4 +16,13 @@ abstract class PhabricatorGuideModule extends Phobject { ->execute(); } + final public static function getEnabledModules() { + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->setUniqueMethod('getModuleKey') + ->setSortMethod('getModulePosition') + ->setFilterMethod('getIsModuleEnabled') + ->execute(); + } + } diff --git a/src/applications/guides/module/PhabricatorGuideQuickStartModule.php b/src/applications/guides/module/PhabricatorGuideQuickStartModule.php index 64ee4d8534..472d8acaa8 100644 --- a/src/applications/guides/module/PhabricatorGuideQuickStartModule.php +++ b/src/applications/guides/module/PhabricatorGuideQuickStartModule.php @@ -14,78 +14,29 @@ final class PhabricatorGuideQuickStartModule extends PhabricatorGuideModule { return 30; } + public function getIsModuleEnabled() { + return true; + } + public function renderModuleStatus(AphrontRequest $request) { $viewer = $request->getViewer(); + $instance = PhabricatorEnv::getEnvConfig('cluster.instance'); $guide_items = new PhabricatorGuideListView(); - $title = pht('Configure Applications'); - $apps_check = true; - $href = PhabricatorEnv::getURI('/applications/'); - if ($apps_check) { - $icon = 'fa-check'; - $icon_bg = 'bg-green'; - $skip = null; - $description = pht( - "You've uninstalled any unneeded applications for now."); - } else { - $icon = 'fa-globe'; - $icon_bg = 'bg-sky'; - $skip = '#'; - $description = - pht('Use all our applications, or uninstall the ones you don\'t want.'); - } - - $item = id(new PhabricatorGuideItemView()) - ->setTitle($title) - ->setHref($href) - ->setIcon($icon) - ->setIconBackground($icon_bg) - ->setSkipHref($skip) - ->setDescription($description); - $guide_items->addItem($item); - - - $title = pht('Invite Collaborators'); - $people_check = true; - $href = PhabricatorEnv::getURI('/people/invite/'); - if ($people_check) { - $icon = 'fa-check'; - $icon_bg = 'bg-green'; - $skip = null; - $description = pht( - 'You will not be alone on this journey.'); - } else { - $icon = 'fa-group'; - $icon_bg = 'bg-sky'; - $skip = '#'; - $description = - pht('Invite the rest of your team to get started on Phabricator.'); - } - - $item = id(new PhabricatorGuideItemView()) - ->setTitle($title) - ->setHref($href) - ->setIcon($icon) - ->setIconBackground($icon_bg) - ->setSkipHref($skip) - ->setDescription($description); - $guide_items->addItem($item); - - $title = pht('Create a Repository'); - $repository_check = true; + $repository_check = id(new PhabricatorRepositoryQuery()) + ->setViewer($viewer) + ->execute(); $href = PhabricatorEnv::getURI('/diffusion/'); if ($repository_check) { $icon = 'fa-check'; $icon_bg = 'bg-green'; - $skip = null; $description = pht( "You've created at least one repository."); } else { $icon = 'fa-code'; $icon_bg = 'bg-sky'; - $skip = '#'; $description = pht('If you are here for code review, let\'s set up your first '. 'repository.'); @@ -96,24 +47,23 @@ final class PhabricatorGuideQuickStartModule extends PhabricatorGuideModule { ->setHref($href) ->setIcon($icon) ->setIconBackground($icon_bg) - ->setSkipHref($skip) ->setDescription($description); $guide_items->addItem($item); $title = pht('Create a Project'); - $project_check = true; + $project_check = id(new PhabricatorProjectQuery()) + ->setViewer($viewer) + ->execute(); $href = PhabricatorEnv::getURI('/project/'); if ($project_check) { $icon = 'fa-check'; $icon_bg = 'bg-green'; - $skip = null; $description = pht( "You've created at least one project."); } else { $icon = 'fa-briefcase'; $icon_bg = 'bg-sky'; - $skip = '#'; $description = pht('Project tags define everything. Create them for teams, tags, '. 'or actual projects.'); @@ -124,24 +74,49 @@ final class PhabricatorGuideQuickStartModule extends PhabricatorGuideModule { ->setHref($href) ->setIcon($icon) ->setIconBackground($icon_bg) - ->setSkipHref($skip) ->setDescription($description); $guide_items->addItem($item); - $title = pht('Build a Dashboard'); - $dashboard_check = true; - $href = PhabricatorEnv::getURI('/dashboard/'); - if ($dashboard_check) { + $title = pht('Create a Task'); + $task_check = id(new ManiphestTaskQuery()) + ->setViewer($viewer) + ->execute(); + $href = PhabricatorEnv::getURI('/maniphest/'); + if ($task_check) { + $icon = 'fa-check'; + $icon_bg = 'bg-green'; + $description = pht( + "You've created at least one task."); + } else { + $icon = 'fa-anchor'; + $icon_bg = 'bg-sky'; + $description = + pht('Create some work for the interns in Maniphest.'); + } + + $item = id(new PhabricatorGuideItemView()) + ->setTitle($title) + ->setHref($href) + ->setIcon($icon) + ->setIconBackground($icon_bg) + ->setDescription($description); + $guide_items->addItem($item); + + $title = pht('Build a Dashboard'); + $have_dashboard = (bool)PhabricatorDashboardInstall::getDashboard( + $viewer, + PhabricatorHomeApplication::DASHBOARD_DEFAULT, + 'PhabricatorHomeApplication'); + $href = PhabricatorEnv::getURI('/dashboard/'); + if ($have_dashboard) { $icon = 'fa-check'; $icon_bg = 'bg-green'; - $skip = null; $description = pht( "You've created at least one dashboard."); } else { $icon = 'fa-dashboard'; $icon_bg = 'bg-sky'; - $skip = '#'; $description = pht('Customize the default homepage layout and items.'); } @@ -151,24 +126,21 @@ final class PhabricatorGuideQuickStartModule extends PhabricatorGuideModule { ->setHref($href) ->setIcon($icon) ->setIconBackground($icon_bg) - ->setSkipHref($skip) ->setDescription($description); $guide_items->addItem($item); $title = pht('Personalize your Install'); - $ui_check = true; - $href = PhabricatorEnv::getURI('/config/group/ui/'); - if ($dashboard_check) { + $wordmark = PhabricatorEnv::getEnvConfig('ui.logo'); + $href = PhabricatorEnv::getURI('/config/edit/ui.logo/'); + if ($wordmark) { $icon = 'fa-check'; $icon_bg = 'bg-green'; - $skip = null; $description = pht( 'It looks amazing, good work. Home Sweet Home.'); } else { $icon = 'fa-home'; $icon_bg = 'bg-sky'; - $skip = '#'; $description = pht('Change the name and add your company logo, just to give it a '. 'little extra polish.'); @@ -179,11 +151,65 @@ final class PhabricatorGuideQuickStartModule extends PhabricatorGuideModule { ->setHref($href) ->setIcon($icon) ->setIconBackground($icon_bg) - ->setSkipHref($skip) ->setDescription($description); $guide_items->addItem($item); - return $guide_items; + $title = pht('Explore Applications'); + $href = PhabricatorEnv::getURI('/applications/'); + $icon = 'fa-globe'; + $icon_bg = 'bg-sky'; + $description = + pht('See all the applications included in Phabricator.'); + + $item = id(new PhabricatorGuideItemView()) + ->setTitle($title) + ->setHref($href) + ->setIcon($icon) + ->setIconBackground($icon_bg) + ->setDescription($description); + $guide_items->addItem($item); + + if (!$instance) { + $title = pht('Invite Collaborators'); + $people_check = id(new PhabricatorPeopleQuery()) + ->setViewer($viewer) + ->execute(); + $people = count($people_check); + $href = PhabricatorEnv::getURI('/people/invite/send/'); + if ($people > 1) { + $icon = 'fa-check'; + $icon_bg = 'bg-green'; + $description = pht( + 'Your invitations have been accepted. You will not be alone on '. + 'this journey.'); + } else { + $icon = 'fa-group'; + $icon_bg = 'bg-sky'; + $description = + pht('Invite the rest of your team to get started on Phabricator.'); + } + } + + $item = id(new PhabricatorGuideItemView()) + ->setTitle($title) + ->setHref($href) + ->setIcon($icon) + ->setIconBackground($icon_bg) + ->setDescription($description); + $guide_items->addItem($item); + + $intro = pht( + 'If your new to Phabricator, these optional steps can help you learn '. + 'the basics. Conceptually, Phabricator is structured as a graph, and '. + 'repositories, tasks, and projects are all independent from each other. '. + 'Feel free to set up Phabricator for how you work best, and explore '. + 'these features at your own pace.'); + + $intro = new PHUIRemarkupView($viewer, $intro); + $intro = id(new PHUIDocumentViewPro()) + ->appendChild($intro); + + return array($intro, $guide_items); } diff --git a/src/applications/guides/module/PhabricatorGuideWelcomeModule.php b/src/applications/guides/module/PhabricatorGuideWelcomeModule.php deleted file mode 100644 index 2b2bb80184..0000000000 --- a/src/applications/guides/module/PhabricatorGuideWelcomeModule.php +++ /dev/null @@ -1,34 +0,0 @@ -getViewer(); - - $content = pht( - 'You have successfully installed Phabricator. These next guides will '. - 'take you through configuration and new user orientation. '. - 'These steps are optional, and you can go through them in any order. '. - 'If you want to get back to this guide later on, you can find it in '. - 'the **Config** application under **Welcome Guide**.'); - - $content = new PHUIRemarkupView($viewer, $content); - - return id(new PHUIDocumentViewPro()) - ->appendChild($content); - - } - -} diff --git a/src/applications/herald/adapter/HeraldAdapter.php b/src/applications/herald/adapter/HeraldAdapter.php index ac227819f9..78ce862945 100644 --- a/src/applications/herald/adapter/HeraldAdapter.php +++ b/src/applications/herald/adapter/HeraldAdapter.php @@ -224,7 +224,7 @@ abstract class HeraldAdapter extends Phobject { return $this->isTestAdapterForObject($object); } - public function newTestAdapter($object) { + public function newTestAdapter(PhabricatorUser $viewer, $object) { return id(clone $this) ->setObject($object); } diff --git a/src/applications/herald/controller/HeraldTestConsoleController.php b/src/applications/herald/controller/HeraldTestConsoleController.php index fae92864f1..21bedcd848 100644 --- a/src/applications/herald/controller/HeraldTestConsoleController.php +++ b/src/applications/herald/controller/HeraldTestConsoleController.php @@ -142,7 +142,9 @@ final class HeraldTestConsoleController extends HeraldController { if ($request->isFormPost() && $adapter_key) { if (isset($can_select[$adapter_key])) { - $adapter = $can_select[$adapter_key]->newTestAdapter($object); + $adapter = $can_select[$adapter_key]->newTestAdapter( + $viewer, + $object); $this->setTestAdapter($adapter); return null; } diff --git a/src/applications/home/controller/PhabricatorHomeMainController.php b/src/applications/home/controller/PhabricatorHomeMainController.php index fce4729a6d..405959fe67 100644 --- a/src/applications/home/controller/PhabricatorHomeMainController.php +++ b/src/applications/home/controller/PhabricatorHomeMainController.php @@ -209,7 +209,7 @@ final class PhabricatorHomeMainController extends PhabricatorHomeController { $content = pht(<<getName().' '.$application->getShortDescription(); $img = 'phui-font-fa phui-icon-view '.$application->getIcon(); $results[] = id(new PhabricatorTypeaheadResult()) diff --git a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php index c585f4022f..4fbd717a2a 100644 --- a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php +++ b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php @@ -358,6 +358,13 @@ final class PhabricatorOwnersPackageQuery $best_match = null; $include = false; + // If this package is archived, it's no longer a controlling package + // for the given path. In particular, it can not force active packages + // with weak dominion to give up control. + if ($package->isArchived()) { + continue; + } + foreach ($package->getPaths() as $package_path) { if ($package_path->getRepositoryPHID() != $repository_phid) { // If this path is for some other repository, skip it. diff --git a/src/applications/owners/storage/PhabricatorOwnersPackage.php b/src/applications/owners/storage/PhabricatorOwnersPackage.php index 35f8ac58b5..f2290d2866 100644 --- a/src/applications/owners/storage/PhabricatorOwnersPackage.php +++ b/src/applications/owners/storage/PhabricatorOwnersPackage.php @@ -211,10 +211,13 @@ final class PhabricatorOwnersPackage $conn, 'SELECT pkg.id, pkg.dominion, p.excluded, p.path FROM %T pkg JOIN %T p ON p.packageID = pkg.id - WHERE p.path IN (%Ls) %Q', + WHERE p.path IN (%Ls) AND pkg.status IN (%Ls) %Q', $package->getTableName(), $path->getTableName(), $chunk, + array( + self::STATUS_ACTIVE, + ), $repository_clause); } $rows = array_mergev($rows); diff --git a/src/applications/phame/controller/post/PhamePostEditController.php b/src/applications/phame/controller/post/PhamePostEditController.php index 91df519adb..88cd109d81 100644 --- a/src/applications/phame/controller/post/PhamePostEditController.php +++ b/src/applications/phame/controller/post/PhamePostEditController.php @@ -21,40 +21,35 @@ final class PhamePostEditController extends PhamePostController { $post = id(new PhamePostQuery()) ->setViewer($viewer) ->withIDs(array($id)) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) ->executeOne(); if (!$post) { return new Aphront404Response(); } - $blog_id = $post->getBlog()->getID(); + $blog = $post->getBlog(); } else { $blog_id = head($request->getArr('blog')); if (!$blog_id) { $blog_id = $request->getStr('blog'); } - } - $query = id(new PhameBlogQuery()) - ->setViewer($viewer) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )); + $query = id(new PhameBlogQuery()) + ->setViewer($viewer) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )); - if (ctype_digit($blog_id)) { - $query->withIDs(array($blog_id)); - } else { - $query->withPHIDs(array($blog_id)); - } + if (ctype_digit($blog_id)) { + $query->withIDs(array($blog_id)); + } else { + $query->withPHIDs(array($blog_id)); + } - $blog = $query->executeOne(); - if (!$blog) { - return new Aphront404Response(); + $blog = $query->executeOne(); + if (!$blog) { + return new Aphront404Response(); + } } $this->setBlog($blog); diff --git a/src/applications/phriction/controller/PhrictionDocumentController.php b/src/applications/phriction/controller/PhrictionDocumentController.php index 7601888987..e455bd86d1 100644 --- a/src/applications/phriction/controller/PhrictionDocumentController.php +++ b/src/applications/phriction/controller/PhrictionDocumentController.php @@ -306,12 +306,23 @@ final class PhrictionDocumentController ->setWorkflow(true)); } - return - $action_view->addAction( - id(new PhabricatorActionView()) - ->setName(pht('View History')) - ->setIcon('fa-list') - ->setHref(PhrictionDocument::getSlugURI($slug, 'history'))); + $action_view->addAction( + id(new PhabricatorActionView()) + ->setName(pht('View History')) + ->setIcon('fa-list') + ->setHref(PhrictionDocument::getSlugURI($slug, 'history'))); + + $print_uri = PhrictionDocument::getSlugURI($slug).'?__print__=1'; + + $action_view->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Printable Page')) + ->setIcon('fa-print') + ->setOpenInNewWindow(true) + ->setHref($print_uri)); + + return $action_view; + } private function renderDocumentChildren($slug) { diff --git a/src/applications/phriction/controller/PhrictionEditController.php b/src/applications/phriction/controller/PhrictionEditController.php index e0e27ad796..038861d136 100644 --- a/src/applications/phriction/controller/PhrictionEditController.php +++ b/src/applications/phriction/controller/PhrictionEditController.php @@ -9,6 +9,7 @@ final class PhrictionEditController $current_version = null; if ($id) { + $is_new = false; $document = id(new PhrictionDocumentQuery()) ->setViewer($viewer) ->withIDs(array($id)) @@ -54,9 +55,11 @@ final class PhrictionEditController if ($document) { $content = $document->getContent(); $current_version = $content->getVersion(); + $is_new = false; } else { $document = PhrictionDocument::initializeNewDocument($viewer, $slug); $content = $document->getContent(); + $is_new = true; } } @@ -91,11 +94,7 @@ final class PhrictionEditController $draft_note->setSeverity(PHUIInfoView::SEVERITY_NOTICE); $draft_note->setTitle(pht('Recovered Draft')); $draft_note->appendChild( - hsprintf( - '

%s

', - pht( - 'Showing a saved draft of your edits, you can %s.', - $discard))); + pht('Showing a saved draft of your edits, you can %s.', $discard)); } else { $content_text = $content->getContent(); $draft_note = null; @@ -112,6 +111,15 @@ final class PhrictionEditController $v_cc = PhabricatorSubscribersQuery::loadSubscribersForPHID( $document->getPHID()); + if ($is_new) { + $v_projects = array(); + } else { + $v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs( + $document->getPHID(), + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); + $v_projects = array_reverse($v_projects); + } + if ($request->isFormPost()) { $title = $request->getStr('title'); @@ -120,7 +128,8 @@ final class PhrictionEditController $current_version = $request->getInt('contentVersion'); $v_view = $request->getStr('viewPolicy'); $v_edit = $request->getStr('editPolicy'); - $v_cc = $request->getArr('cc'); + $v_cc = $request->getArr('cc'); + $v_projects = $request->getArr('projects'); $xactions = array(); $xactions[] = id(new PhrictionTransaction()) @@ -139,6 +148,12 @@ final class PhrictionEditController ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) ->setNewValue(array('=' => $v_cc)); + $proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; + $xactions[] = id(new PhrictionTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) + ->setMetadataValue('edge:type', $proj_edge_type) + ->setNewValue(array('=' => array_fuse($v_projects))); + $editor = id(new PhrictionTransactionEditor()) ->setActor($viewer) ->setContentSourceFromRequest($request) @@ -230,6 +245,12 @@ final class PhrictionEditController ->setName('content') ->setID('document-textarea') ->setUser($viewer)) + ->appendControl( + id(new AphrontFormTokenizerControl()) + ->setLabel(pht('Tags')) + ->setName('projects') + ->setValue($v_projects) + ->setDatasource(new PhabricatorProjectDatasource())) ->appendControl( id(new AphrontFormTokenizerControl()) ->setLabel(pht('Subscribers')) diff --git a/src/applications/phriction/editor/PhrictionTransactionEditor.php b/src/applications/phriction/editor/PhrictionTransactionEditor.php index 4cec1ccdbf..f0606c65a4 100644 --- a/src/applications/phriction/editor/PhrictionTransactionEditor.php +++ b/src/applications/phriction/editor/PhrictionTransactionEditor.php @@ -80,13 +80,14 @@ final class PhrictionTransactionEditor public function getTransactionTypes() { $types = parent::getTransactionTypes(); - $types[] = PhabricatorTransactions::TYPE_COMMENT; $types[] = PhrictionTransaction::TYPE_TITLE; $types[] = PhrictionTransaction::TYPE_CONTENT; $types[] = PhrictionTransaction::TYPE_DELETE; $types[] = PhrictionTransaction::TYPE_MOVE_TO; $types[] = PhrictionTransaction::TYPE_MOVE_AWAY; + $types[] = PhabricatorTransactions::TYPE_EDGE; + $types[] = PhabricatorTransactions::TYPE_COMMENT; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; @@ -428,6 +429,13 @@ final class PhrictionTransactionEditor PhabricatorEnv::getProductionURI($this->contentDiffURI)); } + $description = $object->getContent()->getDescription(); + if (strlen($description)) { + $body->addTextSection( + pht('EDIT NOTES'), + $description); + } + $body->addLinkSection( pht('DOCUMENT DETAIL'), PhabricatorEnv::getProductionURI( diff --git a/src/applications/phriction/query/PhrictionDocumentQuery.php b/src/applications/phriction/query/PhrictionDocumentQuery.php index 63d1764a9a..24e7fbbef5 100644 --- a/src/applications/phriction/query/PhrictionDocumentQuery.php +++ b/src/applications/phriction/query/PhrictionDocumentQuery.php @@ -17,8 +17,6 @@ final class PhrictionDocumentQuery const STATUS_OPEN = 'status-open'; const STATUS_NONSTUB = 'status-nonstub'; - const ORDER_CREATED = 'order-created'; - const ORDER_UPDATED = 'order-updated'; const ORDER_HIERARCHY = 'order-hierarchy'; public function withIDs(array $ids) { @@ -61,38 +59,15 @@ final class PhrictionDocumentQuery return $this; } - public function setOrder($order) { - switch ($order) { - case self::ORDER_CREATED: - $this->setOrderVector(array('id')); - break; - case self::ORDER_UPDATED: - $this->setOrderVector(array('updated')); - break; - case self::ORDER_HIERARCHY: - $this->setOrderVector(array('depth', 'title', 'updated')); - break; - default: - throw new Exception(pht('Unknown order "%s".', $order)); - } - - return $this; + protected function loadPage() { + return $this->loadStandardPage($this->newResultObject()); } - protected function loadPage() { - $table = new PhrictionDocument(); - $conn_r = $table->establishConnection('r'); + public function newResultObject() { + return new PhrictionDocument(); + } - $rows = queryfx_all( - $conn_r, - 'SELECT d.* FROM %T d %Q %Q %Q %Q', - $table->getTableName(), - $this->buildJoinClause($conn_r), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - $documents = $table->loadAllFromArray($rows); + protected function willFilterPage(array $documents) { if ($documents) { $ancestor_slugs = array(); @@ -104,6 +79,8 @@ final class PhrictionDocumentQuery } if ($ancestor_slugs) { + $table = new PhrictionDocument(); + $conn_r = $table->establishConnection('r'); $ancestors = queryfx_all( $conn_r, 'SELECT * FROM %T WHERE slug IN (%Ls)', @@ -122,11 +99,6 @@ final class PhrictionDocumentQuery } } } - - return $documents; - } - - protected function willFilterPage(array $documents) { // To view a Phriction document, you must also be able to view all of the // ancestor documents. Filter out documents which have ancestors that are // not visible. @@ -190,58 +162,59 @@ final class PhrictionDocumentQuery return $documents; } - protected function buildJoinClause(AphrontDatabaseConnection $conn) { - $join = ''; + protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { + $joins = array(); if ($this->getOrderVector()->containsKey('updated')) { $content_dao = new PhrictionContent(); - $join = qsprintf( + $joins[] = qsprintf( $conn, 'JOIN %T c ON d.contentID = c.id', $content_dao->getTableName()); } - return $join; + return $joins; } - protected function buildWhereClause(AphrontDatabaseConnection $conn) { - $where = array(); - if ($this->ids) { + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); + + if ($this->ids !== null) { $where[] = qsprintf( $conn, 'd.id IN (%Ld)', $this->ids); } - if ($this->phids) { + if ($this->phids !== null) { $where[] = qsprintf( $conn, 'd.phid IN (%Ls)', $this->phids); } - if ($this->slugs) { + if ($this->slugs !== null) { $where[] = qsprintf( $conn, 'd.slug IN (%Ls)', $this->slugs); } - if ($this->statuses) { + if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'd.status IN (%Ld)', $this->statuses); } - if ($this->slugPrefix) { + if ($this->slugPrefix !== null) { $where[] = qsprintf( $conn, 'd.slug LIKE %>', $this->slugPrefix); } - if ($this->depths) { + if ($this->depths !== null) { $where[] = qsprintf( $conn, 'd.depth IN (%Ld)', @@ -274,9 +247,16 @@ final class PhrictionDocumentQuery throw new Exception(pht("Unknown status '%s'!", $this->status)); } - $where[] = $this->buildPagingClause($conn); + return $where; + } - return $this->formatWhereClause($where); + public function getBuiltinOrders() { + return array( + self::ORDER_HIERARCHY => array( + 'vector' => array('depth', 'title', 'updated'), + 'name' => pht('Hierarchy'), + ), + ) + parent::getBuiltinOrders(); } public function getOrderableColumns() { @@ -331,6 +311,9 @@ final class PhrictionDocumentQuery } } + protected function getPrimaryTableAlias() { + return 'd'; + } public function getQueryApplicationClass() { return 'PhabricatorPhrictionApplication'; diff --git a/src/applications/phriction/query/PhrictionSearchEngine.php b/src/applications/phriction/query/PhrictionSearchEngine.php index 82f61ec050..245500e0ff 100644 --- a/src/applications/phriction/query/PhrictionSearchEngine.php +++ b/src/applications/phriction/query/PhrictionSearchEngine.php @@ -11,52 +11,29 @@ final class PhrictionSearchEngine return 'PhabricatorPhrictionApplication'; } - public function buildSavedQueryFromRequest(AphrontRequest $request) { - $saved = new PhabricatorSavedQuery(); - - $saved->setParameter('status', $request->getStr('status')); - $saved->setParameter('order', $request->getStr('order')); - - return $saved; - } - - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { - $query = id(new PhrictionDocumentQuery()) + public function newQuery() { + return id(new PhrictionDocumentQuery()) ->needContent(true) ->withStatus(PhrictionDocumentQuery::STATUS_NONSTUB); + } - $status = $saved->getParameter('status'); - $status = idx($this->getStatusValues(), $status); - if ($status) { - $query->withStatus($status); - } + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); - $order = $saved->getParameter('order'); - $order = idx($this->getOrderValues(), $order); - if ($order) { - $query->setOrder($order); + if ($map['status']) { + $query->withStatus($map['status']); } return $query; } - public function buildSearchForm( - AphrontFormView $form, - PhabricatorSavedQuery $saved_query) { - - $form - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Status')) - ->setName('status') - ->setOptions($this->getStatusOptions()) - ->setValue($saved_query->getParameter('status'))) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Order')) - ->setName('order') - ->setOptions($this->getOrderOptions()) - ->setValue($saved_query->getParameter('order'))); + protected function buildCustomSearchFields() { + return array( + id(new PhabricatorSearchSelectField()) + ->setKey('status') + ->setLabel(pht('Status')) + ->setOptions($this->getStatusOptions()), + ); } protected function getURI($path) { @@ -66,7 +43,6 @@ final class PhrictionSearchEngine protected function getBuiltinQueryNames() { $names = array( 'active' => pht('Active'), - 'updated' => pht('Updated'), 'all' => pht('All'), ); @@ -79,12 +55,11 @@ final class PhrictionSearchEngine $query->setQueryKey($query_key); switch ($query_key) { - case 'active': - return $query->setParameter('status', 'active'); case 'all': return $query; - case 'updated': - return $query->setParameter('order', 'updated'); + case 'active': + return $query->setParameter( + 'status', PhrictionDocumentQuery::STATUS_OPEN); } return parent::buildSavedQueryFromBuiltin($query_key); @@ -92,29 +67,8 @@ final class PhrictionSearchEngine private function getStatusOptions() { return array( - 'active' => pht('Show Active Documents'), - 'all' => pht('Show All Documents'), - ); - } - - private function getStatusValues() { - return array( - 'active' => PhrictionDocumentQuery::STATUS_OPEN, - 'all' => PhrictionDocumentQuery::STATUS_NONSTUB, - ); - } - - private function getOrderOptions() { - return array( - 'created' => pht('Date Created'), - 'updated' => pht('Date Updated'), - ); - } - - private function getOrderValues() { - return array( - 'created' => PhrictionDocumentQuery::ORDER_CREATED, - 'updated' => PhrictionDocumentQuery::ORDER_UPDATED, + PhrictionDocumentQuery::STATUS_OPEN => pht('Show Active Documents'), + PhrictionDocumentQuery::STATUS_NONSTUB => pht('Show All Documents'), ); } diff --git a/src/applications/phriction/storage/PhrictionDocument.php b/src/applications/phriction/storage/PhrictionDocument.php index f931ac8dbe..b18f6bab9e 100644 --- a/src/applications/phriction/storage/PhrictionDocument.php +++ b/src/applications/phriction/storage/PhrictionDocument.php @@ -7,8 +7,9 @@ final class PhrictionDocument extends PhrictionDAO PhabricatorFlaggableInterface, PhabricatorTokenReceiverInterface, PhabricatorDestructibleInterface, - PhabricatorApplicationTransactionInterface, - PhabricatorFulltextInterface { + PhabricatorFulltextInterface, + PhabricatorProjectInterface, + PhabricatorApplicationTransactionInterface { protected $slug; protected $depth; diff --git a/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php b/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php index 64cfe6046b..9fe55bcbeb 100644 --- a/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php +++ b/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php @@ -123,18 +123,23 @@ final class PhabricatorPhurlURLEditor foreach ($xactions as $xaction) { if ($xaction->getOldValue() != $xaction->getNewValue()) { $new_alias = $xaction->getNewValue(); + $debug_alias = new PHUIInvisibleCharacterView($new_alias); if (!preg_match('/[a-zA-Z]/', $new_alias)) { $errors[] = new PhabricatorApplicationTransactionValidationError( $type, pht('Invalid Alias'), - pht('The alias must contain at least one letter.'), + pht('The alias you provided (%s) must contain at least one '. + 'letter.', + $debug_alias), $xaction); } if (preg_match('/[^a-z0-9]/i', $new_alias)) { $errors[] = new PhabricatorApplicationTransactionValidationError( $type, pht('Invalid Alias'), - pht('The alias may only contain letters and numbers.'), + pht('The alias you provided (%s) may only contain letters and '. + 'numbers.', + $debug_alias), $xaction); } } diff --git a/src/applications/project/storage/PhabricatorProjectTransaction.php b/src/applications/project/storage/PhabricatorProjectTransaction.php index 2123afae2c..fb9a25eda6 100644 --- a/src/applications/project/storage/PhabricatorProjectTransaction.php +++ b/src/applications/project/storage/PhabricatorProjectTransaction.php @@ -126,7 +126,7 @@ final class PhabricatorProjectTransaction return 'fa-unlock'; } case self::TYPE_ICON: - return $new; + return PhabricatorProjectIconSet::getIconIcon($new); case self::TYPE_IMAGE: return 'fa-photo'; case self::TYPE_MEMBERS: diff --git a/src/applications/repository/worker/PhabricatorRepositoryCommitOwnersWorker.php b/src/applications/repository/worker/PhabricatorRepositoryCommitOwnersWorker.php index 8f94df90be..20869ce273 100644 --- a/src/applications/repository/worker/PhabricatorRepositoryCommitOwnersWorker.php +++ b/src/applications/repository/worker/PhabricatorRepositoryCommitOwnersWorker.php @@ -77,11 +77,6 @@ final class PhabricatorRepositoryCommitOwnersWorker continue; } - if ($package->isArchived()) { - // Don't trigger audits if the package is archived. - continue; - } - if ($package->getAuditingEnabled()) { $reasons = $this->checkAuditReasons( $commit, diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php index d2ce3ad946..13606c542f 100644 --- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php +++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php @@ -1094,6 +1094,22 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { } } + $valid_constraints = array(); + foreach ($fields as $field) { + foreach ($field->getValidConstraintKeys() as $key) { + $valid_constraints[$key] = true; + } + } + + foreach ($constraints as $key => $constraint) { + if (empty($valid_constraints[$key])) { + throw new Exception( + pht( + 'Constraint "%s" is not a valid constraint for this query.', + $key)); + } + } + foreach ($fields as $field) { if (!$field->getValueExistsInConduitRequest($constraints)) { continue; diff --git a/src/applications/search/field/PhabricatorSearchField.php b/src/applications/search/field/PhabricatorSearchField.php index 9624fe22f9..d31aeb1950 100644 --- a/src/applications/search/field/PhabricatorSearchField.php +++ b/src/applications/search/field/PhabricatorSearchField.php @@ -329,6 +329,11 @@ abstract class PhabricatorSearchField extends Phobject { $this->getConduitKey()); } + public function getValidConstraintKeys() { + return $this->getConduitParameterType()->getKeys( + $this->getConduitKey()); + } + /* -( Utility Methods )----------------------------------------------------- */ diff --git a/src/applications/settings/setting/PhabricatorConpherenceWidgetVisibleSetting.php b/src/applications/settings/setting/PhabricatorConpherenceWidgetVisibleSetting.php new file mode 100644 index 0000000000..03bf21d7ad --- /dev/null +++ b/src/applications/settings/setting/PhabricatorConpherenceWidgetVisibleSetting.php @@ -0,0 +1,12 @@ + Press {key M} to view the starmap. + +You can also render sequences with modifier keys. This: + +``` +Use {key command option shift 3} to take a screenshot. +Press {key down down-right right LP} to activate the hadoken technique. +``` + +...renders this: + +> Use {key command option shift 3} to take a screenshot. +> Press {key down down-right right LP} to activate the hadoken technique. + + = Fullscreen Mode = Remarkup editors provide a fullscreen composition mode. This can make it easier diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldLink.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldLink.php index 66f3605b9c..c2b9d6543c 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldLink.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldLink.php @@ -47,7 +47,12 @@ final class PhabricatorStandardCustomFieldLink PhabricatorCursorPagedPolicyAwareQuery $query, $value) { - if (strlen($value)) { + if (is_string($value) && !strlen($value)) { + return; + } + + $value = (array)$value; + if ($value) { $query->withApplicationSearchContainsConstraint( $this->newStringIndex(null), $value); diff --git a/src/infrastructure/env/PhabricatorEnv.php b/src/infrastructure/env/PhabricatorEnv.php index 7ea00957ff..be301ad7a0 100644 --- a/src/infrastructure/env/PhabricatorEnv.php +++ b/src/infrastructure/env/PhabricatorEnv.php @@ -315,6 +315,14 @@ final class PhabricatorEnv extends Phobject { * @task read */ public static function getEnvConfig($key) { + if (!self::$sourceStack) { + throw new Exception( + pht( + 'Trying to read configuration "%s" before configuration has been '. + 'initialized.', + $key)); + } + if (isset(self::$cache[$key])) { return self::$cache[$key]; } diff --git a/src/infrastructure/markup/PhabricatorMarkupEngine.php b/src/infrastructure/markup/PhabricatorMarkupEngine.php index bb13c1d763..565df6cb44 100644 --- a/src/infrastructure/markup/PhabricatorMarkupEngine.php +++ b/src/infrastructure/markup/PhabricatorMarkupEngine.php @@ -505,6 +505,7 @@ final class PhabricatorMarkupEngine extends Phobject { $rules[] = new PhutilRemarkupDocumentLinkRule(); $rules[] = new PhabricatorNavigationRemarkupRule(); + $rules[] = new PhabricatorKeyboardRemarkupRule(); if ($options['youtube']) { $rules[] = new PhabricatorYoutubeRemarkupRule(); diff --git a/src/infrastructure/markup/rule/PhabricatorKeyboardRemarkupRule.php b/src/infrastructure/markup/rule/PhabricatorKeyboardRemarkupRule.php new file mode 100644 index 0000000000..cdbfea7b74 --- /dev/null +++ b/src/infrastructure/markup/rule/PhabricatorKeyboardRemarkupRule.php @@ -0,0 +1,256 @@ +isFlatText($matches[0])) { + return $matches[0]; + } + + $keys = explode(' ', $matches[1]); + foreach ($keys as $k => $v) { + $v = trim($v, " \n"); + $v = preg_replace('/\\\\(.)/', '\\1', $v); + if (!strlen($v)) { + unset($keys[$k]); + continue; + } + $keys[$k] = $v; + } + + $special = array( + array( + 'name' => pht('Command'), + 'symbol' => "\xE2\x8C\x98", + 'aliases' => array( + 'cmd', + 'command', + ), + ), + array( + 'name' => pht('Option'), + 'symbol' => "\xE2\x8C\xA5", + 'aliases' => array( + 'opt', + 'option', + ), + ), + array( + 'name' => pht('Shift'), + 'symbol' => "\xE2\x87\xA7", + 'aliases' => array( + 'shift', + ), + ), + array( + 'name' => pht('Escape'), + 'symbol' => "\xE2\x8E\x8B", + 'aliases' => array( + 'esc', + 'escape', + ), + ), + array( + 'name' => pht('Up'), + 'symbol' => "\xE2\x86\x91", + 'heavy' => "\xE2\xAC\x86", + 'aliases' => array( + 'up', + 'arrow-up', + 'up-arrow', + 'north', + ), + ), + array( + 'name' => pht('Tab'), + 'symbol' => "\xE2\x87\xA5", + 'aliases' => array( + 'tab', + ), + ), + array( + 'name' => pht('Right'), + 'symbol' => "\xE2\x86\x92", + 'heavy' => "\xE2\x9E\xA1", + 'aliases' => array( + 'right', + 'right-arrow', + 'arrow-right', + 'east', + ), + ), + array( + 'name' => pht('Left'), + 'symbol' => "\xE2\x86\x90", + 'heavy' => "\xE2\xAC\x85", + 'aliases' => array( + 'left', + 'left-arrow', + 'arrow-left', + 'west', + ), + ), + array( + 'name' => pht('Down'), + 'symbol' => "\xE2\x86\x93", + 'heavy' => "\xE2\xAC\x87", + 'aliases' => array( + 'down', + 'down-arrow', + 'arrow-down', + 'south', + ), + ), + array( + 'name' => pht('Up Right'), + 'symbol' => "\xE2\x86\x97", + 'heavy' => "\xE2\xAC\x88", + 'aliases' => array( + 'up-right', + 'upright', + 'up-right-arrow', + 'upright-arrow', + 'arrow-up-right', + 'arrow-upright', + 'northeast', + 'north-east', + ), + ), + array( + 'name' => pht('Down Right'), + 'symbol' => "\xE2\x86\x98", + 'heavy' => "\xE2\xAC\x8A", + 'aliases' => array( + 'down-right', + 'downright', + 'down-right-arrow', + 'downright-arrow', + 'arrow-down-right', + 'arrow-downright', + 'southeast', + 'south-east', + ), + ), + array( + 'name' => pht('Down Left'), + 'symbol' => "\xE2\x86\x99", + 'heavy' => "\xE2\xAC\x8B", + 'aliases' => array( + 'down-left', + 'downleft', + 'down-left-arrow', + 'downleft-arrow', + 'arrow-down-left', + 'arrow-downleft', + 'southwest', + 'south-west', + ), + ), + array( + 'name' => pht('Up Left'), + 'symbol' => "\xE2\x86\x96", + 'heavy' => "\xE2\xAC\x89", + 'aliases' => array( + 'up-left', + 'upleft', + 'up-left-arrow', + 'upleft-arrow', + 'arrow-up-left', + 'arrow-upleft', + 'northwest', + 'north-west', + ), + ), + ); + + $map = array(); + foreach ($special as $spec) { + foreach ($spec['aliases'] as $alias) { + $map[$alias] = $spec; + } + } + + $is_text = $this->getEngine()->isTextMode(); + $is_html_mail = $this->getEngine()->isHTMLMailMode(); + + if ($is_html_mail) { + $key_style = array( + 'display: inline-block;', + 'min-width: 1em;', + 'padding: 4px 5px 5px;', + 'font-weight: normal;', + 'font-size: 0.8rem;', + 'text-align: center;', + 'text-decoration: none;', + 'line-height: 0.6rem;', + 'border-radius: 3px;', + 'box-shadow: inset 0 -1px 0 rgba(71, 87, 120, 0.08);', + 'user-select: none;', + 'background: #f7f7f7;', + 'border: 1px solid #C7CCD9;', + ); + $key_style = implode(' ', $key_style); + + $join_style = array( + 'padding: 0 4px;', + 'color: #92969D;', + ); + $join_style = implode(' ', $join_style); + } else { + $key_style = null; + $join_style = null; + } + + $parts = array(); + foreach ($keys as $k => $v) { + $normal = phutil_utf8_strtolower($v); + if (isset($map[$normal])) { + $spec = $map[$normal]; + } else { + $spec = array( + 'name' => null, + 'symbol' => $v, + ); + } + + if ($is_text) { + $parts[] = '['.$spec['symbol'].']'; + } else { + $parts[] = phutil_tag( + 'kbd', + array( + 'title' => $spec['name'], + 'style' => $key_style, + ), + $spec['symbol']); + } + } + + if ($is_text) { + $parts = implode(' + ', $parts); + } else { + $glue = phutil_tag( + 'span', + array( + 'class' => 'kbd-join', + 'style' => $join_style, + ), + '+'); + $parts = phutil_implode_html($glue, $parts); + } + + return $this->getEngine()->storeText($parts); + } + +} diff --git a/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php b/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php index 21fabed8a8..2fbabf037a 100644 --- a/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php +++ b/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php @@ -67,7 +67,7 @@ abstract class PhabricatorLiskDAO extends LiskDAO { $connection = $this->newBasicConnection($database, $mode, $namespace); } - // TODO: This should be testing if the mode is "r", but that would proably + // TODO: This should be testing if the mode is "r", but that would probably // break a lot of things. Perform a more narrow test for readonly mode // until we have greater certainty that this works correctly most of the // time. diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php index 8f22caa803..7bfb4d873d 100644 --- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php +++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php @@ -8,6 +8,8 @@ abstract class PhabricatorStorageManagementWorkflow private $force; private $patches; + private $didInitialize; + final public function getAPI() { return $this->api; } @@ -176,12 +178,16 @@ abstract class PhabricatorStorageManagementWorkflow "%s\n", pht('DRYRUN: Would apply adjustments.')); return 0; + } else if ($this->didInitialize) { + // If we just initialized the database, continue without prompting. This + // is nicer for first-time setup and there's no reasonable reason any + // user would ever answer "no" to the prompt against an empty schema. } else if (!$this->force) { $console->writeOut( "\n%s\n", pht( - "Found %s issues(s) with schemata, detailed above.\n\n". - "You can review issues in more detail from the web interface, ". + "Found %s adjustment(s) to apply, detailed above.\n\n". + "You can review adjustments in more detail from the web interface, ". "in Config > Database Status. To better understand the adjustment ". "workflow, see \"Managing Storage Adjustments\" in the ". "documentation.\n\n". @@ -189,7 +195,7 @@ abstract class PhabricatorStorageManagementWorkflow "migrations may take some time.", phutil_count($adjustments))); - $prompt = pht('Fix these schema issues?'); + $prompt = pht('Apply these schema adjustments?'); if (!phutil_console_confirm($prompt, $default_no = true)) { return 1; } @@ -197,7 +203,7 @@ abstract class PhabricatorStorageManagementWorkflow $console->writeOut( "%s\n", - pht('Fixing schema issues...')); + pht('Applying schema adjustments...')); $conn = $api->getConn(null); @@ -368,7 +374,7 @@ abstract class PhabricatorStorageManagementWorkflow if (!$failed) { $console->writeOut( "%s\n", - pht('Completed fixing all schema issues.')); + pht('Completed applying all schema adjustments.')); $err = 0; } else { @@ -800,6 +806,11 @@ abstract class PhabricatorStorageManagementWorkflow return 1; } + // If we're initializing storage for the first time, track it so that + // we can give the user a nicer experience during the subsequent + // adjustment phase. + $this->didInitialize = true; + $legacy = $api->getLegacyPatches($this->patches); if ($legacy || $no_quickstart || $init_only) { diff --git a/src/view/layout/AphrontSideNavFilterView.php b/src/view/layout/AphrontSideNavFilterView.php index eb9f4f72f8..9dd3720646 100644 --- a/src/view/layout/AphrontSideNavFilterView.php +++ b/src/view/layout/AphrontSideNavFilterView.php @@ -95,9 +95,9 @@ final class AphrontSideNavFilterView extends AphrontView { return $this->menu; } - public function addFilter($key, $name, $uri = null) { + public function addFilter($key, $name, $uri = null, $icon = null) { return $this->addThing( - $key, $name, $uri, PHUIListItemView::TYPE_LINK); + $key, $name, $uri, PHUIListItemView::TYPE_LINK, $icon); } public function addButton($key, $name, $uri = null) { @@ -105,11 +105,15 @@ final class AphrontSideNavFilterView extends AphrontView { $key, $name, $uri, PHUIListItemView::TYPE_BUTTON); } - private function addThing($key, $name, $uri, $type) { + private function addThing($key, $name, $uri, $type, $icon) { $item = id(new PHUIListItemView()) ->setName($name) ->setType($type); + if (strlen($icon)) { + $item->setIcon($icon); + } + if (strlen($key)) { $item->setKey($key); diff --git a/src/view/phui/PHUIIconCircleView.php b/src/view/phui/PHUIIconCircleView.php index 05612c26f2..083818969f 100644 --- a/src/view/phui/PHUIIconCircleView.php +++ b/src/view/phui/PHUIIconCircleView.php @@ -45,7 +45,9 @@ final class PHUIIconCircleView extends AphrontTagView { $classes[] = 'phui-icon-circle'; if ($this->color) { - $classes[] = 'phui-icon-circle-'.$this->color; + $classes[] = 'hover-'.$this->color; + } else { + $classes[] = 'hover-sky'; } if ($this->size) { @@ -60,8 +62,7 @@ final class PHUIIconCircleView extends AphrontTagView { protected function getTagContent() { return id(new PHUIIconView()) - ->setIcon($this->icon) - ->addClass($this->color); + ->setIcon($this->icon); } } diff --git a/src/view/phui/PHUIInvisibleCharacterView.php b/src/view/phui/PHUIInvisibleCharacterView.php new file mode 100644 index 0000000000..dda00f81a1 --- /dev/null +++ b/src/view/phui/PHUIInvisibleCharacterView.php @@ -0,0 +1,94 @@ + 'NULL', + "\t" => 'TAB', + "\n" => 'NEWLINE', + "\x20" => 'SPACE', + ); + + public function __construct($input_text) { + $this->inputText = $input_text; + } + + public function setPlainText($plain_text) { + $this->plainText = $plain_text; + return $this; + } + + public function getStringParts() { + $input_text = $this->inputText; + $text_array = phutil_utf8v($input_text); + + for ($ii = 0; $ii < count($text_array); $ii++) { + $char = $text_array[$ii]; + $char_hex = bin2hex($char); + if (array_key_exists($char, self::$invisibleChars)) { + $text_array[$ii] = array( + 'special' => true, + 'value' => '<'.self::$invisibleChars[$char].'>', + ); + } else if (ord($char) < 32) { + $text_array[$ii] = array( + 'special' => true, + 'value' => '<0x'.$char_hex.'>', + ); + } else { + $text_array[$ii] = array( + 'special' => false, + 'value' => $char, + ); + } + } + return $text_array; + } + + private function renderHtmlArray() { + $html_array = array(); + $parts = $this->getStringParts(); + foreach ($parts as $part) { + if ($part['special']) { + $html_array[] = phutil_tag( + 'span', + array('class' => 'invisible-special'), + $part['value']); + } else { + $html_array[] = $part['value']; + } + } + return $html_array; + } + + private function renderPlainText() { + $parts = $this->getStringParts(); + $res = ''; + foreach ($parts as $part) { + $res .= $part['value']; + } + return $res; + } + + public function render() { + require_celerity_resource('phui-invisible-character-view-css'); + if ($this->plainText) { + return $this->renderPlainText(); + } else { + return $this->renderHtmlArray(); + } + } + +} diff --git a/src/view/phui/__tests__/PHUIInvisibleCharacterTestCase.php b/src/view/phui/__tests__/PHUIInvisibleCharacterTestCase.php new file mode 100644 index 0000000000..09f8c0934b --- /dev/null +++ b/src/view/phui/__tests__/PHUIInvisibleCharacterTestCase.php @@ -0,0 +1,52 @@ +render(); + $this->assertEqual($res, array()); + } + + public function testEmptyPlainText() { + $view = (new PHUIInvisibleCharacterView('')) + ->setPlainText(true); + $res = $view->render(); + $this->assertEqual($res, ''); + } + + public function testWithNamedChars() { + $test_input = "\x00\n\t "; + $view = (new PHUIInvisibleCharacterView($test_input)) + ->setPlainText(true); + $res = $view->render(); + $this->assertEqual($res, ''); + } + + public function testWithHexChars() { + $test_input = "abc\x01"; + $view = (new PHUIInvisibleCharacterView($test_input)) + ->setPlainText(true); + $res = $view->render(); + $this->assertEqual($res, 'abc<0x01>'); + } + + public function testWithNamedAsHex() { + $test_input = "\x00\x0a\x09\x20"; + $view = (new PHUIInvisibleCharacterView($test_input)) + ->setPlainText(true); + $res = $view->render(); + $this->assertEqual($res, ''); + } + + public function testHtmlDecoration() { + $test_input = "a\x00\n\t "; + $view = new PHUIInvisibleCharacterView($test_input); + $res = $view->render(); + $this->assertFalse($res[0] instanceof PhutilSafeHTML); + $this->assertTrue($res[1] instanceof PhutilSafeHTML); + $this->assertTrue($res[2] instanceof PhutilSafeHTML); + $this->assertTrue($res[3] instanceof PhutilSafeHTML); + $this->assertTrue($res[4] instanceof PhutilSafeHTML); + } +} diff --git a/webroot/favicon.ico b/webroot/favicon.ico index e074800241..f07770aa8a 100644 Binary files a/webroot/favicon.ico and b/webroot/favicon.ico differ diff --git a/webroot/rsrc/css/aphront/dialog-view.css b/webroot/rsrc/css/aphront/dialog-view.css index 7328cda5ea..3122fd7e92 100644 --- a/webroot/rsrc/css/aphront/dialog-view.css +++ b/webroot/rsrc/css/aphront/dialog-view.css @@ -46,6 +46,10 @@ border: none; } +.device-phone .aphront-dialog-body { + padding: 8px; +} + .aphront-dialog-tail { border: none; position: relative; diff --git a/webroot/rsrc/css/application/base/main-menu-view.css b/webroot/rsrc/css/application/base/main-menu-view.css index b5a11d7ba5..0e26dc47a4 100644 --- a/webroot/rsrc/css/application/base/main-menu-view.css +++ b/webroot/rsrc/css/application/base/main-menu-view.css @@ -598,6 +598,7 @@ button.phabricator-main-menu-search-dropdown .caret:before { .phabricator-main-menu-dropdown.phui-list-sidenav .phui-list-item-has-icon .phui-list-item-href { padding: 4px 40px 4px 12px; + white-space: nowrap; } .phabricator-main-menu-dropdown.phui-list-sidenav .phui-list-item-type-label diff --git a/webroot/rsrc/css/application/config/config-template.css b/webroot/rsrc/css/application/config/config-template.css index bb8db22c93..13ce56488a 100644 --- a/webroot/rsrc/css/application/config/config-template.css +++ b/webroot/rsrc/css/application/config/config-template.css @@ -11,3 +11,27 @@ body { text-align: left; -webkit-text-size-adjust: none; } + +body.in-flight { + background: #41506e; + color: #e0e0e0; +} + +.in-flight-error-detail { + max-width: 760px; + margin: 72px auto; + background: rgba(255, 255, 255, 0.25); + border-radius: 3px; + padding: 8px 16px; +} + +.in-flight-error-title { + padding: 12px 8px; + font-size: 24px; + font-weight: 500; + margin: 0; +} + +.in-flight-error-body { + padding: 4px 12px 12px; +} diff --git a/webroot/rsrc/css/application/config/config-welcome.css b/webroot/rsrc/css/application/config/config-welcome.css deleted file mode 100644 index 748726476e..0000000000 --- a/webroot/rsrc/css/application/config/config-welcome.css +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @provides config-welcome-css - */ - -.phui-document-content .config-welcome-box .phabricator-remarkup { - padding: 0 16px 16px; - position: relative; -} - -.config-welcome-box { - padding: 8px 0; -} - -.config-welcome-content { - margin-left: 44px; -} - -.config-welcome-box .config-welcome-icon { - width: 32px; - float: left; - text-align: center; -} - -.phui-document-view-pro .phui-document-content .config-welcome-box - .phabricator-remarkup { - margin: 0; - padding: 0; - } diff --git a/webroot/rsrc/css/application/conpherence/durable-column.css b/webroot/rsrc/css/application/conpherence/durable-column.css index d204592cf3..8872e2fb22 100644 --- a/webroot/rsrc/css/application/conpherence/durable-column.css +++ b/webroot/rsrc/css/application/conpherence/durable-column.css @@ -251,7 +251,6 @@ .conpherence-durable-column-transactions .conpherence-transaction-detail .conpherence-transaction-header .conpherence-transaction-info { color: {$lightbluetext}; - float: none; font-size: {$smallerfontsize}; } @@ -266,7 +265,7 @@ .conpherence-durable-column-transactions .conpherence-transaction-detail .conpherence-transaction-content .phui-link-person { - color: {$darkbluetext}; + color: #000; } .conpherence-durable-column-transactions diff --git a/webroot/rsrc/css/application/conpherence/header-pane.css b/webroot/rsrc/css/application/conpherence/header-pane.css new file mode 100644 index 0000000000..9b0c5e98e5 --- /dev/null +++ b/webroot/rsrc/css/application/conpherence/header-pane.css @@ -0,0 +1,65 @@ +/** + * @provides conpherence-header-pane-css + */ + +.conpherence-header-pane { +} + +.conpherence-header-pane .phui-header-shell { + padding: 8px 16px 10px; + min-height: 38px; +} + +.conpherence-header-pane .phui-header-header { + font-size: 16px; + font-family: 'Aleo', {$fontfamily}; + color: #000; +} + +.conpherence-header-pane .phui-header-subheader { + color: {$lightgreytext}; + padding: 0; + font-size: 12px; + margin: 0; +} + +.conpherence-header-pane .phui-header-shell.conpherence-no-topic { + padding: 15px 16px 5px; +} + +.conpherence-header-pane .phui-header-action-list .phui-header-action-item + .phui-icon-view { + height: 18px; + width: 24px; + font-size: 14px; + line-height: 23px; + display: block; +} + +.device .hide-on-device { + display: none; +} + +.device-phone .conpherence-header-pane .phui-header-col3 { + vertical-align: middle; +} + +.conpherence-participant-toggle.phui-icon-circle { + text-decoration: none; + border-color: {$sky}; + cursor: pointer; +} + +.conpherence-participant-toggle.phui-icon-circle .phui-icon-view { + color: {$sky}; +} + +.hide-widgets .conpherence-participant-toggle.phui-icon-circle { + text-decoration: none; + border-color: {$lightblueborder}; + cursor: pointer; +} + +.hide-widgets .conpherence-participant-toggle.phui-icon-circle .phui-icon-view { + color: {$lightblueborder}; +} diff --git a/webroot/rsrc/css/application/conpherence/menu.css b/webroot/rsrc/css/application/conpherence/menu.css index d786848837..aa5798bfff 100644 --- a/webroot/rsrc/css/application/conpherence/menu.css +++ b/webroot/rsrc/css/application/conpherence/menu.css @@ -11,27 +11,6 @@ background: #fff; } -.conpherence-layout .conpherence-no-threads { - text-align: center; - position: fixed; - left: 240px; - right: 241px; - top: 76px; - bottom: 0px; - min-width: 300px; - width: auto; -} - -.device .conpherence-layout .conpherence-no-threads { - left: 0; - right: 0; - width: 100%; -} - -.conpherence-layout .conpherence-no-threads .text { - margin: 0px 0px 16px 0px; -} - .conpherence-menu-pane { width: 100%; position: absolute; @@ -39,16 +18,37 @@ overflow-y: auto; top: 0; bottom: 0; - background: #f7f7f7; - border-right: 1px solid {$lightblueborder} + background-color: {$page.sidenav}; } +.conpherence-menu-pane .phui-basic-nav .phabricator-side-menu + .phui-list-item-href { + padding: 4px 0 4px 8px; + } + .conpherence-menu-pane .phui-list-item-view.hidden { display: none; } -.conpherence-menu-pane.phabricator-side-menu .phui-list-item-type-label { +.conpherence-menu-pane.phabricator-side-menu .room-list-href { padding: 10px 0 9px 8px; + display: inline-block; + text-transform: uppercase; + color: {$darkgreytext}; + font-weight: bold; + font-size: {$smallerfontsize}; + float: left; +} + +.conpherence-menu-pane.phabricator-side-menu .room-list-icons { + display: inline-block; + float: right; + padding: 8px 0 5px; +} + +.conpherence-menu-pane .conpherence-room-list-header { + clear: both; + overflow: hidden; } .conpherence-menu-pane .conpherence-room-list-header @@ -56,7 +56,7 @@ font-weight: bold; float: right; text-transform: none; - margin: 0px 8px 0px 0px; + padding: 4px; } .conpherence-menu-pane .conpherence-room-list-header @@ -69,7 +69,8 @@ } .device-desktop .conpherence-layout .conpherence-menu-pane { - width: 240px; + width: 232px; + padding-right: 8px; } .device .conpherence-menu-pane { @@ -89,7 +90,7 @@ } .device-desktop .conpherence-content-pane { - margin-left: 241px; + margin-left: 240px; } .conpherence-content-pane { @@ -138,7 +139,6 @@ width: 26px; height: 26px; background-size: 26px; - box-shadow: {$borderinset}; border-radius: 3px; float: left; margin-left: 4px; @@ -154,7 +154,7 @@ color: {$darkbluetext}; text-shadow: 0px 1px 1px #fff; overflow: hidden; - width: 175px; + width: 165px; text-overflow: ellipsis; } diff --git a/webroot/rsrc/css/application/conpherence/message-pane.css b/webroot/rsrc/css/application/conpherence/message-pane.css index ea26d3cbe6..7209ac7085 100644 --- a/webroot/rsrc/css/application/conpherence/message-pane.css +++ b/webroot/rsrc/css/application/conpherence/message-pane.css @@ -7,9 +7,9 @@ .loading .messages-loading-icon, .conpherence-layout .conpherence-no-threads { position: fixed; - left: 241px; - right: 241px; - top: 76px; + left: 240px; + right: 240px; + top: 102px; bottom: 0px; min-width: 300px; width: auto; @@ -24,17 +24,20 @@ width: 100%; } -.conpherence-layout .conpherence-no-threads { - text-align: center; +.conpherence-layout .conpherence-content-pane .conpherence-no-threads { + top: 44px; + right: 0; + background: #fff; + z-index: 26; } -.conpherence-layout .conpherence-no-threads .text { - margin: 16px 0px 16px 0px; +.conpherence-layout .phui-big-info-view { + margin: 16px; } -.conpherence-layout .phui-crumbs-view { - padding: 0 0 0 8px; - background: #f7f7f7; +.conpherence-layout .conpherence-no-threads .phui-box-border { + border: none; + margin: 0; } .conpherence-show-more-messages { @@ -52,10 +55,10 @@ .conpherence-message-pane .conpherence-messages { position: fixed; - left: 241px; - right: 241px; - top: 78px; - bottom: 172px; + left: 240px; + right: 240px; + top: 103px; + bottom: 148px; overflow-x: hidden; overflow-y: auto; -webkit-overflow-scrolling: touch; @@ -74,8 +77,7 @@ padding-top: 20px; } -.conpherence-messages > .jx-scrollbar-content .conpherence-edited:last-child, -.conpherence-messages > .conpherence-edited:last-child { +.conpherence-messages .jx-scrollbar-content .conpherence-edited:last-child { padding-bottom: 20px; } @@ -86,7 +88,7 @@ .device .conpherence-message-pane .conpherence-messages { left: 0; right: 0; - bottom: 52px; + bottom: 44px; width: 100%; box-shadow: none; } @@ -103,16 +105,18 @@ .conpherence-message-pane .phui-form-view { border-width: 0; - background-color: {$lightgreybackground}; - height: 156px; - padding: 8px; + height: 140px; + padding: 0 20px 12px; position: fixed; bottom: 0; - border-top: 1px solid {$thinblueborder}; - left: 241px; + left: 240px; right: 241px; } +.device .conpherence-message-pane .phui-form-view { + padding: 8px 8px; +} + .conpherence-message-pane .phui-form-view.login-to-participate { height: 26px; } @@ -126,6 +130,11 @@ margin-top: 6px; } +.device .conpherence-message-pane .aphront-form-control-submit button, +.device .conpherence-message-pane .aphront-form-control-submit a.button { + margin-top: 13px; +} + /** * When entering "Fullscreen Mode" in the remarkup control, we need to drop * all of the "position: fixed" on parent elements or Chrome doesn't put the @@ -139,7 +148,11 @@ } .conpherence-message-pane .remarkup-assist-bar { - border-color: {$lightblueborder}; + border: 2px solid {$lightgreyborder}; + border-bottom: none; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + background-color: #f7f7f7; } .device .conpherence-message-pane .remarkup-assist-bar { @@ -161,7 +174,7 @@ .conpherence-message-pane .conpherence-transaction-view { padding: 2px 0px; - margin: 4px 12px; + margin: 4px 20px; background-size: 100%; min-height: auto; } @@ -178,11 +191,12 @@ background-size: 35px; position: absolute; top: 5px; - box-shadow: {$borderinset}; } .device-phone .conpherence-message-pane .conpherence-transaction-image { - display: none; + height: 25px; + width: 25px; + background-size: 25px; } .conpherence-message-pane .conpherence-comment.anchor-target, @@ -206,12 +220,12 @@ } .device-phone .conpherence-message-pane .conpherence-transaction-detail { - margin: 0; + margin-left: 32px; } .conpherence-message-pane .conpherence-transaction-view.date-marker { padding: 0; - margin: 20px 12px 4px; + margin: 20px 20px 4px; min-height: auto; } @@ -234,7 +248,6 @@ .device .conpherence-message-pane .conpherence-transaction-view.date-marker .date { - color: {$lightbluetext}; left: 4px; } @@ -246,10 +259,12 @@ .conpherence-message-pane .conpherence-edited .conpherence-transaction-content { color: {$lightgreytext}; - font-size: {$normalfontsize}; + font-size: {$biggerfontsize}; + font-style: italic; margin: 0; padding: 0; float: left; + line-height: 20px; } .conpherence-message-pane .conpherence-edited { @@ -277,8 +292,8 @@ color: {$darkbluetext}; } -.device-phone .conpherence-message-pane .conpherence-transaction-info { - display: none; +.conpherence-message-pane .conpherence-transaction-info { + margin: 0 8px; } .conpherence-message-pane .conpherence-transaction-info, @@ -289,18 +304,6 @@ font-size: {$smallerfontsize}; } -.conpherence-message-pane .conpherence-transaction-info { - float: right; -} - -.conpherence-message-pane .conpherence-transaction-header, -.conpherence-message-pane .conpherence-transaction-info, -.conpherence-message-pane .anchor-link, -.conpherence-message-pane .conpherence-transaction-content { - background: none; - padding: 0; -} - .conpherence-message-pane .conpherence-transaction-content { padding: 2px 0 8px 0; } @@ -310,14 +313,23 @@ } .conpherence-message-pane .remarkup-assist-textarea { - height: 100px; - padding: 6px; - border-color: {$lightblueborder}; - border-top-color: {$thinblueborder}; + height: 80px; + padding: 8px; + border: 2px solid {$lightgreyborder}; + border-top: 1px solid {$thinblueborder}; box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; resize: none; + outline: none; + box-shadow: none; + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; +} + +.conpherence-message-pane .remarkup-assist-textarea:focus { + border: 2px solid {$lightgreyborder}; + border-top: 1px solid {$thinblueborder}; } .device .conpherence-message-pane .remarkup-assist-textarea { @@ -326,6 +338,9 @@ width: 100%; height: 34px; resize: none; + border-top: 2px solid {$lightgreyborder}; + border-top-left-radius: 3px; + border-top-right-radius: 3px; } .conpherence-message-pane .remarkup-assist-textarea:focus { @@ -335,8 +350,8 @@ .device .conpherence-message-pane .aphront-form-control-submit { padding: 0; position: absolute; - top: 7px; - right: 13px; + top: -2px; + right: 12px; } .device .conpherence-message-pane .aphront-form-control-textarea { @@ -348,3 +363,4 @@ .conpherence-message .phabricator-remarkup .remarkup-code-block pre { max-height: 200px; } + diff --git a/webroot/rsrc/css/application/conpherence/participant-pane.css b/webroot/rsrc/css/application/conpherence/participant-pane.css new file mode 100644 index 0000000000..94bedce84d --- /dev/null +++ b/webroot/rsrc/css/application/conpherence/participant-pane.css @@ -0,0 +1,118 @@ +/** + * @provides conpherence-participant-pane-css + */ + +.conpherence-participant-pane { + position: fixed; + right: 0px; + top: 103px; + bottom: 0; + width: 240px; + border-width: 0 0 0 1px; + border-color: {$thinblueborder}; + border-style: solid; + overflow-y: auto; + -webkit-overflow-scrolling: touch; +} + +.device .conpherence-participant-pane { + background-color: {$page.background}; +} + +.conpherence-participant-pane .aphront-form-input { + margin: 0; + width: 100%; +} + +.conpherence-participant-pane .aphront-form-inset { + border: 0; +} + +.conpherence-participant-pane .widgets-header.phui-header-shell { + padding: 8px 12px 6px; + border: none; +} + +.conpherence-participant-pane .widgets-header .phui-header-header { + font-size: {$biggerfontsize}; +} + +.conpherence-participant-pane .widgets-header .phui-icon-view.disabled { + color: {$lightgreytext}; +} + +.conpherence-participant-pane .widgets-body { + position: fixed; + overflow-y: auto; + bottom: 0; + top: 102px; + width: 240px; +} + +.conpherence-participant-pane .person-entry { + padding: 4px 8px 4px 12px; +} + +.conpherence-participant-pane .person-entry:hover { + background-color: {$lightgreybackground}; +} + +.conpherence-participant-pane .person-entry a { + float: left; + line-height: 22px; + color: {$bluetext}; + font-weight: bold; +} + +.conpherence-participant-pane .person-entry:hover a { + color: #000; +} + +.conpherence-participant-pane .person-entry a img { + height: 24px; + width: 24px; + border-radius: 3px; +} + +.conpherence-participant-pane .person-entry .pic { + float: left; + width: 30px; +} + +.conpherence-participant-pane .person-entry .remove { + float: right; + width: 20px; + height: 20px; + font-size: 18px; +} + +.person-entry .remove:hover .phui-icon-view { + color: {$red}; +} + +.conpherence-participant-pane .person-entry .remove:hover { + text-decoration: none; +} + +.conpherence-participant-pane .person-entry .remove .close-icon { + color: #bfbfbf; +} + +.conpherence-participant-pane .person-entry .remove:hover .close-icon { + color: #000; +} + +/****** Hide Widgets **********************************************************/ + +.hide-widgets .conpherence-participant-pane { + display: none; +} + +.hide-widgets .conpherence-message-pane, +.hide-widgets .loading .messages-loading-mask, +.hide-widgets .loading .messages-loading-icon, +.hide-widgets .conpherence-no-threads, +.hide-widgets .conpherence-message-pane .conpherence-messages, +.hide-widgets .conpherence-message-pane .phui-form-view { + right: 0; +} diff --git a/webroot/rsrc/css/application/conpherence/transaction.css b/webroot/rsrc/css/application/conpherence/transaction.css index 53ade42326..62454c448e 100644 --- a/webroot/rsrc/css/application/conpherence/transaction.css +++ b/webroot/rsrc/css/application/conpherence/transaction.css @@ -12,16 +12,19 @@ .conpherence-transaction-header .phui-link-person { font-weight: bold; + font-size: {$biggerfontsize}; + color: #000; } .conpherence-transaction-view.date-marker { - border-top: 1px solid {$thinblueborder}; + border-top: 1px solid {$sh-violetborder}; } + .conpherence-transaction-view.date-marker .date { position: relative; top: -11px; background-color: #fff; - color: #000; + color: {$sh-violettext}; font-weight: bold; } diff --git a/webroot/rsrc/css/application/conpherence/update.css b/webroot/rsrc/css/application/conpherence/update.css index f5c6fb43b7..8b29c085ed 100644 --- a/webroot/rsrc/css/application/conpherence/update.css +++ b/webroot/rsrc/css/application/conpherence/update.css @@ -2,10 +2,6 @@ * @provides conpherence-update-css */ -.phabricator-standard-page-body .aphront-dialog-view { - margin: 20px auto 0px auto; -} - .aphront-dialog-view .conpherence-dialogue-drag-photo { border: 1px dashed #bfbfbf; padding: 10px 0px 10px 10px; diff --git a/webroot/rsrc/css/application/conpherence/widget-pane.css b/webroot/rsrc/css/application/conpherence/widget-pane.css deleted file mode 100644 index 6b8e97e359..0000000000 --- a/webroot/rsrc/css/application/conpherence/widget-pane.css +++ /dev/null @@ -1,192 +0,0 @@ -/** - * @provides conpherence-widget-pane-css - */ - -.conpherence-widget-pane, -.loading .widgets-loading-mask { - position: fixed; - right: 0px; - top: 76px; - bottom: 0; - width: 240px; - border-width: 0 0 0 1px; - border-color: {$lightblueborder}; - border-style: solid; - overflow-y: auto; - -webkit-overflow-scrolling: touch; -} -.device .conpherence-widget-pane, -.device .loading .widgets-loading-mask { - top: 44px; - width: 100%; -} - -.conpherence-widget-pane .widgets-loading-mask { - opacity: .6; - background: #fff; - display: none; -} - -.loading .widgets-loading-mask { - display: block; -} - -.conpherence-widget-pane .aphront-form-input { - margin: 0; - width: 100%; -} - -.conpherence-widget-pane .aphront-form-inset { - border: 0; -} - -.conpherence-widget-pane .widgets-header .phui-header-shell { - padding: 6px 8px; -} - -.conpherence-widget-pane .widgets-header .phui-header-header { - font-size: {$biggerfontsize}; -} - -.device .conpherence-widget-pane .widgets-header { - display: none; -} - -.conpherence-widget-pane .widgets-header .caret { - float: none; - height: 0px; - width: 0px; - margin: 10px 0 0 4px; - border-top-color: #000; -} - -.conpherence-widget-pane .widgets-header .phui-icon-view.disabled { - color: {$lightgreytext}; -} - -.device-desktop .conpherence-layout .device-widgets-selector { - display: none; -} - -.conpherence-widget-pane .widgets-body { - position: fixed; - overflow-y: auto; - bottom: 0; - top: 76px; - width: 100%; -} - -#widgets-settings { - padding: 3px 6px; -} - -.device-desktop .conpherence-widget-pane .widgets-body { - top: 111px; - width: 240px; -} - -.conpherence-widget-pane .widget-icon { - display: block; - height: 14px; - width: 14px; -} - -.conpherence-widget-pane .phabricator-remarkup-embed-layout-link { - padding-bottom: 1px; -} - -/* people widget */ -.conpherence-widget-pane .people-widget-header .add-people-widget { - padding: 10px 0 5px 0; - overflow: hidden; -} - -.conpherence-widget-pane .people-widget-header .add-people-widget -.aphront-form-control-tokenizer { - float: left; - width: 150px; - padding: 0px 0px 0px 10px -} - -.device .conpherence-widget-pane .people-widget-header .add-people-widget -.aphront-form-control-tokenizer { - width: 70%; -} - -.conpherence-widget-pane .people-widget-header .add-people-widget -.people-add-button { - float: right; - margin: 2px 8px 0px 0px; - padding: 3px 16px 4px 16px; -} - -#widgets-people { - margin-top: 4px; -} - -.conpherence-widget-pane .person-entry { - padding: 4px 8px; -} - -.conpherence-widget-pane .person-entry:hover { - background-color: {$lightgreybackground}; -} - -.conpherence-widget-pane .person-entry a { - float: left; - font-weight: bold; - line-height: 22px; - color: {$darkbluetext}; -} - -.conpherence-widget-pane .person-entry a img { - height: 24px; - width: 24px; - border-radius: 3px; -} - -.conpherence-widget-pane .person-entry .pic { - float: left; - width: 30px; -} - -.conpherence-widget-pane .person-entry .remove { - float: right; - width: 20px; - height: 20px; - font-size: 18px; -} - -.person-entry .remove:hover .phui-icon-view { - color: {$red}; -} - -.conpherence-widget-pane .person-entry .remove:hover { - text-decoration: none; -} - -.conpherence-widget-pane .person-entry .remove .close-icon { - color: #bfbfbf; -} - -.conpherence-widget-pane .person-entry .remove:hover .close-icon { - color: #000; -} - -/* settings widget */ -.conpherence-widget-pane .title-update, -.conpherence-widget-pane .notifications-update { - margin: 3px 0px 0px 4px; -} - -.conpherence-widget-pane .no-settings { - width: 200px; - padding: 20px; - text-align: center; - color: {$greytext}; -} - -.device .conpherence-widget-pane .no-settings { - width: 60px; - margin: 0 auto 0 auto; -} diff --git a/webroot/rsrc/css/application/project/project-view.css b/webroot/rsrc/css/application/project/project-view.css index 2c0fd7d6d8..7fc3001d80 100644 --- a/webroot/rsrc/css/application/project/project-view.css +++ b/webroot/rsrc/css/application/project/project-view.css @@ -36,10 +36,6 @@ padding: 12px 0; } -.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},.2); diff --git a/webroot/rsrc/css/core/remarkup.css b/webroot/rsrc/css/core/remarkup.css index 42d1d86597..a2644d2c37 100644 --- a/webroot/rsrc/css/core/remarkup.css +++ b/webroot/rsrc/css/core/remarkup.css @@ -54,6 +54,27 @@ white-space: pre-wrap; } +.phabricator-remarkup kbd { + display: inline-block; + min-width: 1em; + padding: 4px 5px 5px; + font-weight: normal; + font-size: 0.8rem; + text-align: center; + text-decoration: none; + line-height: 0.6rem; + border-radius: 3px; + box-shadow: inset 0 -1px 0 rgba({$alphablue},0.08); + user-select: none; + background: #f7f7f7; + border: 1px solid {$lightgreyborder}; +} + +.phabricator-remarkup .kbd-join { + padding: 0 4px; + color: {$lightgreytext}; +} + .phabricator-remarkup pre.remarkup-counterexample { background-color: {$sh-redbackground}; } diff --git a/webroot/rsrc/css/core/z-index.css b/webroot/rsrc/css/core/z-index.css index 5d787b694d..3447a18de4 100644 --- a/webroot/rsrc/css/core/z-index.css +++ b/webroot/rsrc/css/core/z-index.css @@ -150,6 +150,10 @@ div.jx-typeahead-results { z-index: 21; } +.conpherence-participant-pane { + z-index: 25; +} + .phuix-dropdown-menu { z-index: 32; } diff --git a/webroot/rsrc/css/phui/phui-crumbs-view.css b/webroot/rsrc/css/phui/phui-crumbs-view.css index 486ed1d5d3..3cf8013723 100644 --- a/webroot/rsrc/css/phui/phui-crumbs-view.css +++ b/webroot/rsrc/css/phui/phui-crumbs-view.css @@ -13,6 +13,10 @@ background-color: {$page.background}; } +.printable .phui-crumbs-view { + display: none; +} + .phui-crumbs-view, .phui-crumbs-view a.phui-crumb-view, .phui-crumbs-view a.phui-crumbs-action { diff --git a/webroot/rsrc/css/phui/phui-curtain-view.css b/webroot/rsrc/css/phui/phui-curtain-view.css index b33369abe0..7f42d53a3d 100644 --- a/webroot/rsrc/css/phui/phui-curtain-view.css +++ b/webroot/rsrc/css/phui/phui-curtain-view.css @@ -13,7 +13,7 @@ } .device-desktop .phui-curtain-panel { - border-top: 1px solid rgba({$alphablue}, .1); + border-top: 1px solid {$greybackground}; } .phui-curtain-panel-header { diff --git a/webroot/rsrc/css/phui/phui-document-pro.css b/webroot/rsrc/css/phui/phui-document-pro.css index 1027d03a56..4d0dfbdefd 100644 --- a/webroot/rsrc/css/phui/phui-document-pro.css +++ b/webroot/rsrc/css/phui/phui-document-pro.css @@ -20,6 +20,26 @@ margin: 0 auto; } +body.printable { + background-color: #fff; +} + +.printable .phui-document-view-pro-box { + display: none; +} + +.printable .phui-document-container { + border: none; +} + +.printable .phui-document-container .phui-header-view .phui-header-subheader { + display: none; +} + +.printable .phui-document-container .phui-header-view .phui-header-col3 { + display: none; +} + .device .phui-document-view-pro-box { margin: 0 8px; } @@ -47,6 +67,10 @@ left: -36px; } +.printable .phui-document-view-pro a.phui-document-toc { + display: none; +} + a.button.phui-document-toc { display: inline-block; height: 16px; diff --git a/webroot/rsrc/css/phui/phui-icon.css b/webroot/rsrc/css/phui/phui-icon.css index e2124c28b8..338ff4e4c3 100644 --- a/webroot/rsrc/css/phui/phui-icon.css +++ b/webroot/rsrc/css/phui/phui-icon.css @@ -71,16 +71,36 @@ a.phui-icon-view:hover { line-height: 36px; } -a.phui-icon-circle:hover { +a.phui-icon-circle.hover-sky:hover { text-decoration: none; border-color: {$sky}; cursor: pointer; } -a.phui-icon-circle:hover .phui-icon-view { +a.phui-icon-circle.hover-sky:hover .phui-icon-view { color: {$sky}; } +a.phui-icon-circle.hover-violet:hover { + text-decoration: none; + border-color: {$violet}; + cursor: pointer; +} + +a.phui-icon-circle.hover-violet:hover .phui-icon-view { + color: {$violet}; +} + +a.phui-icon-circle.hover-pink:hover { + text-decoration: none; + border-color: {$pink}; + cursor: pointer; +} + +a.phui-icon-circle.hover-pink:hover .phui-icon-view { + color: {$pink}; +} + /* - Icon in a Square ------------------------------------------------------- */ .phui-icon-view.phui-icon-square { diff --git a/webroot/rsrc/css/phui/phui-invisible-character-view.css b/webroot/rsrc/css/phui/phui-invisible-character-view.css new file mode 100644 index 0000000000..b8a848fa9a --- /dev/null +++ b/webroot/rsrc/css/phui/phui-invisible-character-view.css @@ -0,0 +1,12 @@ +/** + * @provides phui-invisible-character-view-css + */ + +.invisible-special { + font-family: monospace; + color: #000; + background: rgba({$alphablue},0.1); + padding: 1px 4px; + border-radius: 3px; + white-space: pre-wrap; +} diff --git a/webroot/rsrc/css/phui/phui-two-column-view.css b/webroot/rsrc/css/phui/phui-two-column-view.css index 41cfd2db21..57f8d81934 100644 --- a/webroot/rsrc/css/phui/phui-two-column-view.css +++ b/webroot/rsrc/css/phui/phui-two-column-view.css @@ -155,10 +155,6 @@ display: none; } -.phui-two-column-properties.phui-object-box { - border: 1px solid rgba({$alphablue}, .2); -} - .device .phui-two-column-content .phui-two-column-properties.phui-object-box { padding: 0 12px; } diff --git a/webroot/rsrc/css/sprite-login.css b/webroot/rsrc/css/sprite-login.css index 8ee1bfadf6..173213922d 100644 --- a/webroot/rsrc/css/sprite-login.css +++ b/webroot/rsrc/css/sprite-login.css @@ -14,7 +14,7 @@ only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx) { .sprite-login { background-image: url(/rsrc/image/sprite-login-X2.png); - background-size: 145px 145px; + background-size: 145px 174px; } } @@ -91,30 +91,34 @@ only screen and (min-resolution: 1.5dppx) { background-position: -58px -87px; } -.login-Stripe { +.login-Slack { background-position: -87px -87px; } -.login-TestPayment { +.login-Stripe { background-position: -116px -87px; } -.login-TwitchTV { +.login-TestPayment { background-position: 0px -116px; } -.login-Twitter { +.login-TwitchTV { background-position: -29px -116px; } -.login-WePay { +.login-Twitter { background-position: -58px -116px; } -.login-WordPressCOM { +.login-WePay { background-position: -87px -116px; } -.login-Yahoo { +.login-WordPressCOM { background-position: -116px -116px; } + +.login-Yahoo { + background-position: 0px -145px; +} diff --git a/webroot/rsrc/favicons/apple-touch-icon-114x114.png b/webroot/rsrc/favicons/apple-touch-icon-114x114.png new file mode 100644 index 0000000000..cb4d8cd937 Binary files /dev/null and b/webroot/rsrc/favicons/apple-touch-icon-114x114.png differ diff --git a/webroot/rsrc/favicons/apple-touch-icon-120x120.png b/webroot/rsrc/favicons/apple-touch-icon-120x120.png index 1ee45709aa..a88125beca 100644 Binary files a/webroot/rsrc/favicons/apple-touch-icon-120x120.png and b/webroot/rsrc/favicons/apple-touch-icon-120x120.png differ diff --git a/webroot/rsrc/favicons/apple-touch-icon-144x144.png b/webroot/rsrc/favicons/apple-touch-icon-144x144.png new file mode 100644 index 0000000000..92f2114b20 Binary files /dev/null and b/webroot/rsrc/favicons/apple-touch-icon-144x144.png differ diff --git a/webroot/rsrc/favicons/apple-touch-icon-152x152.png b/webroot/rsrc/favicons/apple-touch-icon-152x152.png index 69ab6d3554..94a69ade74 100644 Binary files a/webroot/rsrc/favicons/apple-touch-icon-152x152.png and b/webroot/rsrc/favicons/apple-touch-icon-152x152.png differ diff --git a/webroot/rsrc/favicons/apple-touch-icon-57x57.png b/webroot/rsrc/favicons/apple-touch-icon-57x57.png new file mode 100644 index 0000000000..55df0a48e5 Binary files /dev/null and b/webroot/rsrc/favicons/apple-touch-icon-57x57.png differ diff --git a/webroot/rsrc/favicons/apple-touch-icon-60x60.png b/webroot/rsrc/favicons/apple-touch-icon-60x60.png new file mode 100644 index 0000000000..97dfdbc36e Binary files /dev/null and b/webroot/rsrc/favicons/apple-touch-icon-60x60.png differ diff --git a/webroot/rsrc/favicons/apple-touch-icon-72x72.png b/webroot/rsrc/favicons/apple-touch-icon-72x72.png new file mode 100644 index 0000000000..cb1f066ad0 Binary files /dev/null and b/webroot/rsrc/favicons/apple-touch-icon-72x72.png differ diff --git a/webroot/rsrc/favicons/apple-touch-icon-76x76.png b/webroot/rsrc/favicons/apple-touch-icon-76x76.png index 3afc5777f1..0245ac2a21 100644 Binary files a/webroot/rsrc/favicons/apple-touch-icon-76x76.png and b/webroot/rsrc/favicons/apple-touch-icon-76x76.png differ diff --git a/webroot/rsrc/favicons/dark/apple-touch-icon-114x114.png b/webroot/rsrc/favicons/dark/apple-touch-icon-114x114.png new file mode 100644 index 0000000000..ddb29bcde2 Binary files /dev/null and b/webroot/rsrc/favicons/dark/apple-touch-icon-114x114.png differ diff --git a/webroot/rsrc/favicons/dark/apple-touch-icon-120x120.png b/webroot/rsrc/favicons/dark/apple-touch-icon-120x120.png new file mode 100644 index 0000000000..b641cd6558 Binary files /dev/null and b/webroot/rsrc/favicons/dark/apple-touch-icon-120x120.png differ diff --git a/webroot/rsrc/favicons/dark/apple-touch-icon-144x144.png b/webroot/rsrc/favicons/dark/apple-touch-icon-144x144.png new file mode 100644 index 0000000000..d2cf49d293 Binary files /dev/null and b/webroot/rsrc/favicons/dark/apple-touch-icon-144x144.png differ diff --git a/webroot/rsrc/favicons/dark/apple-touch-icon-152x152.png b/webroot/rsrc/favicons/dark/apple-touch-icon-152x152.png new file mode 100644 index 0000000000..41a146e9f3 Binary files /dev/null and b/webroot/rsrc/favicons/dark/apple-touch-icon-152x152.png differ diff --git a/webroot/rsrc/favicons/dark/apple-touch-icon-57x57.png b/webroot/rsrc/favicons/dark/apple-touch-icon-57x57.png new file mode 100644 index 0000000000..a5141404ce Binary files /dev/null and b/webroot/rsrc/favicons/dark/apple-touch-icon-57x57.png differ diff --git a/webroot/rsrc/favicons/dark/apple-touch-icon-60x60.png b/webroot/rsrc/favicons/dark/apple-touch-icon-60x60.png new file mode 100644 index 0000000000..c8dc8def04 Binary files /dev/null and b/webroot/rsrc/favicons/dark/apple-touch-icon-60x60.png differ diff --git a/webroot/rsrc/favicons/dark/apple-touch-icon-72x72.png b/webroot/rsrc/favicons/dark/apple-touch-icon-72x72.png new file mode 100644 index 0000000000..78ae78918b Binary files /dev/null and b/webroot/rsrc/favicons/dark/apple-touch-icon-72x72.png differ diff --git a/webroot/rsrc/favicons/dark/apple-touch-icon-76x76.png b/webroot/rsrc/favicons/dark/apple-touch-icon-76x76.png new file mode 100644 index 0000000000..060cfb4838 Binary files /dev/null and b/webroot/rsrc/favicons/dark/apple-touch-icon-76x76.png differ diff --git a/webroot/rsrc/favicons/dark/favicon-128.png b/webroot/rsrc/favicons/dark/favicon-128.png new file mode 100644 index 0000000000..01165d4f83 Binary files /dev/null and b/webroot/rsrc/favicons/dark/favicon-128.png differ diff --git a/webroot/rsrc/favicons/dark/favicon-16x16.png b/webroot/rsrc/favicons/dark/favicon-16x16.png new file mode 100644 index 0000000000..e98657cc57 Binary files /dev/null and b/webroot/rsrc/favicons/dark/favicon-16x16.png differ diff --git a/webroot/rsrc/favicons/dark/favicon-196x196.png b/webroot/rsrc/favicons/dark/favicon-196x196.png new file mode 100644 index 0000000000..78cbe52e78 Binary files /dev/null and b/webroot/rsrc/favicons/dark/favicon-196x196.png differ diff --git a/webroot/rsrc/favicons/dark/favicon-32x32.png b/webroot/rsrc/favicons/dark/favicon-32x32.png new file mode 100644 index 0000000000..c2955ad83a Binary files /dev/null and b/webroot/rsrc/favicons/dark/favicon-32x32.png differ diff --git a/webroot/rsrc/favicons/dark/favicon-96x96.png b/webroot/rsrc/favicons/dark/favicon-96x96.png new file mode 100644 index 0000000000..9cc09dd833 Binary files /dev/null and b/webroot/rsrc/favicons/dark/favicon-96x96.png differ diff --git a/webroot/rsrc/favicons/dark/favicon.ico b/webroot/rsrc/favicons/dark/favicon.ico new file mode 100644 index 0000000000..ce3d048bd4 Binary files /dev/null and b/webroot/rsrc/favicons/dark/favicon.ico differ diff --git a/webroot/rsrc/favicons/dark/mstile-144x144.png b/webroot/rsrc/favicons/dark/mstile-144x144.png new file mode 100644 index 0000000000..d2cf49d293 Binary files /dev/null and b/webroot/rsrc/favicons/dark/mstile-144x144.png differ diff --git a/webroot/rsrc/favicons/dark/mstile-150x150.png b/webroot/rsrc/favicons/dark/mstile-150x150.png new file mode 100644 index 0000000000..b56a239e02 Binary files /dev/null and b/webroot/rsrc/favicons/dark/mstile-150x150.png differ diff --git a/webroot/rsrc/favicons/dark/mstile-310x150.png b/webroot/rsrc/favicons/dark/mstile-310x150.png new file mode 100644 index 0000000000..99163350dd Binary files /dev/null and b/webroot/rsrc/favicons/dark/mstile-310x150.png differ diff --git a/webroot/rsrc/favicons/dark/mstile-310x310.png b/webroot/rsrc/favicons/dark/mstile-310x310.png new file mode 100644 index 0000000000..136a8e4034 Binary files /dev/null and b/webroot/rsrc/favicons/dark/mstile-310x310.png differ diff --git a/webroot/rsrc/favicons/dark/mstile-70x70.png b/webroot/rsrc/favicons/dark/mstile-70x70.png new file mode 100644 index 0000000000..01165d4f83 Binary files /dev/null and b/webroot/rsrc/favicons/dark/mstile-70x70.png differ diff --git a/webroot/rsrc/favicons/favicon-128.png b/webroot/rsrc/favicons/favicon-128.png index 495b8edf7b..3b51e6052b 100644 Binary files a/webroot/rsrc/favicons/favicon-128.png and b/webroot/rsrc/favicons/favicon-128.png differ diff --git a/webroot/rsrc/favicons/favicon-16x16.png b/webroot/rsrc/favicons/favicon-16x16.png index cd7a66fd9c..1fbf23eb5a 100644 Binary files a/webroot/rsrc/favicons/favicon-16x16.png and b/webroot/rsrc/favicons/favicon-16x16.png differ diff --git a/webroot/rsrc/favicons/favicon-196x196.png b/webroot/rsrc/favicons/favicon-196x196.png new file mode 100644 index 0000000000..bf30eb1cf8 Binary files /dev/null and b/webroot/rsrc/favicons/favicon-196x196.png differ diff --git a/webroot/rsrc/favicons/favicon-32x32.png b/webroot/rsrc/favicons/favicon-32x32.png index b98590889e..c6f2a75566 100644 Binary files a/webroot/rsrc/favicons/favicon-32x32.png and b/webroot/rsrc/favicons/favicon-32x32.png differ diff --git a/webroot/rsrc/favicons/favicon-96x96.png b/webroot/rsrc/favicons/favicon-96x96.png index a62787803f..5f0522be65 100644 Binary files a/webroot/rsrc/favicons/favicon-96x96.png and b/webroot/rsrc/favicons/favicon-96x96.png differ diff --git a/webroot/rsrc/favicons/favicon.ico b/webroot/rsrc/favicons/favicon.ico new file mode 100644 index 0000000000..f07770aa8a Binary files /dev/null and b/webroot/rsrc/favicons/favicon.ico differ diff --git a/webroot/rsrc/favicons/mask-icon.svg b/webroot/rsrc/favicons/mask-icon.svg index d949114e43..b6beedd96f 100644 --- a/webroot/rsrc/favicons/mask-icon.svg +++ b/webroot/rsrc/favicons/mask-icon.svg @@ -1,20 +1,50 @@ - - - - + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + diff --git a/webroot/rsrc/favicons/mstile-144x144.png b/webroot/rsrc/favicons/mstile-144x144.png new file mode 100644 index 0000000000..92f2114b20 Binary files /dev/null and b/webroot/rsrc/favicons/mstile-144x144.png differ diff --git a/webroot/rsrc/favicons/mstile-150x150.png b/webroot/rsrc/favicons/mstile-150x150.png new file mode 100644 index 0000000000..df1804953a Binary files /dev/null and b/webroot/rsrc/favicons/mstile-150x150.png differ diff --git a/webroot/rsrc/favicons/mstile-310x150.png b/webroot/rsrc/favicons/mstile-310x150.png new file mode 100644 index 0000000000..56c3ed1985 Binary files /dev/null and b/webroot/rsrc/favicons/mstile-310x150.png differ diff --git a/webroot/rsrc/favicons/mstile-310x310.png b/webroot/rsrc/favicons/mstile-310x310.png new file mode 100644 index 0000000000..ef911d803d Binary files /dev/null and b/webroot/rsrc/favicons/mstile-310x310.png differ diff --git a/webroot/rsrc/favicons/mstile-70x70.png b/webroot/rsrc/favicons/mstile-70x70.png new file mode 100644 index 0000000000..3b51e6052b Binary files /dev/null and b/webroot/rsrc/favicons/mstile-70x70.png differ diff --git a/webroot/rsrc/favicons/red/apple-touch-icon-114x114.png b/webroot/rsrc/favicons/red/apple-touch-icon-114x114.png new file mode 100644 index 0000000000..9d8293ec39 Binary files /dev/null and b/webroot/rsrc/favicons/red/apple-touch-icon-114x114.png differ diff --git a/webroot/rsrc/favicons/red/apple-touch-icon-120x120.png b/webroot/rsrc/favicons/red/apple-touch-icon-120x120.png new file mode 100644 index 0000000000..89f65a0e2e Binary files /dev/null and b/webroot/rsrc/favicons/red/apple-touch-icon-120x120.png differ diff --git a/webroot/rsrc/favicons/red/apple-touch-icon-144x144.png b/webroot/rsrc/favicons/red/apple-touch-icon-144x144.png new file mode 100644 index 0000000000..b514bdc033 Binary files /dev/null and b/webroot/rsrc/favicons/red/apple-touch-icon-144x144.png differ diff --git a/webroot/rsrc/favicons/red/apple-touch-icon-152x152.png b/webroot/rsrc/favicons/red/apple-touch-icon-152x152.png new file mode 100644 index 0000000000..df893974a5 Binary files /dev/null and b/webroot/rsrc/favicons/red/apple-touch-icon-152x152.png differ diff --git a/webroot/rsrc/favicons/red/apple-touch-icon-57x57.png b/webroot/rsrc/favicons/red/apple-touch-icon-57x57.png new file mode 100644 index 0000000000..c8b423e6c0 Binary files /dev/null and b/webroot/rsrc/favicons/red/apple-touch-icon-57x57.png differ diff --git a/webroot/rsrc/favicons/red/apple-touch-icon-60x60.png b/webroot/rsrc/favicons/red/apple-touch-icon-60x60.png new file mode 100644 index 0000000000..4ef5b42ccf Binary files /dev/null and b/webroot/rsrc/favicons/red/apple-touch-icon-60x60.png differ diff --git a/webroot/rsrc/favicons/red/apple-touch-icon-72x72.png b/webroot/rsrc/favicons/red/apple-touch-icon-72x72.png new file mode 100644 index 0000000000..75d9ba8407 Binary files /dev/null and b/webroot/rsrc/favicons/red/apple-touch-icon-72x72.png differ diff --git a/webroot/rsrc/favicons/red/apple-touch-icon-76x76.png b/webroot/rsrc/favicons/red/apple-touch-icon-76x76.png new file mode 100644 index 0000000000..bd7593a772 Binary files /dev/null and b/webroot/rsrc/favicons/red/apple-touch-icon-76x76.png differ diff --git a/webroot/rsrc/favicons/red/favicon-128.png b/webroot/rsrc/favicons/red/favicon-128.png new file mode 100644 index 0000000000..c9a9df6314 Binary files /dev/null and b/webroot/rsrc/favicons/red/favicon-128.png differ diff --git a/webroot/rsrc/favicons/red/favicon-16x16.png b/webroot/rsrc/favicons/red/favicon-16x16.png new file mode 100644 index 0000000000..a98536fb93 Binary files /dev/null and b/webroot/rsrc/favicons/red/favicon-16x16.png differ diff --git a/webroot/rsrc/favicons/red/favicon-196x196.png b/webroot/rsrc/favicons/red/favicon-196x196.png new file mode 100644 index 0000000000..f4a9c2a69f Binary files /dev/null and b/webroot/rsrc/favicons/red/favicon-196x196.png differ diff --git a/webroot/rsrc/favicons/red/favicon-32x32.png b/webroot/rsrc/favicons/red/favicon-32x32.png new file mode 100644 index 0000000000..7bc7c159db Binary files /dev/null and b/webroot/rsrc/favicons/red/favicon-32x32.png differ diff --git a/webroot/rsrc/favicons/red/favicon-96x96.png b/webroot/rsrc/favicons/red/favicon-96x96.png new file mode 100644 index 0000000000..4a874510f6 Binary files /dev/null and b/webroot/rsrc/favicons/red/favicon-96x96.png differ diff --git a/webroot/rsrc/favicons/red/favicon.ico b/webroot/rsrc/favicons/red/favicon.ico new file mode 100644 index 0000000000..1d9437706e Binary files /dev/null and b/webroot/rsrc/favicons/red/favicon.ico differ diff --git a/webroot/rsrc/favicons/red/mstile-144x144.png b/webroot/rsrc/favicons/red/mstile-144x144.png new file mode 100644 index 0000000000..b514bdc033 Binary files /dev/null and b/webroot/rsrc/favicons/red/mstile-144x144.png differ diff --git a/webroot/rsrc/favicons/red/mstile-150x150.png b/webroot/rsrc/favicons/red/mstile-150x150.png new file mode 100644 index 0000000000..63cba86e5a Binary files /dev/null and b/webroot/rsrc/favicons/red/mstile-150x150.png differ diff --git a/webroot/rsrc/favicons/red/mstile-310x150.png b/webroot/rsrc/favicons/red/mstile-310x150.png new file mode 100644 index 0000000000..ca5a1cb9a3 Binary files /dev/null and b/webroot/rsrc/favicons/red/mstile-310x150.png differ diff --git a/webroot/rsrc/favicons/red/mstile-310x310.png b/webroot/rsrc/favicons/red/mstile-310x310.png new file mode 100644 index 0000000000..abb890254f Binary files /dev/null and b/webroot/rsrc/favicons/red/mstile-310x310.png differ diff --git a/webroot/rsrc/favicons/red/mstile-70x70.png b/webroot/rsrc/favicons/red/mstile-70x70.png new file mode 100644 index 0000000000..c9a9df6314 Binary files /dev/null and b/webroot/rsrc/favicons/red/mstile-70x70.png differ diff --git a/webroot/rsrc/favicons/yellow/apple-touch-icon-114x114.png b/webroot/rsrc/favicons/yellow/apple-touch-icon-114x114.png new file mode 100644 index 0000000000..81ca69890a Binary files /dev/null and b/webroot/rsrc/favicons/yellow/apple-touch-icon-114x114.png differ diff --git a/webroot/rsrc/favicons/yellow/apple-touch-icon-120x120.png b/webroot/rsrc/favicons/yellow/apple-touch-icon-120x120.png new file mode 100644 index 0000000000..8ca5ddb2d2 Binary files /dev/null and b/webroot/rsrc/favicons/yellow/apple-touch-icon-120x120.png differ diff --git a/webroot/rsrc/favicons/yellow/apple-touch-icon-144x144.png b/webroot/rsrc/favicons/yellow/apple-touch-icon-144x144.png new file mode 100644 index 0000000000..73fcc43063 Binary files /dev/null and b/webroot/rsrc/favicons/yellow/apple-touch-icon-144x144.png differ diff --git a/webroot/rsrc/favicons/yellow/apple-touch-icon-152x152.png b/webroot/rsrc/favicons/yellow/apple-touch-icon-152x152.png new file mode 100644 index 0000000000..3de0817a02 Binary files /dev/null and b/webroot/rsrc/favicons/yellow/apple-touch-icon-152x152.png differ diff --git a/webroot/rsrc/favicons/yellow/apple-touch-icon-57x57.png b/webroot/rsrc/favicons/yellow/apple-touch-icon-57x57.png new file mode 100644 index 0000000000..a41af6d514 Binary files /dev/null and b/webroot/rsrc/favicons/yellow/apple-touch-icon-57x57.png differ diff --git a/webroot/rsrc/favicons/yellow/apple-touch-icon-60x60.png b/webroot/rsrc/favicons/yellow/apple-touch-icon-60x60.png new file mode 100644 index 0000000000..45a8e345f6 Binary files /dev/null and b/webroot/rsrc/favicons/yellow/apple-touch-icon-60x60.png differ diff --git a/webroot/rsrc/favicons/yellow/apple-touch-icon-72x72.png b/webroot/rsrc/favicons/yellow/apple-touch-icon-72x72.png new file mode 100644 index 0000000000..8f4f1b35c3 Binary files /dev/null and b/webroot/rsrc/favicons/yellow/apple-touch-icon-72x72.png differ diff --git a/webroot/rsrc/favicons/yellow/apple-touch-icon-76x76.png b/webroot/rsrc/favicons/yellow/apple-touch-icon-76x76.png new file mode 100644 index 0000000000..1f386e2377 Binary files /dev/null and b/webroot/rsrc/favicons/yellow/apple-touch-icon-76x76.png differ diff --git a/webroot/rsrc/favicons/yellow/favicon-128.png b/webroot/rsrc/favicons/yellow/favicon-128.png new file mode 100644 index 0000000000..a7411cc7db Binary files /dev/null and b/webroot/rsrc/favicons/yellow/favicon-128.png differ diff --git a/webroot/rsrc/favicons/yellow/favicon-16x16.png b/webroot/rsrc/favicons/yellow/favicon-16x16.png new file mode 100644 index 0000000000..50847ee4c5 Binary files /dev/null and b/webroot/rsrc/favicons/yellow/favicon-16x16.png differ diff --git a/webroot/rsrc/favicons/yellow/favicon-196x196.png b/webroot/rsrc/favicons/yellow/favicon-196x196.png new file mode 100644 index 0000000000..577a45d64a Binary files /dev/null and b/webroot/rsrc/favicons/yellow/favicon-196x196.png differ diff --git a/webroot/rsrc/favicons/yellow/favicon-32x32.png b/webroot/rsrc/favicons/yellow/favicon-32x32.png new file mode 100644 index 0000000000..9b92b3ff80 Binary files /dev/null and b/webroot/rsrc/favicons/yellow/favicon-32x32.png differ diff --git a/webroot/rsrc/favicons/yellow/favicon-96x96.png b/webroot/rsrc/favicons/yellow/favicon-96x96.png new file mode 100644 index 0000000000..4070e92109 Binary files /dev/null and b/webroot/rsrc/favicons/yellow/favicon-96x96.png differ diff --git a/webroot/rsrc/favicons/yellow/favicon.ico b/webroot/rsrc/favicons/yellow/favicon.ico new file mode 100644 index 0000000000..7728c042e3 Binary files /dev/null and b/webroot/rsrc/favicons/yellow/favicon.ico differ diff --git a/webroot/rsrc/favicons/yellow/mstile-144x144.png b/webroot/rsrc/favicons/yellow/mstile-144x144.png new file mode 100644 index 0000000000..73fcc43063 Binary files /dev/null and b/webroot/rsrc/favicons/yellow/mstile-144x144.png differ diff --git a/webroot/rsrc/favicons/yellow/mstile-150x150.png b/webroot/rsrc/favicons/yellow/mstile-150x150.png new file mode 100644 index 0000000000..3d090a694a Binary files /dev/null and b/webroot/rsrc/favicons/yellow/mstile-150x150.png differ diff --git a/webroot/rsrc/favicons/yellow/mstile-310x150.png b/webroot/rsrc/favicons/yellow/mstile-310x150.png new file mode 100644 index 0000000000..08d4855c67 Binary files /dev/null and b/webroot/rsrc/favicons/yellow/mstile-310x150.png differ diff --git a/webroot/rsrc/favicons/yellow/mstile-310x310.png b/webroot/rsrc/favicons/yellow/mstile-310x310.png new file mode 100644 index 0000000000..9b95754168 Binary files /dev/null and b/webroot/rsrc/favicons/yellow/mstile-310x310.png differ diff --git a/webroot/rsrc/favicons/yellow/mstile-70x70.png b/webroot/rsrc/favicons/yellow/mstile-70x70.png new file mode 100644 index 0000000000..a7411cc7db Binary files /dev/null and b/webroot/rsrc/favicons/yellow/mstile-70x70.png differ diff --git a/webroot/rsrc/image/sprite-login-X2.png b/webroot/rsrc/image/sprite-login-X2.png index 3a75a1890a..78a044ee72 100644 Binary files a/webroot/rsrc/image/sprite-login-X2.png and b/webroot/rsrc/image/sprite-login-X2.png differ diff --git a/webroot/rsrc/image/sprite-login.png b/webroot/rsrc/image/sprite-login.png index 830cc28dc4..a877ad3754 100644 Binary files a/webroot/rsrc/image/sprite-login.png and b/webroot/rsrc/image/sprite-login.png differ diff --git a/webroot/rsrc/js/application/conpherence/behavior-menu.js b/webroot/rsrc/js/application/conpherence/behavior-menu.js index 5b93815716..50add23158 100644 --- a/webroot/rsrc/js/application/conpherence/behavior-menu.js +++ b/webroot/rsrc/js/application/conpherence/behavior-menu.js @@ -231,7 +231,7 @@ JX.behavior('conpherence-menu', function(config) { function markWidgetLoading(loading) { var root = JX.DOM.find(document, 'div', 'conpherence-layout'); - var widgets_root = JX.DOM.find(root, 'div', 'conpherence-widget-pane'); + var widgets_root = JX.DOM.find(root, 'div', 'conpherence-participant-pane'); JX.DOM.alterClass(widgets_root, 'loading', loading); } @@ -241,7 +241,7 @@ JX.behavior('conpherence-menu', function(config) { if (!data.widget) { data.widget = getDefaultWidget(); } - var widget_uri = config.baseURI + 'widget/' + data.threadID + '/'; + var widget_uri = config.baseURI + 'participant/' + data.threadID + '/'; new JX.Workflow(widget_uri, {}) .setHandler(JX.bind(null, onWidgetResponse, data.threadID, data.widget)) .start(); diff --git a/webroot/rsrc/js/application/conpherence/behavior-participant-pane.js b/webroot/rsrc/js/application/conpherence/behavior-participant-pane.js new file mode 100644 index 0000000000..8c9fc056f4 --- /dev/null +++ b/webroot/rsrc/js/application/conpherence/behavior-participant-pane.js @@ -0,0 +1,112 @@ +/** + * @requires javelin-behavior + * javelin-dom + * javelin-stratcom + * javelin-workflow + * javelin-util + * phabricator-notification + * conpherence-thread-manager + * @provides javelin-behavior-conpherence-participant-pane + */ + +JX.behavior('conpherence-participant-pane', function() { + + /** + * Generified adding new stuff to widgets technology! + */ + JX.Stratcom.listen( + ['click'], + 'conpherence-widget-adder', + function (e) { + e.kill(); + + var threadManager = JX.ConpherenceThreadManager.getInstance(); + var href = threadManager._getUpdateURI(); + var latest_transaction_id = threadManager.getLatestTransactionID(); + var data = { + latest_transaction_id : latest_transaction_id, + action : 'add_person' + }; + + var workflow = new JX.Workflow(href, data) + .setHandler(function (r) { + var threadManager = JX.ConpherenceThreadManager.getInstance(); + threadManager.setLatestTransactionID(r.latest_transaction_id); + var root = JX.DOM.find(document, 'div', 'conpherence-layout'); + var messages = null; + try { + messages = JX.DOM.find(root, 'div', 'conpherence-messages'); + } catch (ex) { + } + if (messages) { + JX.DOM.appendContent(messages, JX.$H(r.transactions)); + JX.Stratcom.invoke('conpherence-redraw-thread', null, {}); + } + + try { + var people_root = JX.DOM.find(root, 'div', 'widgets-people'); + // update the people widget + JX.DOM.setContent( + people_root, + JX.$H(r.people_widget)); + } catch (ex) { + } + + }); + + threadManager.syncWorkflow(workflow, 'submit'); + } + ); + + JX.Stratcom.listen( + ['touchstart', 'mousedown'], + 'remove-person', + function (e) { + var threadManager = JX.ConpherenceThreadManager.getInstance(); + var href = threadManager._getUpdateURI(); + var data = e.getNodeData('remove-person'); + + // While the user is removing themselves, disable the notification + // update behavior. If we don't do this, the user can get an error + // when they remove themselves about permissions as the notification + // code tries to load what jist happened. + var loadedPhid = threadManager.getLoadedThreadPHID(); + threadManager.setLoadedThreadPHID(null); + + new JX.Workflow(href, data) + .setCloseHandler(function() { + threadManager.setLoadedThreadPHID(loadedPhid); + }) + // we re-direct to conpherence home so the thread manager will + // fix itself there + .setHandler(function(r) { + JX.$U(r.href).go(); + }) + .start(); + } + ); + + /* settings widget */ + var onsubmitSettings = function (e) { + e.kill(); + var form = e.getNode('tag:form'); + var button = JX.DOM.find(form, 'button'); + JX.Workflow.newFromForm(form) + .setHandler(JX.bind(this, function (r) { + new JX.Notification() + .setDuration(6000) + .setContent(r) + .show(); + button.disabled = ''; + JX.DOM.alterClass(button, 'disabled', false); + })) + .start(); + }; + + JX.Stratcom.listen( + ['submit', 'didSyntheticSubmit'], + 'notifications-update', + onsubmitSettings + ); + +}); diff --git a/webroot/rsrc/js/application/conpherence/behavior-toggle-widget.js b/webroot/rsrc/js/application/conpherence/behavior-toggle-widget.js new file mode 100644 index 0000000000..1b36f8ff79 --- /dev/null +++ b/webroot/rsrc/js/application/conpherence/behavior-toggle-widget.js @@ -0,0 +1,29 @@ +/** + * @provides javelin-behavior-toggle-widget + * @requires javelin-behavior + * javelin-dom + * javelin-util + * javelin-workflow + * javelin-stratcom + */ + +JX.behavior('toggle-widget', function(config) { + + function _toggleColumn(e) { + e.kill(); + var node = JX.$('conpherence-main-layout'); + config.show = !config.show; + JX.DOM.alterClass(node, 'hide-widgets', !config.show); + JX.Stratcom.invoke('resize'); + + new JX.Request(config.settingsURI) + .setData({value: (config.show ? 1 : 0)}) + .send(); + } + + JX.Stratcom.listen( + 'click', + 'conpherence-widget-toggle', + _toggleColumn); + +}); diff --git a/webroot/rsrc/js/application/conpherence/behavior-widget-pane.js b/webroot/rsrc/js/application/conpherence/behavior-widget-pane.js deleted file mode 100644 index f83dc1b4d4..0000000000 --- a/webroot/rsrc/js/application/conpherence/behavior-widget-pane.js +++ /dev/null @@ -1,398 +0,0 @@ -/** - * @requires javelin-behavior - * javelin-dom - * javelin-stratcom - * javelin-workflow - * javelin-util - * phabricator-notification - * javelin-behavior-device - * phuix-dropdown-menu - * phuix-action-list-view - * phuix-action-view - * conpherence-thread-manager - * @provides javelin-behavior-conpherence-widget-pane - */ - -JX.behavior('conpherence-widget-pane', function(config) { - - /** - * There can be race conditions around loading the messages or the widgets - * first. Keep track of what widgets we've loaded with this variable. - */ - var _loadedWidgetsID = null; - - /** - * At any given time there can be only one selected widget. Keep track of - * which one it is by the user-facing name for ease of use with - * PhabricatorDropdownMenuItems. - */ - var _selectedWidgetName = null; - - /** - * This is potentially built each time the user switches conpherence threads - * or when the result JX.Device.getDevice() changes from desktop to some - * other value. - */ - var buildDeviceWidgetSelector = function (data) { - var device_header = _getDeviceWidgetHeader(); - if (!device_header) { - return; - } - JX.DOM.show(device_header); - var device_menu = new JX.PHUIXDropdownMenu(device_header); - data.deviceMenu = true; - _buildWidgetSelector(device_menu, data); - }; - - /** - * This is potentially built each time the user switches conpherence threads - * or when the result JX.Device.getDevice() changes from mobile or tablet to - * desktop. - */ - var buildDesktopWidgetSelector = function (data) { - var root = JX.DOM.find(document, 'div', 'conpherence-layout'); - var widget_pane = JX.DOM.find(root, 'div', 'conpherence-widget-pane'); - var widget_header = JX.DOM.find(widget_pane, 'a', 'widgets-selector'); - - var menu = new JX.PHUIXDropdownMenu(widget_header); - menu - .setAlign('left') - .setOffsetY(4); - - data.deviceMenu = false; - _buildWidgetSelector(menu, data); - }; - - /** - * Workhorse that actually builds the widget selector. Note some fancy bits - * where we listen for the "open" event and enable / disable widgets as - * appropos. - */ - var _buildWidgetSelector = function (menu, data) { - _loadedWidgetsID = data.threadID; - - var list = new JX.PHUIXActionListView(); - var map = {}; - - var widgets = config.widgetRegistry; - for (var widget in widgets) { - var widget_data = widgets[widget]; - if (widget_data.deviceOnly && data.deviceMenu === false) { - continue; - } - - var handler; - var href; - if (widget == 'widgets-edit') { - var threadManager = JX.ConpherenceThreadManager.getInstance(); - handler = function(e) { - e.prevent(); - menu.close(); - threadManager.runUpdateWorkflowFromLink( - e.getTarget(), - { - action : 'metadata', - force_ajax : true, - stage : 'submit' - }); - }; - href = threadManager._getUpdateURI(); - } else { - handler = JX.bind(null, function(widget, e) { - toggleWidget({widget: widget}); - e.prevent(); - menu.close(); - }, widget); - } - var item = new JX.PHUIXActionView() - .setIcon(widget_data.icon || 'none') - .setName(widget_data.name) - .setHref(href) - .setHandler(handler); - map[widget_data.name] = item; - list.addItem(item); - } - - menu - .setWidth(200) - .setContent(list.getNode()); - - menu.listen('open', function() { - for (var k in map) { - map[k].setDisabled((k == _selectedWidgetName)); - } - }); - }; - - /** - * Since this is not always on the page, avoid having a repeat - * try / catch block and consolidate into this helper function. - */ - var _getDeviceWidgetHeader = function () { - var root = JX.DOM.find(document, 'div', 'conpherence-layout'); - var device_header = null; - try { - device_header = JX.DOM.find( - root, - 'a', - 'device-widgets-selector'); - } catch (ex) { - // is okay - no deviceWidgetHeader yet... but bail time - } - return device_header; - }; - - /** - * Responder to the 'conpherence-did-redraw-thread' event, this bad boy - * hides or shows the device widget selector as appropros. - */ - var _didRedrawThread = function (data) { - if (_loadedWidgetsID === null || _loadedWidgetsID != data.threadID) { - return; - } - var device = JX.Device.getDevice(); - var device_selector = _getDeviceWidgetHeader(); - if (device == 'desktop') { - JX.DOM.hide(device_selector); - } else { - JX.DOM.show(device_selector); - } - if (data.buildDeviceWidgetSelector) { - buildDeviceWidgetSelector(data); - } - toggleWidget(data); - }; - JX.Stratcom.listen( - 'conpherence-did-redraw-thread', - null, - function (e) { - _didRedrawThread(e.getData()); - } - ); - - /** - * Toggling a widget involves showing / hiding the appropriate widget - * bodies as well as updating the selectors to have the label on the - * newly selected widget. - */ - var toggleWidget = function (data) { - var widgets = config.widgetRegistry; - var widget_data = widgets[data.widget]; - var device = JX.Device.getDevice(); - var is_desktop = device == 'desktop'; - - if (widget_data.deviceOnly && is_desktop) { - return; - } - _selectedWidgetName = widget_data.name; - - var device_header = _getDeviceWidgetHeader(); - if (device_header) { - // this is fragile but adding a sigil to this element is awkward - var device_header_spans = JX.DOM.scry(device_header, 'span'); - var device_header_span = device_header_spans[1]; - JX.DOM.setContent( - device_header_span, - widget_data.name); - } - - // don't update the non-device selector with device only widget stuff - if (!widget_data.deviceOnly) { - var root = JX.DOM.find(document, 'div', 'conpherence-layout'); - var widget_pane = JX.DOM.find(root, 'div', 'conpherence-widget-pane'); - var widget_header = JX.DOM.find(widget_pane, 'a', 'widgets-selector'); - var adder = JX.DOM.find(widget_pane, 'a', 'conpherence-widget-adder'); - var threadManager = JX.ConpherenceThreadManager.getInstance(); - var disabled = !threadManager.getCanEditLoadedThread(); - JX.DOM.setContent( - widget_header, - widget_data.name); - JX.DOM.appendContent( - widget_header, - JX.$N('span', { className : 'caret' })); - if (widget_data.hasCreate) { - JX.DOM.show(adder); - JX.DOM.alterClass( - adder, - 'disabled', - disabled); - } else { - JX.DOM.hide(adder); - } - } - - for (var widget in config.widgetRegistry) { - widget_data = widgets[widget]; - if (widget_data.deviceOnly && is_desktop) { - // some one off code for conpherence messages which are device-only - // as a widget, but shown always on the desktop - if (widget == 'conpherence-message-pane') { - JX.$(widget).style.display = 'block'; - } - continue; - } - if (widget == data.widget) { - JX.$(widget).style.display = 'block'; - // some one off code for conpherence messages - fancier refresh tech - if (widget == 'conpherence-message-pane') { - JX.Stratcom.invoke('conpherence-redraw-thread', null, {}); - JX.Stratcom.invoke('conpherence-update-page-data', null, {}); - } - } else { - JX.$(widget).style.display = 'none'; - } - } - }; - - JX.Stratcom.listen( - 'conpherence-update-widgets', - null, - function (e) { - var data = e.getData(); - if (data.buildSelectors) { - buildDesktopWidgetSelector(data); - buildDeviceWidgetSelector(data); - } - if (data.toggleWidget) { - toggleWidget(data); - } - }); - - /** - * Generified adding new stuff to widgets technology! - */ - JX.Stratcom.listen( - ['click'], - 'conpherence-widget-adder', - function (e) { - e.kill(); - - var widgets = config.widgetRegistry; - // the widget key might be in node data, but otherwise use the - // selected widget - var event_data = e.getNodeData('conpherence-widget-adder'); - var widget_key = _selectedWidgetName; - if (event_data.widget) { - widget_key = widgets[event_data.widget].name; - } - - var widget_to_update = null; - var create_data = null; - for (var widget in widgets) { - if (widgets[widget].name == widget_key) { - create_data = widgets[widget].createData; - widget_to_update = widget; - break; - } - } - // this should be impossible, but hey - if (!widget_to_update) { - return; - } - var href = config.widgetBaseUpdateURI + _loadedWidgetsID + '/'; - if (create_data.customHref) { - href = create_data.customHref; - } - - var threadManager = JX.ConpherenceThreadManager.getInstance(); - var latest_transaction_id = threadManager.getLatestTransactionID(); - var data = { - latest_transaction_id : latest_transaction_id, - action : create_data.action - }; - - var workflow = new JX.Workflow(href, data) - .setHandler(function (r) { - var threadManager = JX.ConpherenceThreadManager.getInstance(); - threadManager.setLatestTransactionID(r.latest_transaction_id); - var root = JX.DOM.find(document, 'div', 'conpherence-layout'); - if (create_data.refreshFromResponse) { - var messages = null; - try { - messages = JX.DOM.find(root, 'div', 'conpherence-messages'); - } catch (ex) { - } - if (messages) { - JX.DOM.appendContent(messages, JX.$H(r.transactions)); - JX.Stratcom.invoke('conpherence-redraw-thread', null, {}); - } - - if (r.people_widget) { - try { - var people_root = JX.DOM.find(root, 'div', 'widgets-people'); - // update the people widget - JX.DOM.setContent( - people_root, - JX.$H(r.people_widget)); - } catch (ex) { - } - } - - // otherwise let's redraw the widget somewhat lazily - } else { - JX.Stratcom.invoke( - 'conpherence-reload-widget', - null, - { - threadID : _loadedWidgetsID, - widget : widget_to_update - }); - } - }); - - threadManager.syncWorkflow(workflow, 'submit'); - } - ); - - JX.Stratcom.listen( - ['touchstart', 'mousedown'], - 'remove-person', - function (e) { - var href = config.widgetBaseUpdateURI + _loadedWidgetsID + '/'; - var data = e.getNodeData('remove-person'); - - // While the user is removing themselves, disable the notification - // update behavior. If we don't do this, the user can get an error - // when they remove themselves about permissions as the notification - // code tries to load what jist happened. - var threadManager = JX.ConpherenceThreadManager.getInstance(); - var loadedPhid = threadManager.getLoadedThreadPHID(); - threadManager.setLoadedThreadPHID(null); - - new JX.Workflow(href, data) - .setCloseHandler(function() { - threadManager.setLoadedThreadPHID(loadedPhid); - }) - // we re-direct to conpherence home so the thread manager will - // fix itself there - .setHandler(function(r) { - JX.$U(r.href).go(); - }) - .start(); - } - ); - - /* settings widget */ - var onsubmitSettings = function (e) { - e.kill(); - var form = e.getNode('tag:form'); - var button = JX.DOM.find(form, 'button'); - JX.Workflow.newFromForm(form) - .setHandler(JX.bind(this, function (r) { - new JX.Notification() - .setDuration(6000) - .setContent(r) - .show(); - button.disabled = ''; - JX.DOM.alterClass(button, 'disabled', false); - })) - .start(); - }; - - JX.Stratcom.listen( - ['submit', 'didSyntheticSubmit'], - 'notifications-update', - onsubmitSettings - ); - -}); diff --git a/webroot/rsrc/js/application/transactions/behavior-comment-actions.js b/webroot/rsrc/js/application/transactions/behavior-comment-actions.js index 87cb80c74a..a28a6b7ca2 100644 --- a/webroot/rsrc/js/application/transactions/behavior-comment-actions.js +++ b/webroot/rsrc/js/application/transactions/behavior-comment-actions.js @@ -176,17 +176,28 @@ JX.behavior('comment-actions', function(config) { JX.DOM.listen(form_node, 'shouldRefresh', null, always_trigger); request.start(); + var old_device = JX.Device.getDevice(); + var ondevicechange = function() { + var new_device = JX.Device.getDevice(); + var panel = JX.$(config.panelID); - if (JX.Device.getDevice() == 'desktop') { + if (new_device == 'desktop') { request.setRateLimit(500); - always_trigger(); + + // Force an immediate refresh if we switched from another device type + // to desktop. + if (old_device != new_device) { + always_trigger(); + } } else { // On mobile, don't show live previews and only save drafts every // 10 seconds. request.setRateLimit(10000); JX.DOM.hide(panel); } + + old_device = new_device; }; ondevicechange();