1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-27 17:22:42 +01:00

(stable) Promote 2016 Week 38

This commit is contained in:
epriestley 2016-09-17 14:09:22 -07:00
commit c8281bc339
206 changed files with 2774 additions and 2317 deletions

View file

@ -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',

View file

@ -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',

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

@ -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",

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_conpherence.conpherence_thread
ADD topic VARCHAR(255) NOT NULL COLLATE {$COLLATE_TEXT};

View file

@ -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',

View file

@ -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(

View file

@ -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(),

View file

@ -0,0 +1,36 @@
<?php
final class PhabricatorSlackAuthProvider
extends PhabricatorOAuth2AuthProvider {
public function getProviderName() {
return pht('Slack');
}
protected function getProviderConfigurationHelp() {
$login_uri = PhabricatorEnv::getURI($this->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';
}
}

View file

@ -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);
}

View file

@ -59,6 +59,8 @@ final class PhabricatorCalendarApplication extends PhabricatorApplication {
=> 'PhabricatorCalendarEventCancelController',
'(?P<action>join|decline|accept)/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarEventJoinController',
'export/(?P<id>[1-9]\d*)/(?P<filename>[^/]*)'
=> 'PhabricatorCalendarEventExportController',
),
),
);

View file

@ -0,0 +1,40 @@
<?php
final class PhabricatorCalendarEventExportController
extends PhabricatorCalendarController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->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);
}
}

View file

@ -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) {

View file

@ -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;
}
}

View file

@ -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 )--------------------------------------------------- */

View file

@ -20,6 +20,7 @@ final class CelerityHighContrastPostprocessor
'thinblueborder' => '#BFCFDA',
'lightblueborder' => '#8C98B8',
'blueborder' => '#626E82',
'timeline' => '#8C98B8',
'lightgreyborder' => '#555',
'greyborder' => '#333',

View file

@ -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();

View file

@ -44,7 +44,6 @@ final class PhabricatorConfigApplication extends PhabricatorApplication {
'edit/(?P<key>[\w\.\-]+)/' => 'PhabricatorConfigEditController',
'group/(?P<key>[^/]+)/' => 'PhabricatorConfigGroupController',
'version/' => 'PhabricatorConfigVersionController',
'welcome/' => 'PhabricatorConfigWelcomeController',
'database/'.
'(?:(?P<database>[^/]+)/'.
'(?:(?P<table>[^/]+)/'.

View file

@ -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__)

View file

@ -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) {

View file

@ -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;

View file

@ -1,411 +0,0 @@
<?php
final class PhabricatorConfigWelcomeController
extends PhabricatorConfigController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->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;
}
}

View file

@ -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() {

View file

@ -0,0 +1,41 @@
<?php
final class PhabricatorInFlightErrorView extends AphrontView {
private $message;
public function setMessage($message) {
$this->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())),
));
}
}

View file

@ -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();

View file

@ -42,7 +42,7 @@ final class PhabricatorConpherenceApplication extends PhabricatorApplication {
'search/(?:query/(?P<queryKey>[^/]+)/)?'
=> 'ConpherenceRoomListController',
'panel/' => 'ConpherenceNotificationPanelController',
'widget/(?P<id>[1-9]\d*)/' => 'ConpherenceWidgetController',
'participant/(?P<id>[1-9]\d*)/' => 'ConpherenceParticipantController',
'update/(?P<id>[1-9]\d*)/' => 'ConpherenceUpdateController',
),
);

View file

@ -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<phids>',
);
}
@ -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');

View file

@ -1,48 +0,0 @@
<?php
final class ConpherenceWidgetConfigConstants extends ConpherenceConstants {
const UPDATE_URI = '/conpherence/update/';
public static function getWidgetPaneBehaviorConfig() {
return array(
'widgetBaseUpdateURI' => 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,
),
);
}
}

View file

@ -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;
}
}

View file

@ -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')

View file

@ -0,0 +1,38 @@
<?php
final class ConpherenceParticipantController extends ConpherenceController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->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);
}
}

View file

@ -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();
}

View file

@ -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(),

View file

@ -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/'))

View file

@ -1,188 +0,0 @@
<?php
final class ConpherenceWidgetController extends ConpherenceController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$request = $this->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().'/');
}
}

View file

@ -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;
}

View file

@ -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,

View file

@ -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;
}

View file

@ -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(),

View file

@ -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.',

View file

@ -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() {

View file

@ -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;
}
}
}

View file

@ -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,
);
}

View file

@ -1,19 +1,34 @@
<?php
final class ConpherencePeopleWidgetView extends ConpherenceWidgetView {
final class ConpherenceParticipantView extends AphrontView {
private $conpherence;
private $updateURI;
public function setConpherence(ConpherenceThread $conpherence) {
$this->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;
}
}

View file

@ -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;
}

View file

@ -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:

View file

@ -1,24 +0,0 @@
<?php
abstract class ConpherenceWidgetView extends AphrontView {
private $conpherence;
private $updateURI;
public function setUpdateURI($update_uri) {
$this->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;
}
}

View file

@ -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();
}

View file

@ -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());

View file

@ -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

View file

@ -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

View file

@ -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) {

View file

@ -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);
}
}

View file

@ -113,10 +113,7 @@ final class DoorkeeperBridgeAsana extends DoorkeeperBridge {
}
$this->fillObjectFromData($obj, $result);
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$obj->save();
unset($unguarded);
$this->saveExternalObject($ref, $obj);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -104,10 +104,7 @@ final class DoorkeeperBridgeJIRA extends DoorkeeperBridge {
}
$this->fillObjectFromData($obj, $result);
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$obj->save();
unset($unguarded);
$this->saveExternalObject($ref, $obj);
}
}

View file

@ -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(

View file

@ -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;
}

View file

@ -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());
}

View file

@ -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.'/');

View file

@ -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);
}

View file

@ -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();
}
}

View file

@ -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);
}

View file

@ -1,34 +0,0 @@
<?php
final class PhabricatorGuideWelcomeModule extends PhabricatorGuideModule {
public function getModuleKey() {
return 'welcome';
}
public function getModuleName() {
return pht('Welcome');
}
public function getModulePosition() {
return 10;
}
public function renderModuleStatus(AphrontRequest $request) {
$viewer = $request->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);
}
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -209,7 +209,7 @@ final class PhabricatorHomeMainController extends PhabricatorHomeController {
$content = pht(<<<EOT
Welcome to Phabricator, here are some links to get you started:
- [[ /config/ | Configure Phabricator ]]
- [[ /config/welcome/ | Quick Start Guide ]]
- [[ /guides/ | Quick Start Guide ]]
- [[ /diffusion/ | Create a Repository ]]
- [[ /people/invite/send/ | Invite People ]]
- [[ https://twitter.com/phabricator/ | Follow us on Twitter ]]
@ -218,7 +218,7 @@ EOT
} else {
$content = pht(<<<EOT
Welcome to Phabricator, here are some links to get you started:
- [[ /config/welcome/ | Quick Start Guide ]]
- [[ /guides/ | Quick Start Guide ]]
- [[ /diffusion/ | Create a Repository ]]
- [[ https://twitter.com/phabricator/ | Follow us on Twitter ]]
EOT

View file

@ -27,6 +27,12 @@ final class PhabricatorApplicationDatasource
if (!$uri) {
continue;
}
$is_installed = PhabricatorApplication::isClassInstalledForViewer(
get_class($application),
$viewer);
if (!$is_installed) {
continue;
}
$name = $application->getName().' '.$application->getShortDescription();
$img = 'phui-font-fa phui-icon-view '.$application->getIcon();
$results[] = id(new PhabricatorTypeaheadResult())

View file

@ -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.

View file

@ -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);

View file

@ -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);

View file

@ -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) {

View file

@ -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(
'<p>%s</p>',
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'))

View file

@ -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(

View file

@ -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';

View file

@ -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'),
);
}

View file

@ -7,8 +7,9 @@ final class PhrictionDocument extends PhrictionDAO
PhabricatorFlaggableInterface,
PhabricatorTokenReceiverInterface,
PhabricatorDestructibleInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorFulltextInterface {
PhabricatorFulltextInterface,
PhabricatorProjectInterface,
PhabricatorApplicationTransactionInterface {
protected $slug;
protected $depth;

View file

@ -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);
}
}

View file

@ -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:

View file

@ -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,

View file

@ -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;

View file

@ -329,6 +329,11 @@ abstract class PhabricatorSearchField extends Phobject {
$this->getConduitKey());
}
public function getValidConstraintKeys() {
return $this->getConduitParameterType()->getKeys(
$this->getConduitKey());
}
/* -( Utility Methods )----------------------------------------------------- */

View file

@ -0,0 +1,12 @@
<?php
final class PhabricatorConpherenceWidgetVisibleSetting
extends PhabricatorInternalSetting {
const SETTINGKEY = 'conpherence-widget';
public function getSettingName() {
return pht('Conpherence Widget Pane Visible');
}
}

View file

@ -34,15 +34,15 @@ final class PhabricatorMonospacedFontSetting
throw new Exception(
pht(
'Monospaced font value "%s" is unsafe. You may only enter '.
'letters, numbers, spaces, commas, periods, forward slashes '.
'and double quotes.',
'letters, numbers, spaces, commas, periods, hyphens, '.
'forward slashes, and double quotes',
$value));
}
}
public static function filterMonospacedCSSRule($monospaced) {
// Prevent the user from doing dangerous things.
return preg_replace('([^a-z0-9 ,"./]+)i', '', $monospaced);
return preg_replace('([^a-z0-9 ,"./-]+)i', '', $monospaced);
}
}

View file

@ -31,7 +31,7 @@ bug, not a configuration problem.
However, some pages are slow because Phabricator legitimately needs to do a lot
of work to generate them. For example, if you write a 100MB wiki document,
Phabricator will need substantial time to process it, it will take a long time
to download over the network, and your browser will proably not be able to
to download over the network, and your browser will probably not be able to
render it especially quickly.
We may be able to improve perfomance in some cases, but Phabricator is not

View file

@ -143,7 +143,7 @@ Almanac namespaces allow you to control who can create services and devices
with certain names.
If you keep a list of cattle as devices with names like
`cow001.herd.myranch.com`, `cow002.herd.myranch.moo`, you might have some
`cow001.herd.myranch.moo`, `cow002.herd.myranch.moo`, you might have some
applications which query for all devices in `*.herd.myranch.moo`, and thus
want to limit who can create devices there in order to prevent mistakes.

View file

@ -688,6 +688,32 @@ In general:
- The `type` option can be set to `instructions` to indicate that an element
is asking the user to make a choice or follow specific instructions.
Keystrokes
==========
You can use `{key ...}` to render a stylized keystroke. For example, this:
```
Press {key M} to view the starmap.
```
...renders this:
> 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

View file

@ -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);

View file

@ -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];
}

View file

@ -505,6 +505,7 @@ final class PhabricatorMarkupEngine extends Phobject {
$rules[] = new PhutilRemarkupDocumentLinkRule();
$rules[] = new PhabricatorNavigationRemarkupRule();
$rules[] = new PhabricatorKeyboardRemarkupRule();
if ($options['youtube']) {
$rules[] = new PhabricatorYoutubeRemarkupRule();

View file

@ -0,0 +1,256 @@
<?php
final class PhabricatorKeyboardRemarkupRule extends PhutilRemarkupRule {
public function getPriority() {
return 200.0;
}
public function apply($text) {
return preg_replace_callback(
'@{key\b((?:[^}\\\\]+|\\\\.)*)}@m',
array($this, 'markupKeystrokes'),
$text);
}
public function markupKeystrokes(array $matches) {
if (!$this->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);
}
}

View file

@ -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.

View file

@ -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) {

View file

@ -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);

View file

@ -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);
}
}

View file

@ -0,0 +1,94 @@
<?php
/**
* API for replacing whitespace characters and some control characters with
* their printable representations. This is useful for debugging and
* displaying more helpful error messages to users.
*
*/
final class PHUIInvisibleCharacterView extends AphrontView {
private $inputText;
private $plainText = false;
// This is a list of the common invisible characters that are
// actually typeable. Other invisible characters will simply
// be displayed as their hex representations.
private static $invisibleChars = array(
"\x00" => '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();
}
}
}

View file

@ -0,0 +1,52 @@
<?php
final class PHUIInvisibleCharacterTestCase extends PhabricatorTestCase {
public function testEmptyString() {
$view = new PHUIInvisibleCharacterView('');
$res = $view->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, '<NULL><NEWLINE><TAB><SPACE>');
}
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, '<NULL><NEWLINE><TAB><SPACE>');
}
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);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

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