mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-24 15:52:41 +01:00
(stable) Promote 2016 Week 22
This commit is contained in:
commit
4c03f617fc
55 changed files with 1288 additions and 430 deletions
|
@ -7,11 +7,11 @@
|
|||
*/
|
||||
return array(
|
||||
'names' => array(
|
||||
'core.pkg.css' => '204cabae',
|
||||
'core.pkg.js' => '6972d365',
|
||||
'core.pkg.css' => '8aeacc63',
|
||||
'core.pkg.js' => '3f15fa62',
|
||||
'darkconsole.pkg.js' => 'e7393ebb',
|
||||
'differential.pkg.css' => '33da0633',
|
||||
'differential.pkg.js' => 'd0cd0df6',
|
||||
'differential.pkg.js' => '4b7d8f19',
|
||||
'diffusion.pkg.css' => '91c5d3a6',
|
||||
'diffusion.pkg.js' => '3a9a8bfa',
|
||||
'maniphest.pkg.css' => '4845691a',
|
||||
|
@ -116,7 +116,7 @@ return array(
|
|||
'rsrc/css/layout/phabricator-side-menu-view.css' => 'dd849797',
|
||||
'rsrc/css/layout/phabricator-source-code-view.css' => 'cbeef983',
|
||||
'rsrc/css/phui/calendar/phui-calendar-day.css' => 'd1cf6f93',
|
||||
'rsrc/css/phui/calendar/phui-calendar-list.css' => 'c1c7f338',
|
||||
'rsrc/css/phui/calendar/phui-calendar-list.css' => 'e0866209',
|
||||
'rsrc/css/phui/calendar/phui-calendar-month.css' => '476be7e0',
|
||||
'rsrc/css/phui/calendar/phui-calendar.css' => 'ccabe893',
|
||||
'rsrc/css/phui/phui-action-list.css' => 'c5eba19d',
|
||||
|
@ -126,7 +126,7 @@ return array(
|
|||
'rsrc/css/phui/phui-box.css' => '5c8387cf',
|
||||
'rsrc/css/phui/phui-button.css' => 'a64a8de6',
|
||||
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
|
||||
'rsrc/css/phui/phui-crumbs-view.css' => '1a1265d4',
|
||||
'rsrc/css/phui/phui-crumbs-view.css' => '6b813619',
|
||||
'rsrc/css/phui/phui-curtain-view.css' => '7148ae25',
|
||||
'rsrc/css/phui/phui-document-pro.css' => '8419560b',
|
||||
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
|
||||
|
@ -245,7 +245,7 @@ return array(
|
|||
'rsrc/externals/javelin/lib/URI.js' => 'c989ade3',
|
||||
'rsrc/externals/javelin/lib/Vector.js' => '2caa8fb8',
|
||||
'rsrc/externals/javelin/lib/WebSocket.js' => 'e292eaf4',
|
||||
'rsrc/externals/javelin/lib/Workflow.js' => '28cfbdd0',
|
||||
'rsrc/externals/javelin/lib/Workflow.js' => '0eb34d1d',
|
||||
'rsrc/externals/javelin/lib/__tests__/Cookie.js' => '5ed109e8',
|
||||
'rsrc/externals/javelin/lib/__tests__/DOM.js' => 'c984504b',
|
||||
'rsrc/externals/javelin/lib/__tests__/JSON.js' => '837a7d68',
|
||||
|
@ -367,7 +367,7 @@ return array(
|
|||
'rsrc/js/application/config/behavior-reorder-fields.js' => 'b6993408',
|
||||
'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' => 'c72aa091',
|
||||
'rsrc/js/application/conpherence/behavior-durable-column.js' => 'd3506890',
|
||||
'rsrc/js/application/conpherence/behavior-menu.js' => '1d45c74d',
|
||||
'rsrc/js/application/conpherence/behavior-pontificate.js' => '21ba5861',
|
||||
'rsrc/js/application/conpherence/behavior-quicksand-blacklist.js' => '7927a7d3',
|
||||
|
@ -457,7 +457,7 @@ return array(
|
|||
'rsrc/js/application/uiexample/gesture-example.js' => '558829c2',
|
||||
'rsrc/js/application/uiexample/notification-example.js' => '8ce821c5',
|
||||
'rsrc/js/core/Busy.js' => '59a7976a',
|
||||
'rsrc/js/core/DragAndDropFileUpload.js' => '81f182b5',
|
||||
'rsrc/js/core/DragAndDropFileUpload.js' => '58dea2fa',
|
||||
'rsrc/js/core/DraggableList.js' => '5a13c79f',
|
||||
'rsrc/js/core/FileUpload.js' => '680ea2c8',
|
||||
'rsrc/js/core/Hovercard.js' => '1bd28176',
|
||||
|
@ -467,7 +467,7 @@ return array(
|
|||
'rsrc/js/core/Notification.js' => 'ccf1cbf8',
|
||||
'rsrc/js/core/Prefab.js' => 'e67df814',
|
||||
'rsrc/js/core/ShapedRequest.js' => '7cbe244b',
|
||||
'rsrc/js/core/TextAreaUtils.js' => '5813016a',
|
||||
'rsrc/js/core/TextAreaUtils.js' => '320810c8',
|
||||
'rsrc/js/core/Title.js' => 'df5e11d2',
|
||||
'rsrc/js/core/ToolTip.js' => '6323f942',
|
||||
'rsrc/js/core/behavior-active-nav.js' => 'e379b58e',
|
||||
|
@ -477,8 +477,9 @@ return array(
|
|||
'rsrc/js/core/behavior-choose-control.js' => '327a00d1',
|
||||
'rsrc/js/core/behavior-crop.js' => 'fa0f4fc2',
|
||||
'rsrc/js/core/behavior-dark-console.js' => 'f411b6ae',
|
||||
'rsrc/js/core/behavior-device.js' => 'b5b36110',
|
||||
'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '4f6a4b4e',
|
||||
'rsrc/js/core/behavior-detect-timezone.js' => '4c193c96',
|
||||
'rsrc/js/core/behavior-device.js' => 'bb1dd507',
|
||||
'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '484a6e22',
|
||||
'rsrc/js/core/behavior-error-log.js' => '6882e80a',
|
||||
'rsrc/js/core/behavior-fancy-datepicker.js' => '568931f3',
|
||||
'rsrc/js/core/behavior-file-tree.js' => '88236f00',
|
||||
|
@ -489,14 +490,14 @@ return array(
|
|||
'rsrc/js/core/behavior-history-install.js' => '7ee2b591',
|
||||
'rsrc/js/core/behavior-hovercard.js' => 'bcaccd64',
|
||||
'rsrc/js/core/behavior-keyboard-pager.js' => 'a8da01f0',
|
||||
'rsrc/js/core/behavior-keyboard-shortcuts.js' => 'd75709e6',
|
||||
'rsrc/js/core/behavior-keyboard-shortcuts.js' => '7835f8c9',
|
||||
'rsrc/js/core/behavior-lightbox-attachments.js' => 'f8ba29d7',
|
||||
'rsrc/js/core/behavior-line-linker.js' => '1499a8cb',
|
||||
'rsrc/js/core/behavior-more.js' => 'a80d0378',
|
||||
'rsrc/js/core/behavior-object-selector.js' => '49b73b36',
|
||||
'rsrc/js/core/behavior-oncopy.js' => '2926fff2',
|
||||
'rsrc/js/core/behavior-phabricator-nav.js' => '56a1ca03',
|
||||
'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '340c8eff',
|
||||
'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '116cf19b',
|
||||
'rsrc/js/core/behavior-read-only-warning.js' => 'ba158207',
|
||||
'rsrc/js/core/behavior-refresh-csrf.js' => 'ab2f381b',
|
||||
'rsrc/js/core/behavior-remarkup-preview.js' => '4b700e9e',
|
||||
|
@ -514,6 +515,7 @@ return array(
|
|||
'rsrc/js/core/behavior-workflow.js' => '0a3f3021',
|
||||
'rsrc/js/core/phtize.js' => 'd254d646',
|
||||
'rsrc/js/phui/behavior-phui-dropdown-menu.js' => '54733475',
|
||||
'rsrc/js/phui/behavior-phui-file-upload.js' => 'b003d4fb',
|
||||
'rsrc/js/phui/behavior-phui-object-box-tabs.js' => '2bfa2836',
|
||||
'rsrc/js/phui/behavior-phui-profile-menu.js' => '12884df9',
|
||||
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
|
||||
|
@ -578,7 +580,7 @@ return array(
|
|||
'javelin-behavior-aphlict-status' => 'ea681761',
|
||||
'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884',
|
||||
'javelin-behavior-aphront-crop' => 'fa0f4fc2',
|
||||
'javelin-behavior-aphront-drag-and-drop-textarea' => '4f6a4b4e',
|
||||
'javelin-behavior-aphront-drag-and-drop-textarea' => '484a6e22',
|
||||
'javelin-behavior-aphront-form-disable-on-submit' => '5c54cbf3',
|
||||
'javelin-behavior-aphront-more' => 'a80d0378',
|
||||
'javelin-behavior-audio-source' => '59b251eb',
|
||||
|
@ -600,7 +602,8 @@ return array(
|
|||
'javelin-behavior-dashboard-tab-panel' => 'd4eecc63',
|
||||
'javelin-behavior-day-view' => '5c46cff2',
|
||||
'javelin-behavior-desktop-notifications-control' => 'edd1ba66',
|
||||
'javelin-behavior-device' => 'b5b36110',
|
||||
'javelin-behavior-detect-timezone' => '4c193c96',
|
||||
'javelin-behavior-device' => 'bb1dd507',
|
||||
'javelin-behavior-differential-add-reviewers-and-ccs' => 'e10f8e18',
|
||||
'javelin-behavior-differential-comment-jump' => '4fdb476d',
|
||||
'javelin-behavior-differential-diff-radios' => 'e1ff79b1',
|
||||
|
@ -619,7 +622,7 @@ return array(
|
|||
'javelin-behavior-diffusion-pull-lastmodified' => 'f01586dc',
|
||||
'javelin-behavior-doorkeeper-tag' => 'e5822781',
|
||||
'javelin-behavior-drydock-live-operation-status' => '901935ef',
|
||||
'javelin-behavior-durable-column' => 'c72aa091',
|
||||
'javelin-behavior-durable-column' => 'd3506890',
|
||||
'javelin-behavior-editengine-reorder-configs' => 'd7a74243',
|
||||
'javelin-behavior-editengine-reorder-fields' => 'b59e1e96',
|
||||
'javelin-behavior-error-log' => '6882e80a',
|
||||
|
@ -648,13 +651,13 @@ return array(
|
|||
'javelin-behavior-phabricator-gesture' => '3ab51e2c',
|
||||
'javelin-behavior-phabricator-gesture-example' => '558829c2',
|
||||
'javelin-behavior-phabricator-keyboard-pager' => 'a8da01f0',
|
||||
'javelin-behavior-phabricator-keyboard-shortcuts' => 'd75709e6',
|
||||
'javelin-behavior-phabricator-keyboard-shortcuts' => '7835f8c9',
|
||||
'javelin-behavior-phabricator-line-linker' => '1499a8cb',
|
||||
'javelin-behavior-phabricator-nav' => '56a1ca03',
|
||||
'javelin-behavior-phabricator-notification-example' => '8ce821c5',
|
||||
'javelin-behavior-phabricator-object-selector' => '49b73b36',
|
||||
'javelin-behavior-phabricator-oncopy' => '2926fff2',
|
||||
'javelin-behavior-phabricator-remarkup-assist' => '340c8eff',
|
||||
'javelin-behavior-phabricator-remarkup-assist' => '116cf19b',
|
||||
'javelin-behavior-phabricator-reveal-content' => '60821bc7',
|
||||
'javelin-behavior-phabricator-search-typeahead' => '06c32383',
|
||||
'javelin-behavior-phabricator-show-older-transactions' => 'dbbf48b6',
|
||||
|
@ -665,6 +668,7 @@ return array(
|
|||
'javelin-behavior-pholio-mock-edit' => '246dc085',
|
||||
'javelin-behavior-pholio-mock-view' => 'fbe497e7',
|
||||
'javelin-behavior-phui-dropdown-menu' => '54733475',
|
||||
'javelin-behavior-phui-file-upload' => 'b003d4fb',
|
||||
'javelin-behavior-phui-hovercards' => 'bcaccd64',
|
||||
'javelin-behavior-phui-object-box-tabs' => '2bfa2836',
|
||||
'javelin-behavior-phui-profile-menu' => '12884df9',
|
||||
|
@ -743,7 +747,7 @@ return array(
|
|||
'javelin-workboard-card' => 'c587b80f',
|
||||
'javelin-workboard-column' => 'bae58312',
|
||||
'javelin-workboard-controller' => '55baf5ed',
|
||||
'javelin-workflow' => '28cfbdd0',
|
||||
'javelin-workflow' => '0eb34d1d',
|
||||
'lightbox-attachment-css' => '7acac05d',
|
||||
'maniphest-batch-editor' => 'b0f0b6d5',
|
||||
'maniphest-report-css' => '9b9580b7',
|
||||
|
@ -763,7 +767,7 @@ return array(
|
|||
'phabricator-core-css' => 'd0801452',
|
||||
'phabricator-countdown-css' => '16c52f5c',
|
||||
'phabricator-dashboard-css' => 'bc6f2127',
|
||||
'phabricator-drag-and-drop-file-upload' => '81f182b5',
|
||||
'phabricator-drag-and-drop-file-upload' => '58dea2fa',
|
||||
'phabricator-draggable-list' => '5a13c79f',
|
||||
'phabricator-fatal-config-template-css' => '8e6c6fcd',
|
||||
'phabricator-feed-css' => 'ecd4ec57',
|
||||
|
@ -787,7 +791,7 @@ return array(
|
|||
'phabricator-slowvote-css' => 'a94b7230',
|
||||
'phabricator-source-code-view-css' => 'cbeef983',
|
||||
'phabricator-standard-page-view' => 'e709f6d0',
|
||||
'phabricator-textareautils' => '5813016a',
|
||||
'phabricator-textareautils' => '320810c8',
|
||||
'phabricator-title' => 'df5e11d2',
|
||||
'phabricator-tooltip' => '6323f942',
|
||||
'phabricator-ui-example-css' => '528b19de',
|
||||
|
@ -818,10 +822,10 @@ return array(
|
|||
'phui-button-css' => 'a64a8de6',
|
||||
'phui-calendar-css' => 'ccabe893',
|
||||
'phui-calendar-day-css' => 'd1cf6f93',
|
||||
'phui-calendar-list-css' => 'c1c7f338',
|
||||
'phui-calendar-list-css' => 'e0866209',
|
||||
'phui-calendar-month-css' => '476be7e0',
|
||||
'phui-chart-css' => '6bf6f78e',
|
||||
'phui-crumbs-view-css' => '1a1265d4',
|
||||
'phui-crumbs-view-css' => '6b813619',
|
||||
'phui-curtain-view-css' => '7148ae25',
|
||||
'phui-document-summary-view-css' => '9ca48bdf',
|
||||
'phui-document-view-css' => '715aedfb',
|
||||
|
@ -975,10 +979,31 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-router',
|
||||
),
|
||||
'0eb34d1d' => array(
|
||||
'javelin-stratcom',
|
||||
'javelin-request',
|
||||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-mask',
|
||||
'javelin-uri',
|
||||
'javelin-routable',
|
||||
),
|
||||
'0f764c35' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
),
|
||||
'116cf19b' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'phabricator-phtize',
|
||||
'phabricator-textareautils',
|
||||
'javelin-workflow',
|
||||
'javelin-vector',
|
||||
'phuix-autocomplete',
|
||||
),
|
||||
'12884df9' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -1075,17 +1100,6 @@ return array(
|
|||
'phabricator-drag-and-drop-file-upload',
|
||||
'phabricator-draggable-list',
|
||||
),
|
||||
'28cfbdd0' => array(
|
||||
'javelin-stratcom',
|
||||
'javelin-request',
|
||||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-mask',
|
||||
'javelin-uri',
|
||||
'javelin-routable',
|
||||
),
|
||||
'2926fff2' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
@ -1116,22 +1130,17 @@ return array(
|
|||
'2ee659ce' => array(
|
||||
'javelin-install',
|
||||
),
|
||||
'320810c8' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
),
|
||||
'327a00d1' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'javelin-workflow',
|
||||
),
|
||||
'340c8eff' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'phabricator-phtize',
|
||||
'phabricator-textareautils',
|
||||
'javelin-workflow',
|
||||
'javelin-vector',
|
||||
'phuix-autocomplete',
|
||||
),
|
||||
'3ab51e2c' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-behavior-device',
|
||||
|
@ -1199,6 +1208,12 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-workflow',
|
||||
),
|
||||
'484a6e22' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'phabricator-drag-and-drop-file-upload',
|
||||
'phabricator-textareautils',
|
||||
),
|
||||
'49b73b36' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
@ -1211,17 +1226,16 @@ return array(
|
|||
'javelin-util',
|
||||
'phabricator-shaped-request',
|
||||
),
|
||||
'4c193c96' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-uri',
|
||||
'phabricator-notification',
|
||||
),
|
||||
'4e3e79a6' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
),
|
||||
'4f6a4b4e' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'phabricator-drag-and-drop-file-upload',
|
||||
'phabricator-textareautils',
|
||||
),
|
||||
'4fbbc3e9' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -1319,10 +1333,13 @@ return array(
|
|||
'javelin-request',
|
||||
'javelin-util',
|
||||
),
|
||||
'5813016a' => array(
|
||||
'58dea2fa' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-request',
|
||||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
'javelin-uri',
|
||||
'phabricator-file-upload',
|
||||
),
|
||||
'59a7976a' => array(
|
||||
'javelin-install',
|
||||
|
@ -1475,6 +1492,13 @@ return array(
|
|||
'multirow-row-manager',
|
||||
'javelin-json',
|
||||
),
|
||||
'7835f8c9' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-workflow',
|
||||
'javelin-json',
|
||||
'javelin-dom',
|
||||
'phabricator-keyboard-shortcut',
|
||||
),
|
||||
'7927a7d3' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-quicksand',
|
||||
|
@ -1516,14 +1540,6 @@ return array(
|
|||
'javelin-vector',
|
||||
'javelin-stratcom',
|
||||
),
|
||||
'81f182b5' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-request',
|
||||
'javelin-dom',
|
||||
'javelin-uri',
|
||||
'phabricator-file-upload',
|
||||
),
|
||||
'834a1173' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-scrollbar',
|
||||
|
@ -1740,6 +1756,12 @@ return array(
|
|||
'javelin-util',
|
||||
'phabricator-busy',
|
||||
),
|
||||
'b003d4fb' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'phuix-dropdown-menu',
|
||||
),
|
||||
'b064af76' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -1786,13 +1808,6 @@ return array(
|
|||
'javelin-dom',
|
||||
'phabricator-draggable-list',
|
||||
),
|
||||
'b5b36110' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
'javelin-install',
|
||||
),
|
||||
'b5c256b8' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
@ -1819,6 +1834,13 @@ return array(
|
|||
'javelin-install',
|
||||
'javelin-workboard-card',
|
||||
),
|
||||
'bb1dd507' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
'javelin-install',
|
||||
),
|
||||
'bcaccd64' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-behavior-device',
|
||||
|
@ -1858,16 +1880,6 @@ return array(
|
|||
'c587b80f' => array(
|
||||
'javelin-install',
|
||||
),
|
||||
'c72aa091' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
'javelin-behavior-device',
|
||||
'javelin-scrollbar',
|
||||
'javelin-quicksand',
|
||||
'phabricator-keyboard-shortcut',
|
||||
'conpherence-thread-manager',
|
||||
),
|
||||
'c7ccd872' => array(
|
||||
'phui-fontkit-css',
|
||||
),
|
||||
|
@ -1933,6 +1945,16 @@ return array(
|
|||
'd254d646' => array(
|
||||
'javelin-util',
|
||||
),
|
||||
'd3506890' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
'javelin-behavior-device',
|
||||
'javelin-scrollbar',
|
||||
'javelin-quicksand',
|
||||
'phabricator-keyboard-shortcut',
|
||||
'conpherence-thread-manager',
|
||||
),
|
||||
'd4505101' => array(
|
||||
'javelin-stratcom',
|
||||
'javelin-install',
|
||||
|
@ -1958,13 +1980,6 @@ return array(
|
|||
'javelin-json',
|
||||
'phabricator-prefab',
|
||||
),
|
||||
'd75709e6' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-workflow',
|
||||
'javelin-json',
|
||||
'javelin-dom',
|
||||
'phabricator-keyboard-shortcut',
|
||||
),
|
||||
'd7a74243' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -2324,6 +2339,7 @@ return array(
|
|||
'javelin-behavior-scrollbar',
|
||||
'javelin-behavior-durable-column',
|
||||
'conpherence-thread-manager',
|
||||
'javelin-behavior-detect-timezone',
|
||||
),
|
||||
'darkconsole.pkg.js' => array(
|
||||
'javelin-behavior-dark-console',
|
||||
|
|
|
@ -81,6 +81,7 @@ return array(
|
|||
'javelin-behavior-scrollbar',
|
||||
'javelin-behavior-durable-column',
|
||||
'conpherence-thread-manager',
|
||||
'javelin-behavior-detect-timezone',
|
||||
),
|
||||
'core.pkg.css' => array(
|
||||
'phabricator-core-css',
|
||||
|
|
|
@ -1569,6 +1569,7 @@ phutil_register_library_map(array(
|
|||
'PHUICalendarDayView' => 'view/phui/calendar/PHUICalendarDayView.php',
|
||||
'PHUICalendarListView' => 'view/phui/calendar/PHUICalendarListView.php',
|
||||
'PHUICalendarMonthView' => 'view/phui/calendar/PHUICalendarMonthView.php',
|
||||
'PHUICalendarWeekView' => 'view/phui/calendar/PHUICalendarWeekView.php',
|
||||
'PHUICalendarWidgetView' => 'view/phui/calendar/PHUICalendarWidgetView.php',
|
||||
'PHUIColorPalletteExample' => 'applications/uiexample/examples/PHUIColorPalletteExample.php',
|
||||
'PHUICrumbView' => 'view/phui/PHUICrumbView.php',
|
||||
|
@ -1594,6 +1595,7 @@ phutil_register_library_map(array(
|
|||
'PHUIFeedStoryExample' => 'applications/uiexample/examples/PHUIFeedStoryExample.php',
|
||||
'PHUIFeedStoryView' => 'view/phui/PHUIFeedStoryView.php',
|
||||
'PHUIFormDividerControl' => 'view/form/control/PHUIFormDividerControl.php',
|
||||
'PHUIFormFileControl' => 'view/form/control/PHUIFormFileControl.php',
|
||||
'PHUIFormFreeformDateControl' => 'view/form/control/PHUIFormFreeformDateControl.php',
|
||||
'PHUIFormIconSetControl' => 'view/form/control/PHUIFormIconSetControl.php',
|
||||
'PHUIFormInsetView' => 'view/form/PHUIFormInsetView.php',
|
||||
|
@ -2381,6 +2383,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEmailVerificationController' => 'applications/auth/controller/PhabricatorEmailVerificationController.php',
|
||||
'PhabricatorEmbedFileRemarkupRule' => 'applications/files/markup/PhabricatorEmbedFileRemarkupRule.php',
|
||||
'PhabricatorEmojiRemarkupRule' => 'applications/macro/markup/PhabricatorEmojiRemarkupRule.php',
|
||||
'PhabricatorEmojiTranslation' => 'infrastructure/internationalization/translation/PhabricatorEmojiTranslation.php',
|
||||
'PhabricatorEmptyQueryException' => 'infrastructure/query/PhabricatorEmptyQueryException.php',
|
||||
'PhabricatorEnv' => 'infrastructure/env/PhabricatorEnv.php',
|
||||
'PhabricatorEnvTestCase' => 'infrastructure/env/__tests__/PhabricatorEnvTestCase.php',
|
||||
|
@ -3329,7 +3332,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSearchNgramsDestructionEngineExtension' => 'applications/search/engineextension/PhabricatorSearchNgramsDestructionEngineExtension.php',
|
||||
'PhabricatorSearchOrderController' => 'applications/search/controller/PhabricatorSearchOrderController.php',
|
||||
'PhabricatorSearchOrderField' => 'applications/search/field/PhabricatorSearchOrderField.php',
|
||||
'PhabricatorSearchPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorSearchPreferencesSettingsPanel.php',
|
||||
'PhabricatorSearchRelationship' => 'applications/search/constants/PhabricatorSearchRelationship.php',
|
||||
'PhabricatorSearchResultBucket' => 'applications/search/buckets/PhabricatorSearchResultBucket.php',
|
||||
'PhabricatorSearchResultBucketGroup' => 'applications/search/buckets/PhabricatorSearchResultBucketGroup.php',
|
||||
|
@ -3354,6 +3356,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSettingsMainController' => 'applications/settings/controller/PhabricatorSettingsMainController.php',
|
||||
'PhabricatorSettingsMainMenuBarExtension' => 'applications/settings/extension/PhabricatorSettingsMainMenuBarExtension.php',
|
||||
'PhabricatorSettingsPanel' => 'applications/settings/panel/PhabricatorSettingsPanel.php',
|
||||
'PhabricatorSettingsTimezoneController' => 'applications/settings/controller/PhabricatorSettingsTimezoneController.php',
|
||||
'PhabricatorSetupCheck' => 'applications/config/check/PhabricatorSetupCheck.php',
|
||||
'PhabricatorSetupCheckTestCase' => 'applications/config/check/__tests__/PhabricatorSetupCheckTestCase.php',
|
||||
'PhabricatorSetupIssue' => 'applications/config/issue/PhabricatorSetupIssue.php',
|
||||
|
@ -4133,6 +4136,7 @@ phutil_register_library_map(array(
|
|||
'UserEnableConduitAPIMethod' => 'applications/people/conduit/UserEnableConduitAPIMethod.php',
|
||||
'UserFindConduitAPIMethod' => 'applications/people/conduit/UserFindConduitAPIMethod.php',
|
||||
'UserQueryConduitAPIMethod' => 'applications/people/conduit/UserQueryConduitAPIMethod.php',
|
||||
'UserSearchConduitAPIMethod' => 'applications/people/conduit/UserSearchConduitAPIMethod.php',
|
||||
'UserWhoAmIConduitAPIMethod' => 'applications/people/conduit/UserWhoAmIConduitAPIMethod.php',
|
||||
),
|
||||
'function' => array(
|
||||
|
@ -5971,6 +5975,7 @@ phutil_register_library_map(array(
|
|||
'PHUICalendarDayView' => 'AphrontView',
|
||||
'PHUICalendarListView' => 'AphrontTagView',
|
||||
'PHUICalendarMonthView' => 'AphrontView',
|
||||
'PHUICalendarWeekView' => 'AphrontView',
|
||||
'PHUICalendarWidgetView' => 'AphrontTagView',
|
||||
'PHUIColorPalletteExample' => 'PhabricatorUIExample',
|
||||
'PHUICrumbView' => 'AphrontView',
|
||||
|
@ -5996,6 +6001,7 @@ phutil_register_library_map(array(
|
|||
'PHUIFeedStoryExample' => 'PhabricatorUIExample',
|
||||
'PHUIFeedStoryView' => 'AphrontView',
|
||||
'PHUIFormDividerControl' => 'AphrontFormControl',
|
||||
'PHUIFormFileControl' => 'AphrontFormControl',
|
||||
'PHUIFormFreeformDateControl' => 'AphrontFormControl',
|
||||
'PHUIFormIconSetControl' => 'AphrontFormControl',
|
||||
'PHUIFormInsetView' => 'AphrontView',
|
||||
|
@ -6907,6 +6913,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEmailVerificationController' => 'PhabricatorAuthController',
|
||||
'PhabricatorEmbedFileRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
||||
'PhabricatorEmojiRemarkupRule' => 'PhutilRemarkupRule',
|
||||
'PhabricatorEmojiTranslation' => 'PhutilTranslation',
|
||||
'PhabricatorEmptyQueryException' => 'Exception',
|
||||
'PhabricatorEnv' => 'Phobject',
|
||||
'PhabricatorEnvTestCase' => 'PhabricatorTestCase',
|
||||
|
@ -8038,7 +8045,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSearchNgramsDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
|
||||
'PhabricatorSearchOrderController' => 'PhabricatorSearchBaseController',
|
||||
'PhabricatorSearchOrderField' => 'PhabricatorSearchField',
|
||||
'PhabricatorSearchPreferencesSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||
'PhabricatorSearchRelationship' => 'Phobject',
|
||||
'PhabricatorSearchResultBucket' => 'Phobject',
|
||||
'PhabricatorSearchResultBucketGroup' => 'Phobject',
|
||||
|
@ -8063,6 +8069,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSettingsMainController' => 'PhabricatorController',
|
||||
'PhabricatorSettingsMainMenuBarExtension' => 'PhabricatorMainMenuBarExtension',
|
||||
'PhabricatorSettingsPanel' => 'Phobject',
|
||||
'PhabricatorSettingsTimezoneController' => 'PhabricatorController',
|
||||
'PhabricatorSetupCheck' => 'Phobject',
|
||||
'PhabricatorSetupCheckTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorSetupIssue' => 'Phobject',
|
||||
|
@ -8309,6 +8316,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFlaggableInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorFulltextInterface',
|
||||
'PhabricatorConduitResultInterface',
|
||||
),
|
||||
'PhabricatorUserBlurbField' => 'PhabricatorUserCustomField',
|
||||
'PhabricatorUserCardView' => 'AphrontTagView',
|
||||
|
@ -9024,6 +9032,7 @@ phutil_register_library_map(array(
|
|||
'UserEnableConduitAPIMethod' => 'UserConduitAPIMethod',
|
||||
'UserFindConduitAPIMethod' => 'UserConduitAPIMethod',
|
||||
'UserQueryConduitAPIMethod' => 'UserConduitAPIMethod',
|
||||
'UserSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
||||
'UserWhoAmIConduitAPIMethod' => 'UserConduitAPIMethod',
|
||||
),
|
||||
));
|
||||
|
|
|
@ -642,6 +642,12 @@ final class PhabricatorAuditEditor
|
|||
|
||||
$status_resigned = PhabricatorAuditStatusConstants::RESIGNED;
|
||||
foreach ($object->getAudits() as $audit) {
|
||||
if (!$audit->isInteresting()) {
|
||||
// Don't send mail to uninteresting auditors, like packages which
|
||||
// own this code but which audits have not triggered for.
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($audit->getAuditStatus() != $status_resigned) {
|
||||
$phids[] = $audit->getAuditorPHID();
|
||||
}
|
||||
|
|
|
@ -4,16 +4,14 @@ abstract class PhabricatorConfigDatabaseController
|
|||
extends PhabricatorConfigController {
|
||||
|
||||
protected function buildSchemaQuery() {
|
||||
$conf = PhabricatorEnv::newObjectFromConfig(
|
||||
'mysql.configuration-provider',
|
||||
array($dao = null, 'w'));
|
||||
$ref = PhabricatorDatabaseRef::getMasterDatabaseRef();
|
||||
|
||||
$api = id(new PhabricatorStorageManagementAPI())
|
||||
->setUser($conf->getUser())
|
||||
->setHost($conf->getHost())
|
||||
->setPort($conf->getPort())
|
||||
->setUser($ref->getUser())
|
||||
->setHost($ref->getHost())
|
||||
->setPort($ref->getPort())
|
||||
->setNamespace(PhabricatorLiskDAO::getDefaultStorageNamespace())
|
||||
->setPassword($conf->getPassword());
|
||||
->setPassword($ref->getPass());
|
||||
|
||||
$query = id(new PhabricatorConfigSchemaQuery())
|
||||
->setAPI($api);
|
||||
|
|
|
@ -56,7 +56,7 @@ final class PhabricatorFileDropUploadController
|
|||
$file_phid = $result['filePHID'];
|
||||
if ($file_phid) {
|
||||
$file = $this->loadFile($file_phid);
|
||||
$result += $this->getFileDictionary($file);
|
||||
$result += $file->getDragAndDropDictionary();
|
||||
}
|
||||
|
||||
return id(new AphrontAjaxResponse())->setContent($result);
|
||||
|
@ -84,7 +84,7 @@ final class PhabricatorFileDropUploadController
|
|||
} else {
|
||||
$result = array(
|
||||
'complete' => true,
|
||||
) + $this->getFileDictionary($file);
|
||||
) + $file->getDragAndDropDictionary();
|
||||
}
|
||||
|
||||
return id(new AphrontAjaxResponse())->setContent($result);
|
||||
|
@ -99,18 +99,10 @@ final class PhabricatorFileDropUploadController
|
|||
'isExplicitUpload' => true,
|
||||
));
|
||||
|
||||
$result = $this->getFileDictionary($file);
|
||||
$result = $file->getDragAndDropDictionary();
|
||||
return id(new AphrontAjaxResponse())->setContent($result);
|
||||
}
|
||||
|
||||
private function getFileDictionary(PhabricatorFile $file) {
|
||||
return array(
|
||||
'id' => $file->getID(),
|
||||
'phid' => $file->getPHID(),
|
||||
'uri' => $file->getBestURI(),
|
||||
);
|
||||
}
|
||||
|
||||
private function loadFile($file_phid) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
|
|
|
@ -6,12 +6,51 @@ final class PhabricatorFileUploadDialogController
|
|||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Upload File'))
|
||||
->appendChild(pht(
|
||||
'To add files, drag and drop them into the comment text area.'))
|
||||
->addCancelButton('/', pht('Close'));
|
||||
$e_file = true;
|
||||
$errors = array();
|
||||
if ($request->isDialogFormPost()) {
|
||||
$file_phids = $request->getStrList('filePHIDs');
|
||||
if ($file_phids) {
|
||||
$files = id(new PhabricatorFileQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs($file_phids)
|
||||
->setRaisePolicyExceptions(true)
|
||||
->execute();
|
||||
} else {
|
||||
$files = array();
|
||||
}
|
||||
|
||||
if ($files) {
|
||||
$results = array();
|
||||
foreach ($files as $file) {
|
||||
$results[] = $file->getDragAndDropDictionary();
|
||||
}
|
||||
|
||||
$content = array(
|
||||
'files' => $results,
|
||||
);
|
||||
|
||||
return id(new AphrontAjaxResponse())->setContent($content);
|
||||
} else {
|
||||
$e_file = pht('Required');
|
||||
$errors[] = pht('You must choose a file to upload.');
|
||||
}
|
||||
}
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->appendChild(
|
||||
id(new PHUIFormFileControl())
|
||||
->setName('filePHIDs')
|
||||
->setLabel(pht('Upload File'))
|
||||
->setAllowMultiple(true)
|
||||
->setError($e_file));
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('File'))
|
||||
->setErrors($errors)
|
||||
->appendForm($form)
|
||||
->addSubmitButton(pht('Upload'))
|
||||
->addCancelButton('/');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -851,6 +851,14 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
return $supported;
|
||||
}
|
||||
|
||||
public function getDragAndDropDictionary() {
|
||||
return array(
|
||||
'id' => $this->getID(),
|
||||
'phid' => $this->getPHID(),
|
||||
'uri' => $this->getBestURI(),
|
||||
);
|
||||
}
|
||||
|
||||
public function instantiateStorageEngine() {
|
||||
return self::buildEngine($this->getStorageEngine());
|
||||
}
|
||||
|
|
|
@ -45,25 +45,37 @@ final class PhabricatorOwnersOwner extends PhabricatorOwnersDAO {
|
|||
'packageID IN (%Ls)',
|
||||
$package_ids);
|
||||
|
||||
$all_phids = phid_group_by_type(mpull($owners, 'getUserPHID'));
|
||||
$type_user = PhabricatorPeopleUserPHIDType::TYPECONST;
|
||||
$type_project = PhabricatorProjectProjectPHIDType::TYPECONST;
|
||||
|
||||
$user_phids = idx($all_phids,
|
||||
PhabricatorPeopleUserPHIDType::TYPECONST,
|
||||
array());
|
||||
|
||||
if ($user_phids) {
|
||||
$projects = id(new PhabricatorProjectQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withMemberPHIDs($user_phids)
|
||||
->withIsMilestone(false)
|
||||
->execute();
|
||||
$project_phids = mpull($projects, 'getPHID');
|
||||
} else {
|
||||
$project_phids = array();
|
||||
$user_phids = array();
|
||||
$project_phids = array();
|
||||
foreach ($owners as $owner) {
|
||||
$owner_phid = $owner->getUserPHID();
|
||||
switch (phid_get_type($owner_phid)) {
|
||||
case PhabricatorPeopleUserPHIDType::TYPECONST:
|
||||
$user_phids[] = $owner_phid;
|
||||
break;
|
||||
case PhabricatorProjectProjectPHIDType::TYPECONST:
|
||||
$project_phids[] = $owner_phid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$all_phids = array_fuse($user_phids) + array_fuse($project_phids);
|
||||
if ($project_phids) {
|
||||
$projects = id(new PhabricatorProjectQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withPHIDs($project_phids)
|
||||
->needMembers(true)
|
||||
->execute();
|
||||
foreach ($projects as $project) {
|
||||
foreach ($project->getMemberPHIDs() as $member_phid) {
|
||||
$user_phids[] = $member_phid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($all_phids);
|
||||
$user_phids = array_fuse($user_phids);
|
||||
return array_values($user_phids);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
final class UserSearchConduitAPIMethod
|
||||
extends PhabricatorSearchEngineAPIMethod {
|
||||
|
||||
public function getAPIMethodName() {
|
||||
return 'user.search';
|
||||
}
|
||||
|
||||
public function newSearchEngine() {
|
||||
return new PhabricatorPeopleSearchEngine();
|
||||
}
|
||||
|
||||
public function getMethodSummary() {
|
||||
return pht('Read information about users.');
|
||||
}
|
||||
|
||||
}
|
|
@ -59,6 +59,7 @@ final class PhabricatorPeopleProfileViewController
|
|||
|
||||
$projects = $this->buildProjectsView($user);
|
||||
$badges = $this->buildBadgesView($user);
|
||||
$calendar = $this->buildCalendarDayView($user);
|
||||
require_celerity_resource('project-view-css');
|
||||
|
||||
$home = id(new PHUITwoColumnView())
|
||||
|
@ -73,6 +74,7 @@ final class PhabricatorPeopleProfileViewController
|
|||
array(
|
||||
$projects,
|
||||
$badges,
|
||||
$calendar,
|
||||
));
|
||||
|
||||
$nav = $this->getProfileMenu();
|
||||
|
@ -172,6 +174,73 @@ final class PhabricatorPeopleProfileViewController
|
|||
return $box;
|
||||
}
|
||||
|
||||
private function buildCalendarDayView(PhabricatorUser $user) {
|
||||
$viewer = $this->getViewer();
|
||||
$class = 'PhabricatorCalendarApplication';
|
||||
|
||||
if (!PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$midnight = PhabricatorTime::getTodayMidnightDateTime($viewer);
|
||||
$week_end = clone $midnight;
|
||||
$week_end = $week_end->modify('+3 days');
|
||||
|
||||
$range_start = $midnight->format('U');
|
||||
$range_end = $week_end->format('U');
|
||||
|
||||
$query = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($viewer)
|
||||
->withDateRange($range_start, $range_end)
|
||||
->withInvitedPHIDs(array($user->getPHID()))
|
||||
->withIsCancelled(false);
|
||||
|
||||
$statuses = $query->execute();
|
||||
$phids = mpull($statuses, 'getUserPHID');
|
||||
$events = array();
|
||||
|
||||
foreach ($statuses as $status) {
|
||||
$viewer_is_invited = $status->getIsUserInvited($user->getPHID());
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$status,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
$event = id(new AphrontCalendarEventView())
|
||||
->setCanEdit($can_edit)
|
||||
->setEventID($status->getID())
|
||||
->setEpochRange($status->getDateFrom(), $status->getDateTo())
|
||||
->setIsAllDay($status->getIsAllDay())
|
||||
->setIcon($status->getIcon())
|
||||
->setViewerIsInvited($viewer_is_invited)
|
||||
->setName($status->getName())
|
||||
->setURI($status->getURI());
|
||||
$events[] = $event;
|
||||
}
|
||||
|
||||
$events = msort($events, 'getEpochStart');
|
||||
$day_view = id(new PHUICalendarWeekView())
|
||||
->setViewer($viewer)
|
||||
->setView('week')
|
||||
->setEvents($events)
|
||||
->setWeekLength(3)
|
||||
->render();
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Calendar'))
|
||||
->setHref(
|
||||
urisprintf(
|
||||
'/calendar/?invitedPHIDs=%s#R',
|
||||
$user->getPHID()));
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->appendChild($day_view)
|
||||
->setBackground(PHUIObjectBoxView::GREY);
|
||||
|
||||
return $box;
|
||||
}
|
||||
|
||||
private function buildBadgesView(PhabricatorUser $user) {
|
||||
|
||||
$viewer = $this->getViewer();
|
||||
|
|
|
@ -26,23 +26,6 @@ final class PhabricatorPeopleProfilePanelEngine
|
|||
->setBuiltinKey(self::PANEL_PROFILE)
|
||||
->setPanelKey(PhabricatorPeopleDetailsProfilePanel::PANELKEY);
|
||||
|
||||
// TODO: Convert this into a proper panel type.
|
||||
$have_calendar = PhabricatorApplication::isClassInstalledForViewer(
|
||||
'PhabricatorCalendarApplication',
|
||||
$viewer);
|
||||
if ($have_calendar) {
|
||||
$uri = urisprintf(
|
||||
'/p/%s/calendar/',
|
||||
$object->getUsername());
|
||||
|
||||
$panels[] = $this->newPanel()
|
||||
->setBuiltinKey('calendar')
|
||||
->setPanelKey(PhabricatorLinkProfilePanel::PANELKEY)
|
||||
->setPanelProperty('icon', 'calendar')
|
||||
->setPanelProperty('name', pht('Calendar'))
|
||||
->setPanelProperty('uri', $uri);
|
||||
}
|
||||
|
||||
$have_maniphest = PhabricatorApplication::isClassInstalledForViewer(
|
||||
'PhabricatorManiphestApplication',
|
||||
$viewer);
|
||||
|
|
|
@ -22,51 +22,79 @@ final class PhabricatorPeopleSearchEngine
|
|||
id(new PhabricatorSearchStringListField())
|
||||
->setLabel(pht('Usernames'))
|
||||
->setKey('usernames')
|
||||
->setAliases(array('username')),
|
||||
->setAliases(array('username'))
|
||||
->setDescription(pht('Find users by exact username.')),
|
||||
id(new PhabricatorSearchTextField())
|
||||
->setLabel(pht('Name Contains'))
|
||||
->setKey('nameLike'),
|
||||
->setKey('nameLike')
|
||||
->setDescription(
|
||||
pht('Find users whose usernames contain a substring.')),
|
||||
id(new PhabricatorSearchThreeStateField())
|
||||
->setLabel(pht('Administrators'))
|
||||
->setKey('isAdmin')
|
||||
->setOptions(
|
||||
pht('(Show All)'),
|
||||
pht('Show Only Administrators'),
|
||||
pht('Hide Administrators')),
|
||||
pht('Hide Administrators'))
|
||||
->setDescription(
|
||||
pht(
|
||||
'Pass true to find only administrators, or false to omit '.
|
||||
'administrators.')),
|
||||
id(new PhabricatorSearchThreeStateField())
|
||||
->setLabel(pht('Disabled'))
|
||||
->setKey('isDisabled')
|
||||
->setOptions(
|
||||
pht('(Show All)'),
|
||||
pht('Show Only Disabled Users'),
|
||||
pht('Hide Disabled Users')),
|
||||
pht('Hide Disabled Users'))
|
||||
->setDescription(
|
||||
pht(
|
||||
'Pass true to find only disabled users, or false to omit '.
|
||||
'disabled users.')),
|
||||
id(new PhabricatorSearchThreeStateField())
|
||||
->setLabel(pht('Bots'))
|
||||
->setKey('isSystemAgent')
|
||||
->setKey('isBot')
|
||||
->setAliases(array('isSystemAgent'))
|
||||
->setOptions(
|
||||
pht('(Show All)'),
|
||||
pht('Show Only Bots'),
|
||||
pht('Hide Bots')),
|
||||
pht('Hide Bots'))
|
||||
->setDescription(
|
||||
pht(
|
||||
'Pass true to find only bots, or false to omit bots.')),
|
||||
id(new PhabricatorSearchThreeStateField())
|
||||
->setLabel(pht('Mailing Lists'))
|
||||
->setKey('isMailingList')
|
||||
->setOptions(
|
||||
pht('(Show All)'),
|
||||
pht('Show Only Mailing Lists'),
|
||||
pht('Hide Mailing Lists')),
|
||||
pht('Hide Mailing Lists'))
|
||||
->setDescription(
|
||||
pht(
|
||||
'Pass true to find only mailing lists, or false to omit '.
|
||||
'mailing lists.')),
|
||||
id(new PhabricatorSearchThreeStateField())
|
||||
->setLabel(pht('Needs Approval'))
|
||||
->setKey('needsApproval')
|
||||
->setOptions(
|
||||
pht('(Show All)'),
|
||||
pht('Show Only Unapproved Users'),
|
||||
pht('Hide Unappproved Users')),
|
||||
pht('Hide Unappproved Users'))
|
||||
->setDescription(
|
||||
pht(
|
||||
'Pass true to find only users awaiting administrative approval, '.
|
||||
'or false to omit these users.')),
|
||||
id(new PhabricatorSearchDateField())
|
||||
->setKey('createdStart')
|
||||
->setLabel(pht('Joined After')),
|
||||
->setLabel(pht('Joined After'))
|
||||
->setDescription(
|
||||
pht('Find user accounts created after a given time.')),
|
||||
id(new PhabricatorSearchDateField())
|
||||
->setKey('createdEnd')
|
||||
->setLabel(pht('Joined Before')),
|
||||
->setLabel(pht('Joined Before'))
|
||||
->setDescription(
|
||||
pht('Find user accounts created before a given time.')),
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -115,8 +143,8 @@ final class PhabricatorPeopleSearchEngine
|
|||
$query->withIsMailingList($map['isMailingList']);
|
||||
}
|
||||
|
||||
if ($map['isSystemAgent'] !== null) {
|
||||
$query->withIsSystemAgent($map['isSystemAgent']);
|
||||
if ($map['isBot'] !== null) {
|
||||
$query->withIsSystemAgent($map['isBot']);
|
||||
}
|
||||
|
||||
if ($map['needsApproval'] !== null) {
|
||||
|
|
|
@ -16,7 +16,8 @@ final class PhabricatorUser
|
|||
PhabricatorSSHPublicKeyInterface,
|
||||
PhabricatorFlaggableInterface,
|
||||
PhabricatorApplicationTransactionInterface,
|
||||
PhabricatorFulltextInterface {
|
||||
PhabricatorFulltextInterface,
|
||||
PhabricatorConduitResultInterface {
|
||||
|
||||
const SESSION_TABLE = 'phabricator_session';
|
||||
const NAMETOKEN_TABLE = 'user_nametoken';
|
||||
|
@ -755,6 +756,17 @@ final class PhabricatorUser
|
|||
return new DateTimeZone($this->getTimezoneIdentifier());
|
||||
}
|
||||
|
||||
public function getTimeZoneOffset() {
|
||||
$timezone = $this->getTimeZone();
|
||||
$now = new DateTime('@'.PhabricatorTime::getNow());
|
||||
$offset = $timezone->getOffset($now);
|
||||
|
||||
// Javascript offsets are in minutes and have the opposite sign.
|
||||
$offset = -(int)($offset / 60);
|
||||
|
||||
return $offset;
|
||||
}
|
||||
|
||||
public function formatShortDateTime($when, $now = null) {
|
||||
if ($now === null) {
|
||||
$now = PhabricatorTime::getNow();
|
||||
|
@ -1378,4 +1390,68 @@ final class PhabricatorUser
|
|||
return new PhabricatorUserFulltextEngine();
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorConduitResultInterface )---------------------------------- */
|
||||
|
||||
|
||||
public function getFieldSpecificationsForConduit() {
|
||||
return array(
|
||||
id(new PhabricatorConduitSearchFieldSpecification())
|
||||
->setKey('username')
|
||||
->setType('string')
|
||||
->setDescription(pht("The user's username.")),
|
||||
id(new PhabricatorConduitSearchFieldSpecification())
|
||||
->setKey('realName')
|
||||
->setType('string')
|
||||
->setDescription(pht("The user's real name.")),
|
||||
id(new PhabricatorConduitSearchFieldSpecification())
|
||||
->setKey('roles')
|
||||
->setType('list<string>')
|
||||
->setDescription(pht('List of acccount roles.')),
|
||||
);
|
||||
}
|
||||
|
||||
public function getFieldValuesForConduit() {
|
||||
$roles = array();
|
||||
|
||||
if ($this->getIsDisabled()) {
|
||||
$roles[] = 'disabled';
|
||||
}
|
||||
|
||||
if ($this->getIsSystemAgent()) {
|
||||
$roles[] = 'bot';
|
||||
}
|
||||
|
||||
if ($this->getIsMailingList()) {
|
||||
$roles[] = 'list';
|
||||
}
|
||||
|
||||
if ($this->getIsAdmin()) {
|
||||
$roles[] = 'admin';
|
||||
}
|
||||
|
||||
if ($this->getIsEmailVerified()) {
|
||||
$roles[] = 'verified';
|
||||
}
|
||||
|
||||
if ($this->getIsApproved()) {
|
||||
$roles[] = 'approved';
|
||||
}
|
||||
|
||||
if ($this->isUserActivated()) {
|
||||
$roles[] = 'activated';
|
||||
}
|
||||
|
||||
return array(
|
||||
'username' => $this->getUsername(),
|
||||
'realName' => $this->getRealName(),
|
||||
'roles' => $roles,
|
||||
);
|
||||
}
|
||||
|
||||
public function getConduitSearchAttachments() {
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -82,8 +82,11 @@ final class PhabricatorProjectDatasource
|
|||
$closed = pht('Archived');
|
||||
}
|
||||
|
||||
$all_strings = mpull($proj->getSlugs(), 'getSlug');
|
||||
$all_strings = array();
|
||||
$all_strings[] = $proj->getDisplayName();
|
||||
foreach ($proj->getSlugs() as $project_slug) {
|
||||
$all_strings[] = $project_slug->getSlug();
|
||||
}
|
||||
$all_strings = implode(' ', $all_strings);
|
||||
|
||||
$proj_result = id(new PhabricatorTypeaheadResult())
|
||||
|
|
|
@ -2078,7 +2078,13 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
PhabricatorRepositoryURI::BUILTIN_IDENTIFIER_ID => true,
|
||||
);
|
||||
|
||||
$allow_http = PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth');
|
||||
// If the view policy of the repository is public, support anonymous HTTP
|
||||
// even if authenticated HTTP is not supported.
|
||||
if ($this->getViewPolicy() === PhabricatorPolicies::POLICY_PUBLIC) {
|
||||
$allow_http = true;
|
||||
} else {
|
||||
$allow_http = PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth');
|
||||
}
|
||||
|
||||
$base_uri = PhabricatorEnv::getURI('/');
|
||||
$base_uri = new PhutilURI($base_uri);
|
||||
|
|
|
@ -379,14 +379,40 @@ final class PhabricatorRepositoryURI
|
|||
}
|
||||
|
||||
private function getForcedPort() {
|
||||
switch ($this->getBuiltinProtocol()) {
|
||||
case self::BUILTIN_PROTOCOL_SSH:
|
||||
return PhabricatorEnv::getEnvConfig('diffusion.ssh-port');
|
||||
case self::BUILTIN_PROTOCOL_HTTP:
|
||||
case self::BUILTIN_PROTOCOL_HTTPS:
|
||||
default:
|
||||
return null;
|
||||
$protocol = $this->getBuiltinProtocol();
|
||||
|
||||
if ($protocol == self::BUILTIN_PROTOCOL_SSH) {
|
||||
return PhabricatorEnv::getEnvConfig('diffusion.ssh-port');
|
||||
}
|
||||
|
||||
// If Phabricator is running on a nonstandard port, use that as the defualt
|
||||
// port for URIs with the same protocol.
|
||||
|
||||
$is_http = ($protocol == self::BUILTIN_PROTOCOL_HTTP);
|
||||
$is_https = ($protocol == self::BUILTIN_PROTOCOL_HTTPS);
|
||||
|
||||
if ($is_http || $is_https) {
|
||||
$uri = PhabricatorEnv::getURI('/');
|
||||
$uri = new PhutilURI($uri);
|
||||
|
||||
$port = $uri->getPort();
|
||||
if (!$port) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$uri_protocol = $uri->getProtocol();
|
||||
$use_port =
|
||||
($is_http && ($uri_protocol == 'http')) ||
|
||||
($is_https && ($uri_protocol == 'https'));
|
||||
|
||||
if (!$use_port) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $port;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getForcedPath() {
|
||||
|
|
|
@ -13,14 +13,11 @@ final class PhabricatorSearchController
|
|||
$viewer = $this->getViewer();
|
||||
|
||||
if ($request->getStr('jump') != 'no') {
|
||||
$pref_jump = PhabricatorUserPreferences::PREFERENCE_SEARCHBAR_JUMP;
|
||||
if ($viewer->loadPreferences($pref_jump, 1)) {
|
||||
$response = PhabricatorJumpNavHandler::getJumpResponse(
|
||||
$viewer,
|
||||
$request->getStr('query'));
|
||||
if ($response) {
|
||||
return $response;
|
||||
}
|
||||
$response = PhabricatorJumpNavHandler::getJumpResponse(
|
||||
$viewer,
|
||||
$request->getStr('query'));
|
||||
if ($response) {
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -501,6 +501,7 @@ EOTEXT
|
|||
}
|
||||
|
||||
$table = id(new AphrontTableView($rows))
|
||||
->setNoDataString(pht('This call does not support any attachments.'))
|
||||
->setHeaders(
|
||||
array(
|
||||
pht('Key'),
|
||||
|
@ -597,6 +598,11 @@ EOTEXT
|
|||
|
||||
$view = new PHUIRemarkupView($viewer, $remarkup);
|
||||
|
||||
$view->setRemarkupOptions(
|
||||
array(
|
||||
PHUIRemarkupView::OPTION_PRESERVE_LINEBREAKS => false,
|
||||
));
|
||||
|
||||
return id(new PHUIBoxView())
|
||||
->appendChild($view)
|
||||
->addPadding(PHUI::PADDING_LARGE);
|
||||
|
|
|
@ -45,4 +45,8 @@ final class PhabricatorSearchThreeStateField
|
|||
return null;
|
||||
}
|
||||
|
||||
protected function newConduitParameterType() {
|
||||
return new ConduitBoolParameterType();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@ final class PhabricatorSettingsApplication extends PhabricatorApplication {
|
|||
'(?:(?P<id>\d+)/)?(?:panel/(?P<key>[^/]+)/)?'
|
||||
=> 'PhabricatorSettingsMainController',
|
||||
'adjust/' => 'PhabricatorSettingsAdjustController',
|
||||
'timezone/(?P<offset>[^/]+)/'
|
||||
=> 'PhabricatorSettingsTimezoneController',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorSettingsTimezoneController
|
||||
extends PhabricatorController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$client_offset = $request->getURIData('offset');
|
||||
$client_offset = (int)$client_offset;
|
||||
|
||||
$timezones = DateTimeZone::listIdentifiers();
|
||||
$now = new DateTime('@'.PhabricatorTime::getNow());
|
||||
|
||||
$options = array(
|
||||
'ignore' => pht('Ignore Conflict'),
|
||||
);
|
||||
|
||||
foreach ($timezones as $identifier) {
|
||||
$zone = new DateTimeZone($identifier);
|
||||
$offset = -($zone->getOffset($now) / 60);
|
||||
if ($offset == $client_offset) {
|
||||
$options[$identifier] = $identifier;
|
||||
}
|
||||
}
|
||||
|
||||
$settings_help = pht(
|
||||
'You can change your date and time preferences in Settings.');
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$timezone = $request->getStr('timezone');
|
||||
|
||||
$pref_ignore = PhabricatorUserPreferences::PREFERENCE_IGNORE_OFFSET;
|
||||
|
||||
$preferences = $viewer->loadPreferences();
|
||||
|
||||
if ($timezone == 'ignore') {
|
||||
$preferences
|
||||
->setPreference($pref_ignore, $client_offset)
|
||||
->save();
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Conflict Ignored'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'The conflict between your browser and profile timezone '.
|
||||
'settings will be ignored.'))
|
||||
->appendParagraph($settings_help)
|
||||
->addCancelButton('/', pht('Done'));
|
||||
}
|
||||
|
||||
if (isset($options[$timezone])) {
|
||||
$preferences
|
||||
->setPreference($pref_ignore, null)
|
||||
->save();
|
||||
|
||||
$viewer
|
||||
->setTimezoneIdentifier($timezone)
|
||||
->save();
|
||||
}
|
||||
}
|
||||
|
||||
$server_offset = $viewer->getTimeZoneOffset();
|
||||
|
||||
if ($client_offset == $server_offset) {
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Timezone Calibrated'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'Your browser timezone and profile timezone are now '.
|
||||
'in agreement (%s).',
|
||||
$this->formatOffset($client_offset)))
|
||||
->appendParagraph($settings_help)
|
||||
->addCancelButton('/', pht('Done'));
|
||||
}
|
||||
|
||||
// If we have a guess at the timezone from the client, select it as the
|
||||
// default.
|
||||
$guess = $request->getStr('guess');
|
||||
if (empty($options[$guess])) {
|
||||
$guess = 'ignore';
|
||||
}
|
||||
|
||||
$current_zone = $viewer->getTimezoneIdentifier();
|
||||
$current_zone = phutil_tag('strong', array(), $current_zone);
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->appendChild(
|
||||
id(new AphrontFormMarkupControl())
|
||||
->setLabel(pht('Current Setting'))
|
||||
->setValue($current_zone))
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setName('timezone')
|
||||
->setLabel(pht('New Setting'))
|
||||
->setOptions($options)
|
||||
->setValue($guess));
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Adjust Timezone'))
|
||||
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'Your browser timezone (%s) differs from your profile timezone '.
|
||||
'(%s). You can ignore this conflict or adjust your profile setting '.
|
||||
'to match your client.',
|
||||
$this->formatOffset($client_offset),
|
||||
$this->formatOffset($server_offset)))
|
||||
->appendForm($form)
|
||||
->addCancelButton(pht('Cancel'))
|
||||
->addSubmitButton(pht('Change Timezone'));
|
||||
}
|
||||
|
||||
private function formatOffset($offset) {
|
||||
$offset = $offset / 60;
|
||||
|
||||
if ($offset >= 0) {
|
||||
return pht('GMT-%d', $offset);
|
||||
} else {
|
||||
return pht('GMT+%d', -$offset);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -53,29 +53,7 @@ final class PhabricatorAccountSettingsPanel extends PhabricatorSettingsPanel {
|
|||
PhutilPerson::SEX_FEMALE => $label_her,
|
||||
);
|
||||
|
||||
$locales = PhutilLocale::loadAllLocales();
|
||||
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
|
||||
$is_dev = PhabricatorEnv::getEnvConfig('phabricator.developer-mode');
|
||||
|
||||
$translations = array();
|
||||
foreach ($locales as $locale) {
|
||||
if ($is_serious && $locale->isSillyLocale()) {
|
||||
// Omit silly locales on serious business installs.
|
||||
continue;
|
||||
}
|
||||
if (!$is_dev && $locale->isTestLocale()) {
|
||||
// Omit test locales on installs which aren't in development mode.
|
||||
continue;
|
||||
}
|
||||
$translations[$locale->getLocaleCode()] = $locale->getLocaleName();
|
||||
}
|
||||
|
||||
asort($translations);
|
||||
// TODO: Implement "locale.default" and use it here.
|
||||
$default = 'en_US';
|
||||
$translations = array(
|
||||
'' => pht('Server Default: %s', $locales[$default]->getLocaleName()),
|
||||
) + $translations;
|
||||
$translations = $this->getTranslationOptions();
|
||||
|
||||
$form = new AphrontFormView();
|
||||
$form
|
||||
|
@ -107,4 +85,87 @@ final class PhabricatorAccountSettingsPanel extends PhabricatorSettingsPanel {
|
|||
$form_box,
|
||||
);
|
||||
}
|
||||
|
||||
private function getTranslationOptions() {
|
||||
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
|
||||
$locales = PhutilLocale::loadAllLocales();
|
||||
|
||||
$group_labels = array(
|
||||
'normal' => pht('Translations'),
|
||||
'limited' => pht('Limited Translations'),
|
||||
'silly' => pht('Silly Translations'),
|
||||
'test' => pht('Developer/Test Translations'),
|
||||
);
|
||||
|
||||
$groups = array_fill_keys(array_keys($group_labels), array());
|
||||
|
||||
$translations = array();
|
||||
foreach ($locales as $locale) {
|
||||
$code = $locale->getLocaleCode();
|
||||
|
||||
// Get the locale's localized name if it's available. For example,
|
||||
// "Deutsch" instead of "German". This helps users who do not speak the
|
||||
// current language to find the correct setting.
|
||||
$raw_scope = PhabricatorEnv::beginScopedLocale($code);
|
||||
$name = $locale->getLocaleName();
|
||||
unset($raw_scope);
|
||||
|
||||
if ($locale->isSillyLocale()) {
|
||||
if ($is_serious) {
|
||||
// Omit silly locales on serious business installs.
|
||||
continue;
|
||||
}
|
||||
$groups['silly'][$code] = $name;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($locale->isTestLocale()) {
|
||||
$groups['test'][$code] = $name;
|
||||
continue;
|
||||
}
|
||||
|
||||
$strings = PhutilTranslation::getTranslationMapForLocale($code);
|
||||
$size = count($strings);
|
||||
|
||||
// If a translation is English, assume it can fall back to the default
|
||||
// strings and don't caveat its completeness.
|
||||
$is_english = (substr($code, 0, 3) == 'en_');
|
||||
|
||||
// Arbitrarily pick some number of available strings to promote a
|
||||
// translation out of the "limited" group. The major goal is just to
|
||||
// keep locales with very few strings out of the main group, so users
|
||||
// aren't surprised if a locale has no upstream translations available.
|
||||
if ($size > 512 || $is_english) {
|
||||
$type = 'normal';
|
||||
} else {
|
||||
$type = 'limited';
|
||||
}
|
||||
|
||||
$groups[$type][$code] = $name;
|
||||
}
|
||||
|
||||
// TODO: Select a default properly.
|
||||
$default = 'en_US';
|
||||
|
||||
$results = array();
|
||||
foreach ($groups as $key => $group) {
|
||||
$label = $group_labels[$key];
|
||||
if (!$group) {
|
||||
continue;
|
||||
}
|
||||
|
||||
asort($group);
|
||||
|
||||
if ($key == 'normal') {
|
||||
$group = array(
|
||||
'' => pht('Server Default: %s', $locales[$default]->getLocaleName()),
|
||||
) + $group;
|
||||
}
|
||||
|
||||
$results[$label] = $group;
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ final class PhabricatorDateTimeSettingsPanel extends PhabricatorSettingsPanel {
|
|||
$pref_time = PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT;
|
||||
$pref_date = PhabricatorUserPreferences::PREFERENCE_DATE_FORMAT;
|
||||
$pref_week_start = PhabricatorUserPreferences::PREFERENCE_WEEK_START_DAY;
|
||||
$pref_ignore = PhabricatorUserPreferences::PREFERENCE_IGNORE_OFFSET;
|
||||
$preferences = $user->loadPreferences();
|
||||
|
||||
$errors = array();
|
||||
|
@ -41,7 +42,8 @@ final class PhabricatorDateTimeSettingsPanel extends PhabricatorSettingsPanel {
|
|||
$request->getStr($pref_date))
|
||||
->setPreference(
|
||||
$pref_week_start,
|
||||
$request->getStr($pref_week_start));
|
||||
$request->getStr($pref_week_start))
|
||||
->setPreference($pref_ignore, null);
|
||||
|
||||
if (!$errors) {
|
||||
$preferences->save();
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorSearchPreferencesSettingsPanel
|
||||
extends PhabricatorSettingsPanel {
|
||||
|
||||
public function getPanelKey() {
|
||||
return 'search';
|
||||
}
|
||||
|
||||
public function getPanelName() {
|
||||
return pht('Search Preferences');
|
||||
}
|
||||
|
||||
public function getPanelGroup() {
|
||||
return pht('Application Settings');
|
||||
}
|
||||
|
||||
public function processRequest(AphrontRequest $request) {
|
||||
$user = $request->getUser();
|
||||
$preferences = $user->loadPreferences();
|
||||
|
||||
$pref_jump = PhabricatorUserPreferences::PREFERENCE_SEARCHBAR_JUMP;
|
||||
$pref_shortcut = PhabricatorUserPreferences::PREFERENCE_SEARCH_SHORTCUT;
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$preferences->setPreference($pref_jump,
|
||||
$request->getBool($pref_jump));
|
||||
|
||||
$preferences->setPreference($pref_shortcut,
|
||||
$request->getBool($pref_shortcut));
|
||||
|
||||
$preferences->save();
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI($this->getPanelURI('?saved=true'));
|
||||
}
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($user)
|
||||
->appendChild(
|
||||
id(new AphrontFormCheckboxControl())
|
||||
->addCheckbox($pref_jump,
|
||||
1,
|
||||
pht('Enable jump nav functionality in all search boxes.'),
|
||||
$preferences->getPreference($pref_jump, 1))
|
||||
->addCheckbox($pref_shortcut,
|
||||
1,
|
||||
pht("Press '%s' to focus the search input.", '/'),
|
||||
$preferences->getPreference($pref_shortcut, 1)))
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue(pht('Save')));
|
||||
|
||||
$form_box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Search Preferences'))
|
||||
->setFormSaved($request->getStr('saved') === 'true')
|
||||
->setForm($form);
|
||||
|
||||
return array(
|
||||
$form_box,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -19,8 +19,6 @@ final class PhabricatorUserPreferences extends PhabricatorUserDAO {
|
|||
const PREFERENCE_VARY_SUBJECT = 'vary-subject';
|
||||
const PREFERENCE_HTML_EMAILS = 'html-emails';
|
||||
|
||||
const PREFERENCE_SEARCHBAR_JUMP = 'searchbar-jump';
|
||||
const PREFERENCE_SEARCH_SHORTCUT = 'search-shortcut';
|
||||
const PREFERENCE_SEARCH_SCOPE = 'search-scope';
|
||||
|
||||
const PREFERENCE_DIFFUSION_BLAME = 'diffusion-blame';
|
||||
|
@ -43,6 +41,7 @@ final class PhabricatorUserPreferences extends PhabricatorUserDAO {
|
|||
|
||||
const PREFERENCE_PROFILE_MENU_COLLAPSED = 'profile-menu.collapsed';
|
||||
const PREFERENCE_FAVORITE_POLICIES = 'policy.favorites';
|
||||
const PREFERENCE_IGNORE_OFFSET = 'time.offset.ignore';
|
||||
|
||||
// These are in an unusual order for historic reasons.
|
||||
const MAILTAG_PREFERENCE_NOTIFY = 0;
|
||||
|
|
|
@ -313,7 +313,14 @@ EOTEXT
|
|||
|
||||
protected function renderInstructions($corpus) {
|
||||
$viewer = $this->getUser();
|
||||
return new PHUIRemarkupView($viewer, $corpus);
|
||||
$view = new PHUIRemarkupView($viewer, $corpus);
|
||||
|
||||
$view->setRemarkupOptions(
|
||||
array(
|
||||
PHUIRemarkupView::OPTION_PRESERVE_LINEBREAKS => false,
|
||||
));
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -173,14 +173,16 @@ SSH clone URIs by examining configuration.
|
|||
**HTTP**: The `http://` clone URI will be available if these conditions are
|
||||
satisfied:
|
||||
|
||||
- `diffusion.allow-http-auth` must be enabled.
|
||||
- `diffusion.allow-http-auth` must be enabled or the repository view policy
|
||||
must be "Public".
|
||||
- The repository must be a Git or Mercurial repository.
|
||||
- `security.require-https` must be disabled.
|
||||
|
||||
**HTTPS**: The `https://` clone URI will be available if these conditions are
|
||||
satisfied:
|
||||
|
||||
- `diffusion.allow-http-auth` must be enabled.
|
||||
- `diffusion.allow-http-auth` must be enabled or the repository view policy
|
||||
must be "Public".
|
||||
- The repository must be a Git or Mercurial repository.
|
||||
- The `phabricator.base-uri` protocol must be `https://`.
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ final class PhabricatorInternationalizationManagementExtractWorkflow
|
|||
}
|
||||
}
|
||||
|
||||
$console->writeOut(
|
||||
$console->writeErr(
|
||||
"%s\n",
|
||||
pht('Found %s file(s)...', phutil_count($futures)));
|
||||
|
||||
|
@ -44,14 +44,24 @@ final class PhabricatorInternationalizationManagementExtractWorkflow
|
|||
$bar = id(new PhutilConsoleProgressBar())
|
||||
->setTotal(count($futures));
|
||||
|
||||
$messages = array();
|
||||
|
||||
$futures = id(new FutureIterator($futures))
|
||||
->limit(8);
|
||||
foreach ($futures as $full_path => $future) {
|
||||
$bar->update(1);
|
||||
|
||||
$tree = XHPASTTree::newFromDataAndResolvedExecFuture(
|
||||
Filesystem::readFile($full_path),
|
||||
$future->resolve());
|
||||
try {
|
||||
$tree = XHPASTTree::newFromDataAndResolvedExecFuture(
|
||||
Filesystem::readFile($full_path),
|
||||
$future->resolve());
|
||||
} catch (Exception $ex) {
|
||||
$messages[] = pht(
|
||||
'WARNING: Failed to extract strings from file "%s": %s',
|
||||
$full_path,
|
||||
$ex->getMessage());
|
||||
continue;
|
||||
}
|
||||
|
||||
$root = $tree->getRootNode();
|
||||
$calls = $root->selectDescendantsOfType('n_FUNCTION_CALL');
|
||||
|
@ -69,7 +79,11 @@ final class PhabricatorInternationalizationManagementExtractWorkflow
|
|||
'line' => $string_line,
|
||||
);
|
||||
} catch (Exception $ex) {
|
||||
// TODO: Deal with this junks.
|
||||
$messages[] = pht(
|
||||
'WARNING: Failed to evaluate pht() call on line %d in "%s": %s',
|
||||
$call->getLineNumber(),
|
||||
$full_path,
|
||||
$ex->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -78,6 +92,10 @@ final class PhabricatorInternationalizationManagementExtractWorkflow
|
|||
}
|
||||
$bar->done();
|
||||
|
||||
foreach ($messages as $message) {
|
||||
$console->writeErr("%s\n", $message);
|
||||
}
|
||||
|
||||
ksort($results);
|
||||
|
||||
$out = array();
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorEmojiTranslation
|
||||
extends PhutilTranslation {
|
||||
|
||||
public function getLocaleCode() {
|
||||
return 'en_X*';
|
||||
}
|
||||
|
||||
protected function getTranslations() {
|
||||
return array(
|
||||
'Emoji (Internet)' => "\xF0\x9F\x92\xAC (\xF0\x9F\x8C\x8D)",
|
||||
);
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ final class PhabricatorVeryWowEnglishTranslation
|
|||
'Prototype' => 'Chew Toy',
|
||||
'Continue' => 'Bark And Run',
|
||||
'Countdown to Events' => 'To the Moon!',
|
||||
'English (Very Wow)' => 'Such English',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -470,6 +470,7 @@ final class PhabricatorMarkupEngine extends Phobject {
|
|||
$engine = new PhutilRemarkupEngine();
|
||||
|
||||
$engine->setConfig('preserve-linebreaks', $options['preserve-linebreaks']);
|
||||
|
||||
$engine->setConfig('pygments.enabled', $options['pygments']);
|
||||
$engine->setConfig(
|
||||
'uri.allowed-protocols',
|
||||
|
|
|
@ -10,6 +10,7 @@ final class PhabricatorMarkupOneOff
|
|||
private $content;
|
||||
private $preserveLinebreaks;
|
||||
private $engineRuleset;
|
||||
private $engine;
|
||||
private $disableCache;
|
||||
|
||||
public function setEngineRuleset($engine_ruleset) {
|
||||
|
@ -35,6 +36,15 @@ final class PhabricatorMarkupOneOff
|
|||
return $this->content;
|
||||
}
|
||||
|
||||
public function setEngine(PhutilMarkupEngine $engine) {
|
||||
$this->engine = $engine;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEngine() {
|
||||
return $this->engine;
|
||||
}
|
||||
|
||||
public function setDisableCache($disable_cache) {
|
||||
$this->disableCache = $disable_cache;
|
||||
return $this;
|
||||
|
@ -49,6 +59,10 @@ final class PhabricatorMarkupOneOff
|
|||
}
|
||||
|
||||
public function newMarkupEngine($field) {
|
||||
if ($this->engine) {
|
||||
return $this->engine;
|
||||
}
|
||||
|
||||
if ($this->engineRuleset) {
|
||||
return PhabricatorMarkupEngine::getEngine($this->engineRuleset);
|
||||
} else if ($this->preserveLinebreaks) {
|
||||
|
|
|
@ -12,21 +12,19 @@
|
|||
final class PHUIRemarkupView extends AphrontView {
|
||||
|
||||
private $corpus;
|
||||
private $markupType;
|
||||
private $contextObject;
|
||||
private $options;
|
||||
|
||||
const DOCUMENT = 'document';
|
||||
// TODO: In the long run, rules themselves should define available options.
|
||||
// For now, just define constants here so we can more easily replace things
|
||||
// later once this is cleaned up.
|
||||
const OPTION_PRESERVE_LINEBREAKS = 'preserve-linebreaks';
|
||||
|
||||
public function __construct(PhabricatorUser $viewer, $corpus) {
|
||||
$this->setUser($viewer);
|
||||
$this->corpus = $corpus;
|
||||
}
|
||||
|
||||
private function setMarkupType($type) {
|
||||
$this->markupType($type);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setContextObject($context_object) {
|
||||
$this->contextObject = $context_object;
|
||||
return $this;
|
||||
|
@ -36,29 +34,63 @@ final class PHUIRemarkupView extends AphrontView {
|
|||
return $this->contextObject;
|
||||
}
|
||||
|
||||
public function setRemarkupOption($key, $value) {
|
||||
$this->options[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setRemarkupOptions(array $options) {
|
||||
foreach ($options as $key => $value) {
|
||||
$this->setRemarkupOption($key, $value);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$viewer = $this->getUser();
|
||||
$viewer = $this->getViewer();
|
||||
$corpus = $this->corpus;
|
||||
$context = $this->getContextObject();
|
||||
|
||||
$options = $this->options;
|
||||
|
||||
$oneoff = id(new PhabricatorMarkupOneOff())
|
||||
->setContent($corpus);
|
||||
|
||||
if ($options) {
|
||||
$oneoff->setEngine($this->getEngine());
|
||||
} else {
|
||||
$oneoff->setPreserveLinebreaks(true);
|
||||
}
|
||||
|
||||
$content = PhabricatorMarkupEngine::renderOneObject(
|
||||
id(new PhabricatorMarkupOneOff())
|
||||
->setPreserveLinebreaks(true)
|
||||
->setContent($corpus),
|
||||
$oneoff,
|
||||
'default',
|
||||
$viewer,
|
||||
$context);
|
||||
|
||||
if ($this->markupType == self::DOCUMENT) {
|
||||
return phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'phabricator-remarkup phui-document-view',
|
||||
),
|
||||
$content);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function getEngine() {
|
||||
$options = $this->options;
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$viewer_key = $viewer->getCacheFragment();
|
||||
|
||||
ksort($options);
|
||||
$engine_key = serialize($options);
|
||||
$engine_key = PhabricatorHash::digestForIndex($engine_key);
|
||||
|
||||
$cache = PhabricatorCaches::getRequestCache();
|
||||
$cache_key = "remarkup.engine({$viewer}, {$engine_key})";
|
||||
|
||||
$engine = $cache->getKey($cache_key);
|
||||
if (!$engine) {
|
||||
$engine = PhabricatorMarkupEngine::newMarkupEngine($options);
|
||||
$cache->setKey($cache_key, $engine);
|
||||
}
|
||||
|
||||
return $engine;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -84,9 +84,20 @@ final class AphrontFormView extends AphrontView {
|
|||
}
|
||||
|
||||
public function appendRemarkupInstructions($remarkup) {
|
||||
return $this->appendInstructions(
|
||||
new PHUIRemarkupView($this->getViewer(), $remarkup));
|
||||
$view = $this->newInstructionsRemarkupView($remarkup);
|
||||
return $this->appendInstructions($view);
|
||||
}
|
||||
|
||||
public function newInstructionsRemarkupView($remarkup) {
|
||||
$viewer = $this->getViewer();
|
||||
$view = new PHUIRemarkupView($viewer, $remarkup);
|
||||
|
||||
$view->setRemarkupOptions(
|
||||
array(
|
||||
PHUIRemarkupView::OPTION_PRESERVE_LINEBREAKS => false,
|
||||
));
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
public function buildLayoutView() {
|
||||
|
|
|
@ -31,14 +31,11 @@ final class PHUIFormLayoutView extends AphrontView {
|
|||
}
|
||||
|
||||
public function appendRemarkupInstructions($remarkup) {
|
||||
if ($this->getUser() === null) {
|
||||
throw new PhutilInvalidStateException('setUser');
|
||||
}
|
||||
$view = id(new AphrontFormView())
|
||||
->setViewer($this->getViewer())
|
||||
->newInstructionsRemarkupView($remarkup);
|
||||
|
||||
$viewer = $this->getUser();
|
||||
$instructions = new PHUIRemarkupView($viewer, $remarkup);
|
||||
|
||||
return $this->appendInstructions($instructions);
|
||||
return $this->appendInstructions($view);
|
||||
}
|
||||
|
||||
public function render() {
|
||||
|
|
44
src/view/form/control/PHUIFormFileControl.php
Normal file
44
src/view/form/control/PHUIFormFileControl.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
final class PHUIFormFileControl
|
||||
extends AphrontFormControl {
|
||||
|
||||
private $allowMultiple;
|
||||
|
||||
protected function getCustomControlClass() {
|
||||
return 'phui-form-file-upload';
|
||||
}
|
||||
|
||||
public function setAllowMultiple($allow_multiple) {
|
||||
$this->allowMultiple = $allow_multiple;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAllowMultiple() {
|
||||
return $this->allowMultiple;
|
||||
}
|
||||
|
||||
protected function renderInput() {
|
||||
$file_id = $this->getID();
|
||||
|
||||
Javelin::initBehavior(
|
||||
'phui-file-upload',
|
||||
array(
|
||||
'fileInputID' => $file_id,
|
||||
'inputName' => $this->getName(),
|
||||
'uploadURI' => '/file/dropupload/',
|
||||
'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold(),
|
||||
));
|
||||
|
||||
return phutil_tag(
|
||||
'input',
|
||||
array(
|
||||
'type' => 'file',
|
||||
'multiple' => $this->getAllowMultiple() ? 'multiple' : null,
|
||||
'name' => $this->getName().'.raw',
|
||||
'id' => $file_id,
|
||||
'disabled' => $this->getDisabled() ? 'disabled' : null,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
|
@ -223,6 +223,30 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView
|
|||
}
|
||||
|
||||
if ($user) {
|
||||
if ($user->isLoggedIn()) {
|
||||
$offset = $user->getTimeZoneOffset();
|
||||
|
||||
$preferences = $user->loadPreferences();
|
||||
$ignore_key = PhabricatorUserPreferences::PREFERENCE_IGNORE_OFFSET;
|
||||
|
||||
$ignore = $preferences->getPreference($ignore_key);
|
||||
if (!strlen($ignore)) {
|
||||
$ignore = null;
|
||||
}
|
||||
|
||||
Javelin::initBehavior(
|
||||
'detect-timezone',
|
||||
array(
|
||||
'offset' => $offset,
|
||||
'uri' => '/settings/timezone/',
|
||||
'message' => pht(
|
||||
'Your browser timezone setting differs from the timezone '.
|
||||
'setting in your profile, click to reconcile.'),
|
||||
'ignoreKey' => $ignore_key,
|
||||
'ignore' => $ignore,
|
||||
));
|
||||
}
|
||||
|
||||
$default_img_uri =
|
||||
celerity_get_resource_uri(
|
||||
'rsrc/image/icon/fatcow/document_black.png');
|
||||
|
|
|
@ -186,11 +186,6 @@ final class PhabricatorMainMenuView extends AphrontView {
|
|||
}
|
||||
|
||||
$result = $search;
|
||||
|
||||
$pref_shortcut = PhabricatorUserPreferences::PREFERENCE_SEARCH_SHORTCUT;
|
||||
if ($viewer->loadPreferences()->getPreference($pref_shortcut, true)) {
|
||||
$keyboard_config['searchID'] = $search->getID();
|
||||
}
|
||||
}
|
||||
|
||||
Javelin::initBehavior('phabricator-keyboard-shortcuts', $keyboard_config);
|
||||
|
|
|
@ -145,7 +145,11 @@ final class PHUICalendarDayView extends AphrontView {
|
|||
}
|
||||
|
||||
$header = $this->renderDayViewHeader();
|
||||
$sidebar = $this->renderSidebar();
|
||||
$sidebar = id(new PHUICalendarWeekView())
|
||||
->setViewer($this->getViewer())
|
||||
->setEvents($this->events)
|
||||
->setDateTime($this->getDateTime())
|
||||
->render();
|
||||
$warnings = $this->getQueryRangeWarning();
|
||||
|
||||
$table_id = celerity_generate_unique_node_id();
|
||||
|
@ -242,91 +246,6 @@ final class PHUICalendarDayView extends AphrontView {
|
|||
return $errors;
|
||||
}
|
||||
|
||||
private function renderSidebar() {
|
||||
$this->events = msort($this->events, 'getEpochStart');
|
||||
$week_of_boxes = $this->getWeekOfBoxes();
|
||||
$filled_boxes = array();
|
||||
|
||||
foreach ($week_of_boxes as $day_box) {
|
||||
$box_start = $day_box['start'];
|
||||
$box_end = id(clone $box_start)->modify('+1 day');
|
||||
|
||||
$box_start = $box_start->format('U');
|
||||
$box_end = $box_end->format('U');
|
||||
|
||||
$box_events = array();
|
||||
|
||||
foreach ($this->events as $event) {
|
||||
$event_start = $event->getEpochStart();
|
||||
$event_end = $event->getEpochEnd();
|
||||
|
||||
if ($event_start < $box_end && $event_end > $box_start) {
|
||||
$box_events[] = $event;
|
||||
}
|
||||
}
|
||||
|
||||
$filled_boxes[] = $this->renderSidebarBox(
|
||||
$box_events,
|
||||
$day_box['title']);
|
||||
}
|
||||
|
||||
return $filled_boxes;
|
||||
}
|
||||
|
||||
private function renderSidebarBox($events, $title) {
|
||||
$widget = id(new PHUICalendarWidgetView())
|
||||
->addClass('calendar-day-view-sidebar');
|
||||
|
||||
$list = id(new PHUICalendarListView())
|
||||
->setUser($this->getViewer())
|
||||
->setView('day');
|
||||
|
||||
if (count($events) == 0) {
|
||||
$list->showBlankState(true);
|
||||
} else {
|
||||
$sorted_events = msort($events, 'getEpochStart');
|
||||
foreach ($sorted_events as $event) {
|
||||
$list->addEvent($event);
|
||||
}
|
||||
}
|
||||
|
||||
$widget
|
||||
->setCalendarList($list)
|
||||
->setHeader($title);
|
||||
return $widget;
|
||||
}
|
||||
|
||||
private function getWeekOfBoxes() {
|
||||
$sidebar_day_boxes = array();
|
||||
|
||||
$display_start_day = $this->getDateTime();
|
||||
$display_end_day = id(clone $display_start_day)->modify('+6 day');
|
||||
|
||||
$box_start_time = clone $display_start_day;
|
||||
|
||||
$today_time = PhabricatorTime::getTodayMidnightDateTime($this->getViewer());
|
||||
$tomorrow_time = clone $today_time;
|
||||
$tomorrow_time->modify('+1 day');
|
||||
|
||||
while ($box_start_time <= $display_end_day) {
|
||||
if ($box_start_time == $today_time) {
|
||||
$title = pht('Today');
|
||||
} else if ($box_start_time == $tomorrow_time) {
|
||||
$title = pht('Tomorrow');
|
||||
} else {
|
||||
$title = $box_start_time->format('l');
|
||||
}
|
||||
|
||||
$sidebar_day_boxes[] = array(
|
||||
'title' => $title,
|
||||
'start' => clone $box_start_time,
|
||||
);
|
||||
|
||||
$box_start_time->modify('+1 day');
|
||||
}
|
||||
return $sidebar_day_boxes;
|
||||
}
|
||||
|
||||
private function renderDayViewHeader() {
|
||||
$button_bar = null;
|
||||
$uri = $this->getBrowseURI();
|
||||
|
|
|
@ -85,7 +85,13 @@ final class PHUICalendarListView extends AphrontTagView {
|
|||
}
|
||||
|
||||
$tip = $this->getEventTooltip($event);
|
||||
$tip_align = ($this->getView() == 'day') ? 'E' : 'N';
|
||||
if ($this->getView() == 'day') {
|
||||
$tip_align = 'E';
|
||||
} else if ($this->getView() == 'month') {
|
||||
$tip_align = 'N';
|
||||
} else {
|
||||
$tip_align = 'W';
|
||||
}
|
||||
$content = javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
|
|
|
@ -90,8 +90,9 @@ final class PHUICalendarMonthView extends AphrontView {
|
|||
$max_daily = 15;
|
||||
$counter = 0;
|
||||
|
||||
$list = new PHUICalendarListView();
|
||||
$list->setViewer($viewer);
|
||||
$list = id(new PHUICalendarListView())
|
||||
->setViewer($viewer)
|
||||
->setView('month');
|
||||
foreach ($all_day_events as $item) {
|
||||
if ($counter <= $max_daily) {
|
||||
$list->addEvent($item);
|
||||
|
|
131
src/view/phui/calendar/PHUICalendarWeekView.php
Normal file
131
src/view/phui/calendar/PHUICalendarWeekView.php
Normal file
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
final class PHUICalendarWeekView extends AphrontView {
|
||||
private $events;
|
||||
private $dateTime;
|
||||
private $weekLength = 7;
|
||||
private $view = 'day';
|
||||
|
||||
public function setEvents($events) {
|
||||
$this->events = $events;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDateTime($date_time) {
|
||||
$this->dateTime = $date_time;
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function getDateTime() {
|
||||
if ($this->dateTime) {
|
||||
return $this->dateTime;
|
||||
}
|
||||
return $this->getDefaultDateTime();
|
||||
}
|
||||
|
||||
public function setWeekLength($week_length) {
|
||||
$this->weekLength = $week_length;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setView($view) {
|
||||
$this->view = $view;
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function getView() {
|
||||
return $this->view;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$this->events = msort($this->events, 'getEpochStart');
|
||||
$week_of_boxes = $this->getWeekOfBoxes();
|
||||
$filled_boxes = array();
|
||||
|
||||
foreach ($week_of_boxes as $day_box) {
|
||||
$box_start = $day_box['start'];
|
||||
$box_end = id(clone $box_start)->modify('+1 day');
|
||||
|
||||
$box_start = $box_start->format('U');
|
||||
$box_end = $box_end->format('U');
|
||||
|
||||
$box_events = array();
|
||||
|
||||
foreach ($this->events as $event) {
|
||||
$event_start = $event->getEpochStart();
|
||||
$event_end = $event->getEpochEnd();
|
||||
|
||||
if ($event_start < $box_end && $event_end > $box_start) {
|
||||
$box_events[] = $event;
|
||||
}
|
||||
}
|
||||
|
||||
$filled_boxes[] = $this->renderSidebarBox(
|
||||
$box_events,
|
||||
$day_box['title']);
|
||||
}
|
||||
|
||||
return $filled_boxes;
|
||||
}
|
||||
|
||||
private function renderSidebarBox($events, $title) {
|
||||
$widget = id(new PHUICalendarWidgetView())
|
||||
->addClass('calendar-day-view-sidebar');
|
||||
|
||||
$list = id(new PHUICalendarListView())
|
||||
->setUser($this->getViewer())
|
||||
->setView($this->getView());
|
||||
|
||||
if (count($events) == 0) {
|
||||
$list->showBlankState(true);
|
||||
} else {
|
||||
$sorted_events = msort($events, 'getEpochStart');
|
||||
foreach ($sorted_events as $event) {
|
||||
$list->addEvent($event);
|
||||
}
|
||||
}
|
||||
|
||||
$widget
|
||||
->setCalendarList($list)
|
||||
->setHeader($title);
|
||||
return $widget;
|
||||
}
|
||||
|
||||
private function getWeekOfBoxes() {
|
||||
$day_boxes = array();
|
||||
$week_length = $this->weekLength - 1;
|
||||
|
||||
$display_start_day = $this->getDateTime();
|
||||
$display_end_day = id(clone $display_start_day)
|
||||
->modify('+'.$week_length.' day');
|
||||
|
||||
$box_start_time = clone $display_start_day;
|
||||
|
||||
$today_time = PhabricatorTime::getTodayMidnightDateTime($this->getViewer());
|
||||
$tomorrow_time = clone $today_time;
|
||||
$tomorrow_time->modify('+1 day');
|
||||
|
||||
while ($box_start_time <= $display_end_day) {
|
||||
if ($box_start_time == $today_time) {
|
||||
$title = pht('Today');
|
||||
} else if ($box_start_time == $tomorrow_time) {
|
||||
$title = pht('Tomorrow');
|
||||
} else {
|
||||
$title = $box_start_time->format('l');
|
||||
}
|
||||
|
||||
$day_boxes[] = array(
|
||||
'title' => $title,
|
||||
'start' => clone $box_start_time,
|
||||
);
|
||||
|
||||
$box_start_time->modify('+1 day');
|
||||
}
|
||||
return $day_boxes;
|
||||
}
|
||||
|
||||
private function getDefaultDateTime() {
|
||||
return PhabricatorTime::getTodayMidnightDateTime($this->getViewer());
|
||||
}
|
||||
|
||||
}
|
|
@ -2,10 +2,6 @@
|
|||
* @provides phui-calendar-list-css
|
||||
*/
|
||||
|
||||
.phui-calendar-list-container {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.device-phone .phui-calendar-list-container {
|
||||
width: auto;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
.device-phone .phui-crumbs-view {
|
||||
.device-tablet .phui-crumbs-view,
|
||||
.device-phone .phui-crumbs-view,
|
||||
.project-board-nav .phui-crumbs-view {
|
||||
padding-left: 8px;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
|
63
webroot/rsrc/externals/javelin/lib/Workflow.js
vendored
63
webroot/rsrc/externals/javelin/lib/Workflow.js
vendored
|
@ -25,7 +25,7 @@ JX.install('Workflow', {
|
|||
this.setData(data || {});
|
||||
},
|
||||
|
||||
events : ['error', 'finally', 'submit'],
|
||||
events : ['error', 'finally', 'submit', 'start'],
|
||||
|
||||
statics : {
|
||||
_stack : [],
|
||||
|
@ -54,6 +54,9 @@ JX.install('Workflow', {
|
|||
}
|
||||
|
||||
var workflow = new JX.Workflow(form.getAttribute('action'), {});
|
||||
|
||||
workflow._form = form;
|
||||
|
||||
workflow.setDataWithListOfPairs(pairs);
|
||||
workflow.setMethod(form.getAttribute('method'));
|
||||
workflow.listen('finally', function() {
|
||||
|
@ -137,9 +140,14 @@ JX.install('Workflow', {
|
|||
data.push([button.name, button.value || true]);
|
||||
|
||||
var active = JX.Workflow._getActiveWorkflow();
|
||||
|
||||
active._form = form;
|
||||
|
||||
var e = active.invoke('submit', {form: form, data: data});
|
||||
if (!e.getStopped()) {
|
||||
active._destroy();
|
||||
// NOTE: Don't remove the current dialog yet because additional
|
||||
// handlers may still want to access the nodes.
|
||||
|
||||
active
|
||||
.setURI(form.getAttribute('action') || active.getURI())
|
||||
.setDataWithListOfPairs(data)
|
||||
|
@ -156,7 +164,41 @@ JX.install('Workflow', {
|
|||
_root : null,
|
||||
_pushed : false,
|
||||
_data : null,
|
||||
|
||||
_form: null,
|
||||
_paused: 0,
|
||||
_nextCallback: null,
|
||||
|
||||
getSourceForm: function() {
|
||||
return this._form;
|
||||
},
|
||||
|
||||
pause: function() {
|
||||
this._paused++;
|
||||
return this;
|
||||
},
|
||||
|
||||
resume: function() {
|
||||
if (!this._paused) {
|
||||
JX.$E('Resuming a workflow which is not paused!');
|
||||
}
|
||||
|
||||
this._paused--;
|
||||
|
||||
if (!this._paused) {
|
||||
var next = this._nextCallback;
|
||||
this._nextCallback = null;
|
||||
if (next) {
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_onload : function(r) {
|
||||
this._destroy();
|
||||
|
||||
// It is permissible to send back a falsey redirect to force a page
|
||||
// reload, so we need to take this branch if the key is present.
|
||||
if (r && (typeof r.redirect != 'undefined')) {
|
||||
|
@ -247,7 +289,19 @@ JX.install('Workflow', {
|
|||
this._root = null;
|
||||
}
|
||||
},
|
||||
|
||||
start : function() {
|
||||
var next = JX.bind(this, this._send);
|
||||
|
||||
this.pause();
|
||||
this._nextCallback = next;
|
||||
|
||||
this.invoke('start', this);
|
||||
|
||||
this.resume();
|
||||
},
|
||||
|
||||
_send: function() {
|
||||
var uri = this.getURI();
|
||||
var method = this.getMethod();
|
||||
var r = new JX.Request(uri, JX.bind(this, this._onload));
|
||||
|
@ -291,6 +345,11 @@ JX.install('Workflow', {
|
|||
return this;
|
||||
},
|
||||
|
||||
addData: function(key, value) {
|
||||
this._data.push([key, value]);
|
||||
return this;
|
||||
},
|
||||
|
||||
setDataWithListOfPairs : function(list_of_pairs) {
|
||||
this._data = list_of_pairs;
|
||||
return this;
|
||||
|
|
|
@ -33,7 +33,7 @@ JX.behavior('durable-column', function(config, statics) {
|
|||
|
||||
var columnWidth = (300 + margin);
|
||||
// This is the smallest window size where we'll enable the column.
|
||||
var minimumViewportWidth = (768 - margin);
|
||||
var minimumViewportWidth = (920 - margin);
|
||||
|
||||
var quick = JX.$('phabricator-standard-page-body');
|
||||
|
||||
|
|
|
@ -155,7 +155,7 @@ JX.install('PhabricatorDragAndDropFileUpload', {
|
|||
|
||||
var files = e.getRawEvent().dataTransfer.files;
|
||||
for (var ii = 0; ii < files.length; ii++) {
|
||||
this._sendRequest(files[ii]);
|
||||
this.sendRequest(files[ii]);
|
||||
}
|
||||
|
||||
// Force depth to 0.
|
||||
|
@ -216,7 +216,7 @@ JX.install('PhabricatorDragAndDropFileUpload', {
|
|||
if (!spec.name) {
|
||||
spec.name = 'pasted_file';
|
||||
}
|
||||
this._sendRequest(spec);
|
||||
this.sendRequest(spec);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
@ -224,7 +224,7 @@ JX.install('PhabricatorDragAndDropFileUpload', {
|
|||
this.setIsEnabled(true);
|
||||
},
|
||||
|
||||
_sendRequest : function(spec) {
|
||||
sendRequest : function(spec) {
|
||||
var file = new JX.PhabricatorFileUpload()
|
||||
.setRawFileObject(spec)
|
||||
.setName(spec.name)
|
||||
|
|
|
@ -62,6 +62,26 @@ JX.install('TextAreaUtils', {
|
|||
JX.TextAreaUtils.setSelectionRange(area, start, end);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Insert a reference to a given uploaded file into a textarea.
|
||||
*/
|
||||
insertFileReference: function(area, file) {
|
||||
var ref = '{F' + file.getID() + '}';
|
||||
|
||||
// If we're inserting immediately after a "}" (usually, another file
|
||||
// reference), put some newlines before our token so that multiple file
|
||||
// uploads get laid out more nicely.
|
||||
var range = JX.TextAreaUtils.getSelectionRange(area);
|
||||
var before = area.value.substring(0, range.start);
|
||||
if (before.match(/\}$/)) {
|
||||
ref = '\n\n' + ref;
|
||||
}
|
||||
|
||||
JX.TextAreaUtils.setSelectionText(area, ref, false);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Get the document pixel positions of the beginning and end of a character
|
||||
* range in a textarea.
|
||||
|
|
65
webroot/rsrc/js/core/behavior-detect-timezone.js
Normal file
65
webroot/rsrc/js/core/behavior-detect-timezone.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* @provides javelin-behavior-detect-timezone
|
||||
* @requires javelin-behavior
|
||||
* javelin-uri
|
||||
* phabricator-notification
|
||||
*/
|
||||
|
||||
JX.behavior('detect-timezone', function(config) {
|
||||
|
||||
var offset = new Date().getTimezoneOffset();
|
||||
var ignore = config.ignore;
|
||||
|
||||
if (ignore !== null) {
|
||||
// If we're ignoring a client offset and it's the current offset, just
|
||||
// bail. This means the user has chosen to ignore the clock difference
|
||||
// between the current client setting and their server setting.
|
||||
if (offset == ignore) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're ignoring a client offset but the current offset is different,
|
||||
// wipe the offset. If you go from SF to NY, ignore the difference, return
|
||||
// to SF, then travel back to NY a few months later, we want to prompt you
|
||||
// again. This code will clear the ignored setting upon your return to SF.
|
||||
new JX.Request('/settings/adjust/', JX.bag)
|
||||
.setData({key: config.ignoreKey, value: ''})
|
||||
.send();
|
||||
|
||||
ignore = null;
|
||||
}
|
||||
|
||||
// If the client and server clocks are in sync, we're all set.
|
||||
if (offset == config.offset) {
|
||||
return;
|
||||
}
|
||||
|
||||
var notification = new JX.Notification()
|
||||
.alterClassName('jx-notification-alert', true)
|
||||
.setContent(config.message)
|
||||
.setDuration(0);
|
||||
|
||||
notification.listen('activate', function() {
|
||||
JX.Stratcom.context().kill();
|
||||
notification.hide();
|
||||
|
||||
var uri = config.uri + offset + '/';
|
||||
|
||||
// Some browsers (notably, Chrome) expose an "Intl" API which gives us
|
||||
// direct access to a timezone setting. If we are able to read this, use
|
||||
// it to guess which timezone the user is in so we can prefill the
|
||||
// dropdown.
|
||||
try {
|
||||
var guess = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
uri = JX.$U(uri).setQueryParam('guess', guess);
|
||||
} catch (error) {
|
||||
// Ignore any errors here, we'll just make the user pick from the big
|
||||
// list.
|
||||
}
|
||||
|
||||
new JX.Workflow(uri)
|
||||
.start();
|
||||
});
|
||||
|
||||
notification.show();
|
||||
});
|
|
@ -10,7 +10,7 @@
|
|||
JX.install('Device', {
|
||||
statics : {
|
||||
_device : null,
|
||||
_tabletBreakpoint: 768,
|
||||
_tabletBreakpoint: 920,
|
||||
|
||||
setTabletBreakpoint: function(width) {
|
||||
var self = JX.Device;
|
||||
|
|
|
@ -10,32 +10,23 @@ JX.behavior('aphront-drag-and-drop-textarea', function(config) {
|
|||
|
||||
var target = JX.$(config.target);
|
||||
|
||||
function onupload(f) {
|
||||
var ref = '{F' + f.getID() + '}';
|
||||
|
||||
// If we're inserting immediately after a "}" (usually, another file
|
||||
// reference), put some newlines before our token so that multiple file
|
||||
// uploads get laid out more nicely.
|
||||
var range = JX.TextAreaUtils.getSelectionRange(target);
|
||||
var before = target.value.substring(0, range.start);
|
||||
if (before.match(/\}$/)) {
|
||||
ref = '\n\n' + ref;
|
||||
}
|
||||
|
||||
JX.TextAreaUtils.setSelectionText(target, ref, false);
|
||||
}
|
||||
|
||||
if (JX.PhabricatorDragAndDropFileUpload.isSupported()) {
|
||||
var drop = new JX.PhabricatorDragAndDropFileUpload(target)
|
||||
.setURI(config.uri)
|
||||
.setChunkThreshold(config.chunkThreshold);
|
||||
|
||||
drop.listen('didBeginDrag', function() {
|
||||
JX.DOM.alterClass(target, config.activatedClass, true);
|
||||
});
|
||||
|
||||
drop.listen('didEndDrag', function() {
|
||||
JX.DOM.alterClass(target, config.activatedClass, false);
|
||||
});
|
||||
drop.listen('didUpload', onupload);
|
||||
|
||||
drop.listen('didUpload', function(file) {
|
||||
JX.TextAreaUtils.insertFileReference(target, file);
|
||||
});
|
||||
|
||||
drop.start();
|
||||
}
|
||||
|
||||
|
|
|
@ -30,14 +30,4 @@ JX.behavior('phabricator-keyboard-shortcuts', function(config) {
|
|||
})
|
||||
.register();
|
||||
|
||||
if (config.searchID) {
|
||||
desc = 'Give keyboard focus to the search box.';
|
||||
new JX.KeyboardShortcut('/', desc)
|
||||
.setHandler(function() {
|
||||
var search = JX.$(config.searchID);
|
||||
search.focus();
|
||||
search.select();
|
||||
})
|
||||
.register();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -194,7 +194,21 @@ JX.behavior('phabricator-remarkup-assist', function(config) {
|
|||
.start();
|
||||
break;
|
||||
case 'fa-cloud-upload':
|
||||
new JX.Workflow('/file/uploaddialog/').start();
|
||||
new JX.Workflow('/file/uploaddialog/')
|
||||
.setHandler(function(response) {
|
||||
var files = response.files;
|
||||
for (var ii = 0; ii < files.length; ii++) {
|
||||
var file = files[ii];
|
||||
|
||||
var upload = new JX.PhabricatorFileUpload()
|
||||
.setID(file.id)
|
||||
.setPHID(file.phid)
|
||||
.setURI(file.uri);
|
||||
|
||||
JX.TextAreaUtils.insertFileReference(area, upload);
|
||||
}
|
||||
})
|
||||
.start();
|
||||
break;
|
||||
case 'fa-arrows-alt':
|
||||
if (edit_mode == 'fa-arrows-alt') {
|
||||
|
|
80
webroot/rsrc/js/phui/behavior-phui-file-upload.js
Normal file
80
webroot/rsrc/js/phui/behavior-phui-file-upload.js
Normal file
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* @provides javelin-behavior-phui-file-upload
|
||||
* @requires javelin-behavior
|
||||
* javelin-stratcom
|
||||
* javelin-dom
|
||||
* phuix-dropdown-menu
|
||||
*/
|
||||
|
||||
JX.behavior('phui-file-upload', function(config) {
|
||||
|
||||
function startUpload(workflow, input) {
|
||||
var files = input.files;
|
||||
|
||||
if (!files || !files.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var state = {
|
||||
workflow: workflow,
|
||||
input: input,
|
||||
waiting: 0,
|
||||
phids: []
|
||||
};
|
||||
|
||||
var callback = JX.bind(null, didUpload, state);
|
||||
|
||||
var dummy = input;
|
||||
var uploader = new JX.PhabricatorDragAndDropFileUpload(dummy)
|
||||
.setURI(config.uploadURI)
|
||||
.setChunkThreshold(config.chunkThreshold);
|
||||
|
||||
uploader.listen('didUpload', callback);
|
||||
uploader.start();
|
||||
|
||||
workflow.pause();
|
||||
for (var ii = 0; ii < files.length; ii++) {
|
||||
state.waiting++;
|
||||
uploader.sendRequest(files[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
function didUpload(state, file) {
|
||||
state.phids.push(file.getPHID());
|
||||
state.waiting--;
|
||||
|
||||
if (state.waiting) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.workflow
|
||||
.addData(config.inputName, state.phids.join(', '))
|
||||
.resume();
|
||||
}
|
||||
|
||||
JX.Workflow.listen('start', function(workflow) {
|
||||
var form = workflow.getSourceForm();
|
||||
if (!form) {
|
||||
return;
|
||||
}
|
||||
|
||||
var input;
|
||||
try {
|
||||
input = JX.$(config.fileInputID);
|
||||
} catch (ex) {
|
||||
return;
|
||||
}
|
||||
|
||||
var local_form = JX.DOM.findAbove(input, 'form');
|
||||
if (!local_form) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (local_form !== form) {
|
||||
return;
|
||||
}
|
||||
|
||||
startUpload(workflow, input);
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in a new issue