1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-28 09:42:41 +01:00

(stable) Promote 2016 Week 22

This commit is contained in:
epriestley 2016-05-29 20:00:01 -07:00
commit 4c03f617fc
55 changed files with 1288 additions and 430 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -45,4 +45,8 @@ final class PhabricatorSearchThreeStateField
return null;
}
protected function newConduitParameterType() {
return new ConduitBoolParameterType();
}
}

View file

@ -32,6 +32,8 @@ final class PhabricatorSettingsApplication extends PhabricatorApplication {
'(?:(?P<id>\d+)/)?(?:panel/(?P<key>[^/]+)/)?'
=> 'PhabricatorSettingsMainController',
'adjust/' => 'PhabricatorSettingsAdjustController',
'timezone/(?P<offset>[^/]+)/'
=> 'PhabricatorSettingsTimezoneController',
),
);
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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://`.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

@ -2,10 +2,6 @@
* @provides phui-calendar-list-css
*/
.phui-calendar-list-container {
width: 300px;
}
.device-phone .phui-calendar-list-container {
width: auto;
}

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

@ -10,7 +10,7 @@
JX.install('Device', {
statics : {
_device : null,
_tabletBreakpoint: 768,
_tabletBreakpoint: 920,
setTabletBreakpoint: function(width) {
var self = JX.Device;

View file

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

View file

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

View file

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

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