mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-28 01:32:42 +01:00
(stable) Promote 2016 Week 43
This commit is contained in:
commit
5efbf4d74a
229 changed files with 5747 additions and 1488 deletions
|
@ -7,15 +7,16 @@
|
|||
*/
|
||||
return array(
|
||||
'names' => array(
|
||||
'conpherence.pkg.css' => '4601645d',
|
||||
'conpherence.pkg.js' => '11f3e07e',
|
||||
'core.pkg.css' => 'de918edf',
|
||||
'core.pkg.js' => '30185d95',
|
||||
'conpherence.pkg.css' => 'cea72e09',
|
||||
'conpherence.pkg.js' => '6249a1cf',
|
||||
'core.pkg.css' => '46d588e4',
|
||||
'core.pkg.js' => '035325a7',
|
||||
'darkconsole.pkg.js' => 'e7393ebb',
|
||||
'differential.pkg.css' => 'e1d704ce',
|
||||
'differential.pkg.js' => '634399e9',
|
||||
'diffusion.pkg.css' => '91c5d3a6',
|
||||
'diffusion.pkg.js' => '84c8f8fd',
|
||||
'favicon.ico' => '30672e08',
|
||||
'maniphest.pkg.css' => '4845691a',
|
||||
'maniphest.pkg.js' => '949a7498',
|
||||
'rsrc/css/aphront/aphront-bars.css' => '231ac33c',
|
||||
|
@ -42,18 +43,17 @@ return array(
|
|||
'rsrc/css/application/chatlog/chatlog.css' => 'd295b020',
|
||||
'rsrc/css/application/conduit/conduit-api.css' => '7bc725c4',
|
||||
'rsrc/css/application/config/config-options.css' => '0ede4c9b',
|
||||
'rsrc/css/application/config/config-page.css' => '8798e14f',
|
||||
'rsrc/css/application/config/config-page.css' => 'b80124ae',
|
||||
'rsrc/css/application/config/config-template.css' => '8f18fa41',
|
||||
'rsrc/css/application/config/setup-issue.css' => 'f794cfc3',
|
||||
'rsrc/css/application/config/unhandled-exception.css' => '4c96257a',
|
||||
'rsrc/css/application/conpherence/durable-column.css' => '44bcaa19',
|
||||
'rsrc/css/application/conpherence/header-pane.css' => '20a7028c',
|
||||
'rsrc/css/application/conpherence/durable-column.css' => 'd82e130c',
|
||||
'rsrc/css/application/conpherence/header-pane.css' => '1c81cda6',
|
||||
'rsrc/css/application/conpherence/menu.css' => '4f51db5a',
|
||||
'rsrc/css/application/conpherence/message-pane.css' => '0d7dff02',
|
||||
'rsrc/css/application/conpherence/message-pane.css' => '394ae8fa',
|
||||
'rsrc/css/application/conpherence/notification.css' => '965db05b',
|
||||
'rsrc/css/application/conpherence/participant-pane.css' => '7bba0b56',
|
||||
'rsrc/css/application/conpherence/transaction.css' => '46253e19',
|
||||
'rsrc/css/application/conpherence/update.css' => '53bc527a',
|
||||
'rsrc/css/application/conpherence/participant-pane.css' => 'ac1baaa8',
|
||||
'rsrc/css/application/conpherence/transaction.css' => '85129c68',
|
||||
'rsrc/css/application/contentsource/content-source-view.css' => '4b8b05d4',
|
||||
'rsrc/css/application/countdown/timer.css' => '16c52f5c',
|
||||
'rsrc/css/application/daemon/bulk-job.css' => 'df9c1d4a',
|
||||
|
@ -102,7 +102,7 @@ return array(
|
|||
'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5',
|
||||
'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '8d8b92cd',
|
||||
'rsrc/css/application/releeph/releeph-request-typeahead.css' => '667a48ae',
|
||||
'rsrc/css/application/search/application-search-view.css' => 'be6454ec',
|
||||
'rsrc/css/application/search/application-search-view.css' => '8452c849',
|
||||
'rsrc/css/application/search/search-results.css' => '7dea472c',
|
||||
'rsrc/css/application/slowvote/slowvote.css' => 'a94b7230',
|
||||
'rsrc/css/application/tokens/tokens.css' => '3d0f239e',
|
||||
|
@ -110,7 +110,7 @@ return array(
|
|||
'rsrc/css/core/core.css' => 'd0801452',
|
||||
'rsrc/css/core/remarkup.css' => 'cd912f2c',
|
||||
'rsrc/css/core/syntax.css' => '769d3498',
|
||||
'rsrc/css/core/z-index.css' => '0d4e5558',
|
||||
'rsrc/css/core/z-index.css' => 'd1270942',
|
||||
'rsrc/css/diviner/diviner-shared.css' => 'aa3656aa',
|
||||
'rsrc/css/font/font-aleo.css' => '8bdb2835',
|
||||
'rsrc/css/font/font-awesome.css' => '2b7ebbcc',
|
||||
|
@ -136,7 +136,7 @@ return array(
|
|||
'rsrc/css/phui/phui-document-pro.css' => 'ca1fed81',
|
||||
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
|
||||
'rsrc/css/phui/phui-document.css' => 'c32e8dec',
|
||||
'rsrc/css/phui/phui-feed-story.css' => 'aa49845d',
|
||||
'rsrc/css/phui/phui-feed-story.css' => '44a9c8e9',
|
||||
'rsrc/css/phui/phui-fontkit.css' => '9cda225e',
|
||||
'rsrc/css/phui/phui-form-view.css' => '9e22b190',
|
||||
'rsrc/css/phui/phui-form.css' => 'aac1d51d',
|
||||
|
@ -144,7 +144,7 @@ return array(
|
|||
'rsrc/css/phui/phui-header-view.css' => '06385974',
|
||||
'rsrc/css/phui/phui-hovercard.css' => 'de1a2119',
|
||||
'rsrc/css/phui/phui-icon-set-selector.css' => '1ab67aad',
|
||||
'rsrc/css/phui/phui-icon.css' => '9bab6f02',
|
||||
'rsrc/css/phui/phui-icon.css' => '417f80fb',
|
||||
'rsrc/css/phui/phui-image-mask.css' => 'a8498f9c',
|
||||
'rsrc/css/phui/phui-info-panel.css' => '27ea50a1',
|
||||
'rsrc/css/phui/phui-info-view.css' => '28efab79',
|
||||
|
@ -162,7 +162,7 @@ return array(
|
|||
'rsrc/css/phui/phui-status.css' => 'd5263e49',
|
||||
'rsrc/css/phui/phui-tag-view.css' => '6bbd83e2',
|
||||
'rsrc/css/phui/phui-timeline-view.css' => 'bc523970',
|
||||
'rsrc/css/phui/phui-two-column-view.css' => 'fcfbe347',
|
||||
'rsrc/css/phui/phui-two-column-view.css' => 'bbe32c23',
|
||||
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7',
|
||||
'rsrc/css/phui/workboards/phui-workboard.css' => 'e09eb53a',
|
||||
'rsrc/css/phui/workboards/phui-workcard.css' => '0c62d7c5',
|
||||
|
@ -273,71 +273,20 @@ return array(
|
|||
'rsrc/favicons/apple-touch-icon-60x60.png' => '8ff52925',
|
||||
'rsrc/favicons/apple-touch-icon-72x72.png' => 'a2bb65d6',
|
||||
'rsrc/favicons/apple-touch-icon-76x76.png' => '2d061a11',
|
||||
'rsrc/favicons/dark/apple-touch-icon-114x114.png' => 'd0c8978c',
|
||||
'rsrc/favicons/dark/apple-touch-icon-120x120.png' => '3a618bc0',
|
||||
'rsrc/favicons/dark/apple-touch-icon-144x144.png' => '92c1e188',
|
||||
'rsrc/favicons/dark/apple-touch-icon-152x152.png' => '7ce7e469',
|
||||
'rsrc/favicons/dark/apple-touch-icon-57x57.png' => 'e3f3f38b',
|
||||
'rsrc/favicons/dark/apple-touch-icon-60x60.png' => '1e0dcc72',
|
||||
'rsrc/favicons/dark/apple-touch-icon-72x72.png' => '7fb599b6',
|
||||
'rsrc/favicons/dark/apple-touch-icon-76x76.png' => '91146961',
|
||||
'rsrc/favicons/dark/favicon-128.png' => 'd6ac4346',
|
||||
'rsrc/favicons/dark/favicon-16x16.png' => '17434bb0',
|
||||
'rsrc/favicons/dark/favicon-196x196.png' => '5e06ee72',
|
||||
'rsrc/favicons/dark/favicon-32x32.png' => 'bdd7e16b',
|
||||
'rsrc/favicons/dark/favicon-96x96.png' => '0cf55978',
|
||||
'rsrc/favicons/dark/mstile-144x144.png' => '4dc9d42d',
|
||||
'rsrc/favicons/dark/mstile-150x150.png' => '2dc61c90',
|
||||
'rsrc/favicons/dark/mstile-310x150.png' => '4fe58ab2',
|
||||
'rsrc/favicons/dark/mstile-310x310.png' => 'e62c1677',
|
||||
'rsrc/favicons/dark/mstile-70x70.png' => '6d1f41b7',
|
||||
'rsrc/favicons/favicon-128.png' => '72f7e812',
|
||||
'rsrc/favicons/favicon-16x16.png' => 'fc6275ba',
|
||||
'rsrc/favicons/favicon-196x196.png' => '95db275e',
|
||||
'rsrc/favicons/favicon-32x32.png' => '5bd18b6c',
|
||||
'rsrc/favicons/favicon-96x96.png' => '7242c8e9',
|
||||
'rsrc/favicons/favicon-mention.ico' => '1fdd0fa4',
|
||||
'rsrc/favicons/favicon-message.ico' => '115bc010',
|
||||
'rsrc/favicons/favicon.ico' => 'cdb11121',
|
||||
'rsrc/favicons/mask-icon.svg' => 'e132a80f',
|
||||
'rsrc/favicons/mstile-144x144.png' => '310c2ee5',
|
||||
'rsrc/favicons/mstile-150x150.png' => '74bf5133',
|
||||
'rsrc/favicons/mstile-310x150.png' => '4a49d3ee',
|
||||
'rsrc/favicons/mstile-310x310.png' => 'a52ab264',
|
||||
'rsrc/favicons/mstile-70x70.png' => '5edce7b8',
|
||||
'rsrc/favicons/red/apple-touch-icon-114x114.png' => '91e37d1d',
|
||||
'rsrc/favicons/red/apple-touch-icon-120x120.png' => '66687533',
|
||||
'rsrc/favicons/red/apple-touch-icon-144x144.png' => 'bc06002c',
|
||||
'rsrc/favicons/red/apple-touch-icon-152x152.png' => 'a713de42',
|
||||
'rsrc/favicons/red/apple-touch-icon-57x57.png' => '4729688b',
|
||||
'rsrc/favicons/red/apple-touch-icon-60x60.png' => '07b9b609',
|
||||
'rsrc/favicons/red/apple-touch-icon-72x72.png' => 'b20c3698',
|
||||
'rsrc/favicons/red/apple-touch-icon-76x76.png' => 'c6e7dd5c',
|
||||
'rsrc/favicons/red/favicon-128.png' => 'e2b2f8fe',
|
||||
'rsrc/favicons/red/favicon-16x16.png' => '929fbceb',
|
||||
'rsrc/favicons/red/favicon-196x196.png' => '94c089a5',
|
||||
'rsrc/favicons/red/favicon-32x32.png' => '5848673e',
|
||||
'rsrc/favicons/red/favicon-96x96.png' => '895d54e8',
|
||||
'rsrc/favicons/red/mstile-144x144.png' => '448639f5',
|
||||
'rsrc/favicons/red/mstile-150x150.png' => 'c2ba45d0',
|
||||
'rsrc/favicons/red/mstile-310x150.png' => 'b0e50799',
|
||||
'rsrc/favicons/red/mstile-310x310.png' => '2475c5a5',
|
||||
'rsrc/favicons/red/mstile-70x70.png' => '49b197e8',
|
||||
'rsrc/favicons/yellow/apple-touch-icon-114x114.png' => '5271fb3f',
|
||||
'rsrc/favicons/yellow/apple-touch-icon-120x120.png' => '6c3d9bf9',
|
||||
'rsrc/favicons/yellow/apple-touch-icon-144x144.png' => '6484472c',
|
||||
'rsrc/favicons/yellow/apple-touch-icon-152x152.png' => 'e305dda8',
|
||||
'rsrc/favicons/yellow/apple-touch-icon-57x57.png' => 'fa6c43d4',
|
||||
'rsrc/favicons/yellow/apple-touch-icon-60x60.png' => '2673f162',
|
||||
'rsrc/favicons/yellow/apple-touch-icon-72x72.png' => '3ad8020c',
|
||||
'rsrc/favicons/yellow/apple-touch-icon-76x76.png' => '58cffd81',
|
||||
'rsrc/favicons/yellow/favicon-128.png' => '3b2f8233',
|
||||
'rsrc/favicons/yellow/favicon-16x16.png' => 'f3a90518',
|
||||
'rsrc/favicons/yellow/favicon-196x196.png' => '932c7c65',
|
||||
'rsrc/favicons/yellow/favicon-32x32.png' => '005c9f92',
|
||||
'rsrc/favicons/yellow/favicon-96x96.png' => '3ad9bfa9',
|
||||
'rsrc/favicons/yellow/mstile-144x144.png' => 'fc52335c',
|
||||
'rsrc/favicons/yellow/mstile-150x150.png' => '9e556f80',
|
||||
'rsrc/favicons/yellow/mstile-310x150.png' => '2c915073',
|
||||
'rsrc/favicons/yellow/mstile-310x310.png' => 'ee49978d',
|
||||
'rsrc/favicons/yellow/mstile-70x70.png' => '85c7c939',
|
||||
'rsrc/image/BFCFDA.png' => 'd5ec91f4',
|
||||
'rsrc/image/actions/edit.png' => '2fc41442',
|
||||
'rsrc/image/avatar.png' => 'e132bb6a',
|
||||
|
@ -426,7 +375,7 @@ return array(
|
|||
'rsrc/image/texture/table_header_hover.png' => '038ec3b9',
|
||||
'rsrc/image/texture/table_header_tall.png' => 'd56b434f',
|
||||
'rsrc/js/application/aphlict/Aphlict.js' => '5359e785',
|
||||
'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => '49e20786',
|
||||
'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => '2a171a9d',
|
||||
'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => 'fb20ac8d',
|
||||
'rsrc/js/application/aphlict/behavior-aphlict-status.js' => '5e2634b9',
|
||||
'rsrc/js/application/aphlict/behavior-desktop-notifications-control.js' => 'edd1ba66',
|
||||
|
@ -436,14 +385,14 @@ return array(
|
|||
'rsrc/js/application/calendar/behavior-month-view.js' => 'fe33e256',
|
||||
'rsrc/js/application/calendar/behavior-recurring-edit.js' => '5f1c4d5f',
|
||||
'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' => 'c5238acb',
|
||||
'rsrc/js/application/conpherence/behavior-menu.js' => '9eb55204',
|
||||
'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => '358c717b',
|
||||
'rsrc/js/application/conpherence/behavior-conpherence-search.js' => '9bbf3762',
|
||||
'rsrc/js/application/conpherence/behavior-durable-column.js' => 'aa3bd034',
|
||||
'rsrc/js/application/conpherence/behavior-menu.js' => '7524fcfa',
|
||||
'rsrc/js/application/conpherence/behavior-participant-pane.js' => '8604caa8',
|
||||
'rsrc/js/application/conpherence/behavior-pontificate.js' => 'f2e58483',
|
||||
'rsrc/js/application/conpherence/behavior-quicksand-blacklist.js' => '7927a7d3',
|
||||
'rsrc/js/application/conpherence/behavior-toggle-widget.js' => '9bdbbab0',
|
||||
'rsrc/js/application/conpherence/behavior-toggle-widget.js' => '3dbf94d5',
|
||||
'rsrc/js/application/countdown/timer.js' => 'e4cc26b3',
|
||||
'rsrc/js/application/daemon/behavior-bulk-job-reload.js' => 'edf8a145',
|
||||
'rsrc/js/application/dashboard/behavior-dashboard-async-panel.js' => '469c0d9e',
|
||||
|
@ -531,6 +480,7 @@ return array(
|
|||
'rsrc/js/core/Busy.js' => '59a7976a',
|
||||
'rsrc/js/core/DragAndDropFileUpload.js' => '58dea2fa',
|
||||
'rsrc/js/core/DraggableList.js' => '5a13c79f',
|
||||
'rsrc/js/core/Favicon.js' => '1fe2510c',
|
||||
'rsrc/js/core/FileUpload.js' => '680ea2c8',
|
||||
'rsrc/js/core/Hovercard.js' => '1bd28176',
|
||||
'rsrc/js/core/KeyboardShortcut.js' => '1ae869f2',
|
||||
|
@ -540,7 +490,7 @@ return array(
|
|||
'rsrc/js/core/Prefab.js' => 'cfd23f37',
|
||||
'rsrc/js/core/ShapedRequest.js' => '7cbe244b',
|
||||
'rsrc/js/core/TextAreaUtils.js' => '320810c8',
|
||||
'rsrc/js/core/Title.js' => 'df5e11d2',
|
||||
'rsrc/js/core/Title.js' => '485aaa6c',
|
||||
'rsrc/js/core/ToolTip.js' => '6323f942',
|
||||
'rsrc/js/core/behavior-active-nav.js' => 'e379b58e',
|
||||
'rsrc/js/core/behavior-audio-source.js' => '59b251eb',
|
||||
|
@ -556,7 +506,7 @@ return array(
|
|||
'rsrc/js/core/behavior-file-tree.js' => '88236f00',
|
||||
'rsrc/js/core/behavior-form.js' => '5c54cbf3',
|
||||
'rsrc/js/core/behavior-gesture.js' => '3ab51e2c',
|
||||
'rsrc/js/core/behavior-global-drag-and-drop.js' => 'c8e57404',
|
||||
'rsrc/js/core/behavior-global-drag-and-drop.js' => '960f6a39',
|
||||
'rsrc/js/core/behavior-high-security-warning.js' => 'a464fe03',
|
||||
'rsrc/js/core/behavior-history-install.js' => '7ee2b591',
|
||||
'rsrc/js/core/behavior-hovercard.js' => 'bcaccd64',
|
||||
|
@ -610,22 +560,21 @@ return array(
|
|||
'aphront-tokenizer-control-css' => '056da01b',
|
||||
'aphront-tooltip-css' => '1a07aea8',
|
||||
'aphront-typeahead-control-css' => 'd4f16145',
|
||||
'application-search-view-css' => 'be6454ec',
|
||||
'application-search-view-css' => '8452c849',
|
||||
'auth-css' => '0877ed6e',
|
||||
'bulk-job-css' => 'df9c1d4a',
|
||||
'changeset-view-manager' => 'a2828756',
|
||||
'conduit-api-css' => '7bc725c4',
|
||||
'config-options-css' => '0ede4c9b',
|
||||
'config-page-css' => '8798e14f',
|
||||
'conpherence-durable-column-view' => '44bcaa19',
|
||||
'conpherence-header-pane-css' => '20a7028c',
|
||||
'config-page-css' => 'b80124ae',
|
||||
'conpherence-durable-column-view' => 'd82e130c',
|
||||
'conpherence-header-pane-css' => '1c81cda6',
|
||||
'conpherence-menu-css' => '4f51db5a',
|
||||
'conpherence-message-pane-css' => '0d7dff02',
|
||||
'conpherence-message-pane-css' => '394ae8fa',
|
||||
'conpherence-notification-css' => '965db05b',
|
||||
'conpherence-participant-pane-css' => '7bba0b56',
|
||||
'conpherence-thread-manager' => '01774ab2',
|
||||
'conpherence-transaction-css' => '46253e19',
|
||||
'conpherence-update-css' => '53bc527a',
|
||||
'conpherence-participant-pane-css' => 'ac1baaa8',
|
||||
'conpherence-thread-manager' => '358c717b',
|
||||
'conpherence-transaction-css' => '85129c68',
|
||||
'd3' => 'a11a5ff2',
|
||||
'differential-changeset-view-css' => '9ef7d354',
|
||||
'differential-core-view-css' => '5b7b8ff4',
|
||||
|
@ -650,7 +599,7 @@ return array(
|
|||
'inline-comment-summary-css' => '51efda3a',
|
||||
'javelin-aphlict' => '5359e785',
|
||||
'javelin-behavior' => '61cbc29a',
|
||||
'javelin-behavior-aphlict-dropdown' => '49e20786',
|
||||
'javelin-behavior-aphlict-dropdown' => '2a171a9d',
|
||||
'javelin-behavior-aphlict-listen' => 'fb20ac8d',
|
||||
'javelin-behavior-aphlict-status' => '5e2634b9',
|
||||
'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884',
|
||||
|
@ -665,10 +614,10 @@ return array(
|
|||
'javelin-behavior-choose-control' => '327a00d1',
|
||||
'javelin-behavior-comment-actions' => '0300eae6',
|
||||
'javelin-behavior-config-reorder-fields' => 'b6993408',
|
||||
'javelin-behavior-conpherence-drag-and-drop-photo' => 'cf86d16a',
|
||||
'javelin-behavior-conpherence-menu' => '9eb55204',
|
||||
'javelin-behavior-conpherence-menu' => '7524fcfa',
|
||||
'javelin-behavior-conpherence-participant-pane' => '8604caa8',
|
||||
'javelin-behavior-conpherence-pontificate' => 'f2e58483',
|
||||
'javelin-behavior-conpherence-search' => '9bbf3762',
|
||||
'javelin-behavior-countdown-timer' => 'e4cc26b3',
|
||||
'javelin-behavior-dark-console' => 'f411b6ae',
|
||||
'javelin-behavior-dashboard-async-panel' => '469c0d9e',
|
||||
|
@ -697,13 +646,13 @@ return array(
|
|||
'javelin-behavior-diffusion-pull-lastmodified' => 'f01586dc',
|
||||
'javelin-behavior-doorkeeper-tag' => 'e5822781',
|
||||
'javelin-behavior-drydock-live-operation-status' => '901935ef',
|
||||
'javelin-behavior-durable-column' => 'c5238acb',
|
||||
'javelin-behavior-durable-column' => 'aa3bd034',
|
||||
'javelin-behavior-editengine-reorder-configs' => 'd7a74243',
|
||||
'javelin-behavior-editengine-reorder-fields' => 'b59e1e96',
|
||||
'javelin-behavior-error-log' => '6882e80a',
|
||||
'javelin-behavior-event-all-day' => '937bb700',
|
||||
'javelin-behavior-fancy-datepicker' => '568931f3',
|
||||
'javelin-behavior-global-drag-and-drop' => 'c8e57404',
|
||||
'javelin-behavior-global-drag-and-drop' => '960f6a39',
|
||||
'javelin-behavior-herald-rule-editor' => '7ebaeed3',
|
||||
'javelin-behavior-high-security-warning' => 'a464fe03',
|
||||
'javelin-behavior-history-install' => '7ee2b591',
|
||||
|
@ -774,7 +723,7 @@ return array(
|
|||
'javelin-behavior-test-payment-form' => 'fc91ab6c',
|
||||
'javelin-behavior-time-typeahead' => '522431f7',
|
||||
'javelin-behavior-toggle-class' => '92b9ec77',
|
||||
'javelin-behavior-toggle-widget' => '9bdbbab0',
|
||||
'javelin-behavior-toggle-widget' => '3dbf94d5',
|
||||
'javelin-behavior-typeahead-browse' => '635de1ec',
|
||||
'javelin-behavior-typeahead-search' => '93d0c9e3',
|
||||
'javelin-behavior-view-placeholder' => '47830651',
|
||||
|
@ -848,6 +797,7 @@ return array(
|
|||
'phabricator-drag-and-drop-file-upload' => '58dea2fa',
|
||||
'phabricator-draggable-list' => '5a13c79f',
|
||||
'phabricator-fatal-config-template-css' => '8f18fa41',
|
||||
'phabricator-favicon' => '1fe2510c',
|
||||
'phabricator-feed-css' => 'ecd4ec57',
|
||||
'phabricator-file-upload' => '680ea2c8',
|
||||
'phabricator-filetree-view-css' => 'fccf9f82',
|
||||
|
@ -869,7 +819,7 @@ return array(
|
|||
'phabricator-source-code-view-css' => 'cbeef983',
|
||||
'phabricator-standard-page-view' => '79176f5a',
|
||||
'phabricator-textareautils' => '320810c8',
|
||||
'phabricator-title' => 'df5e11d2',
|
||||
'phabricator-title' => '485aaa6c',
|
||||
'phabricator-tooltip' => '6323f942',
|
||||
'phabricator-ui-example-css' => '528b19de',
|
||||
'phabricator-uiexample-javelin-view' => 'd4a14807',
|
||||
|
@ -882,7 +832,7 @@ return array(
|
|||
'phabricator-uiexample-reactor-select' => 'a155550f',
|
||||
'phabricator-uiexample-reactor-sendclass' => '1def2711',
|
||||
'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
|
||||
'phabricator-zindex-css' => '0d4e5558',
|
||||
'phabricator-zindex-css' => 'd1270942',
|
||||
'phame-css' => '8efb0729',
|
||||
'pholio-css' => 'ca89d380',
|
||||
'pholio-edit-css' => '07676f51',
|
||||
|
@ -909,7 +859,7 @@ return array(
|
|||
'phui-document-summary-view-css' => '9ca48bdf',
|
||||
'phui-document-view-css' => 'c32e8dec',
|
||||
'phui-document-view-pro-css' => 'ca1fed81',
|
||||
'phui-feed-story-css' => 'aa49845d',
|
||||
'phui-feed-story-css' => '44a9c8e9',
|
||||
'phui-font-icon-base-css' => '870a7360',
|
||||
'phui-fontkit-css' => '9cda225e',
|
||||
'phui-form-css' => 'aac1d51d',
|
||||
|
@ -919,7 +869,7 @@ return array(
|
|||
'phui-hovercard' => '1bd28176',
|
||||
'phui-hovercard-view-css' => 'de1a2119',
|
||||
'phui-icon-set-selector-css' => '1ab67aad',
|
||||
'phui-icon-view-css' => '9bab6f02',
|
||||
'phui-icon-view-css' => '417f80fb',
|
||||
'phui-image-mask-css' => 'a8498f9c',
|
||||
'phui-info-panel-css' => '27ea50a1',
|
||||
'phui-info-view-css' => '28efab79',
|
||||
|
@ -939,7 +889,7 @@ return array(
|
|||
'phui-tag-view-css' => '6bbd83e2',
|
||||
'phui-theme-css' => '798c69b8',
|
||||
'phui-timeline-view-css' => 'bc523970',
|
||||
'phui-two-column-view-css' => 'fcfbe347',
|
||||
'phui-two-column-view-css' => 'bbe32c23',
|
||||
'phui-workboard-color-css' => 'ac6fe6a7',
|
||||
'phui-workboard-view-css' => 'e09eb53a',
|
||||
'phui-workcard-view-css' => '0c62d7c5',
|
||||
|
@ -976,17 +926,6 @@ return array(
|
|||
'javelin-request',
|
||||
'javelin-typeahead-source',
|
||||
),
|
||||
'01774ab2' => array(
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-stratcom',
|
||||
'javelin-install',
|
||||
'javelin-aphlict',
|
||||
'javelin-workflow',
|
||||
'javelin-router',
|
||||
'javelin-behavior-device',
|
||||
'javelin-vector',
|
||||
),
|
||||
'019f36c4' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
@ -1152,6 +1091,10 @@ return array(
|
|||
'javelin-uri',
|
||||
'javelin-routable',
|
||||
),
|
||||
'1fe2510c' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
),
|
||||
'21df4ff5' => array(
|
||||
'javelin-install',
|
||||
'javelin-workboard-card',
|
||||
|
@ -1171,6 +1114,17 @@ return array(
|
|||
'javelin-install',
|
||||
'javelin-util',
|
||||
),
|
||||
'2a171a9d' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-request',
|
||||
'javelin-stratcom',
|
||||
'javelin-vector',
|
||||
'javelin-dom',
|
||||
'javelin-uri',
|
||||
'javelin-behavior-device',
|
||||
'phabricator-title',
|
||||
'phabricator-favicon',
|
||||
),
|
||||
'2b8de964' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
|
@ -1199,6 +1153,17 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-workflow',
|
||||
),
|
||||
'358c717b' => array(
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-stratcom',
|
||||
'javelin-install',
|
||||
'javelin-aphlict',
|
||||
'javelin-workflow',
|
||||
'javelin-router',
|
||||
'javelin-behavior-device',
|
||||
'javelin-vector',
|
||||
),
|
||||
'3ab51e2c' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-behavior-device',
|
||||
|
@ -1215,6 +1180,13 @@ return array(
|
|||
'javelin-util',
|
||||
'javelin-uri',
|
||||
),
|
||||
'3dbf94d5' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-workflow',
|
||||
'javelin-stratcom',
|
||||
),
|
||||
'3f5d6dbf' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
@ -1272,6 +1244,9 @@ return array(
|
|||
'phabricator-drag-and-drop-file-upload',
|
||||
'phabricator-textareautils',
|
||||
),
|
||||
'485aaa6c' => array(
|
||||
'javelin-install',
|
||||
),
|
||||
'491416b3' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-uri',
|
||||
|
@ -1282,16 +1257,6 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
),
|
||||
'49e20786' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-request',
|
||||
'javelin-stratcom',
|
||||
'javelin-vector',
|
||||
'javelin-dom',
|
||||
'javelin-uri',
|
||||
'javelin-behavior-device',
|
||||
'phabricator-title',
|
||||
),
|
||||
'4a021c10' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
|
@ -1536,6 +1501,20 @@ return array(
|
|||
'javelin-vector',
|
||||
'javelin-dom',
|
||||
),
|
||||
'7524fcfa' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-stratcom',
|
||||
'javelin-workflow',
|
||||
'javelin-behavior-device',
|
||||
'javelin-history',
|
||||
'javelin-vector',
|
||||
'javelin-scrollbar',
|
||||
'phabricator-title',
|
||||
'phabricator-shaped-request',
|
||||
'conpherence-thread-manager',
|
||||
),
|
||||
'769d3498' => array(
|
||||
'syntax-default-css',
|
||||
),
|
||||
|
@ -1723,6 +1702,13 @@ return array(
|
|||
'javelin-dom',
|
||||
'phabricator-busy',
|
||||
),
|
||||
'960f6a39' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-uri',
|
||||
'javelin-mask',
|
||||
'phabricator-drag-and-drop-file-upload',
|
||||
),
|
||||
'988040b4' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
@ -1740,27 +1726,13 @@ return array(
|
|||
'phabricator-phtize',
|
||||
'changeset-view-manager',
|
||||
),
|
||||
'9bdbbab0' => array(
|
||||
'9bbf3762' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-workflow',
|
||||
'javelin-stratcom',
|
||||
),
|
||||
'9eb55204' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-stratcom',
|
||||
'javelin-workflow',
|
||||
'javelin-behavior-device',
|
||||
'javelin-history',
|
||||
'javelin-vector',
|
||||
'javelin-scrollbar',
|
||||
'phabricator-title',
|
||||
'phabricator-shaped-request',
|
||||
'conpherence-thread-manager',
|
||||
),
|
||||
'9ef7d354' => array(
|
||||
'phui-inline-comment-view-css',
|
||||
),
|
||||
|
@ -1834,6 +1806,16 @@ return array(
|
|||
'javelin-util',
|
||||
'phabricator-prefab',
|
||||
),
|
||||
'aa3bd034' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
'javelin-behavior-device',
|
||||
'javelin-scrollbar',
|
||||
'javelin-quicksand',
|
||||
'phabricator-keyboard-shortcut',
|
||||
'conpherence-thread-manager',
|
||||
),
|
||||
'ab2f381b' => array(
|
||||
'javelin-request',
|
||||
'javelin-behavior',
|
||||
|
@ -1961,29 +1943,12 @@ return array(
|
|||
'javelin-install',
|
||||
'javelin-dom',
|
||||
),
|
||||
'c5238acb' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
'javelin-behavior-device',
|
||||
'javelin-scrollbar',
|
||||
'javelin-quicksand',
|
||||
'phabricator-keyboard-shortcut',
|
||||
'conpherence-thread-manager',
|
||||
),
|
||||
'c587b80f' => array(
|
||||
'javelin-install',
|
||||
),
|
||||
'c7ccd872' => array(
|
||||
'phui-fontkit-css',
|
||||
),
|
||||
'c8e57404' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-uri',
|
||||
'javelin-mask',
|
||||
'phabricator-drag-and-drop-file-upload',
|
||||
),
|
||||
'c90a04fc' => array(
|
||||
'javelin-dom',
|
||||
'javelin-dynval',
|
||||
|
@ -2010,12 +1975,6 @@ return array(
|
|||
'javelin-util',
|
||||
'phabricator-notification-css',
|
||||
),
|
||||
'cf86d16a' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-workflow',
|
||||
'phabricator-drag-and-drop-file-upload',
|
||||
),
|
||||
'cfd23f37' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
|
@ -2093,9 +2052,6 @@ return array(
|
|||
'javelin-typeahead-ondemand-source',
|
||||
'javelin-dom',
|
||||
),
|
||||
'df5e11d2' => array(
|
||||
'javelin-install',
|
||||
),
|
||||
'e0ec7f2f' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
@ -2300,12 +2256,10 @@ return array(
|
|||
'conpherence-message-pane-css',
|
||||
'conpherence-notification-css',
|
||||
'conpherence-transaction-css',
|
||||
'conpherence-update-css',
|
||||
'conpherence-participant-pane-css',
|
||||
'conpherence-header-pane-css',
|
||||
),
|
||||
'conpherence.pkg.js' => array(
|
||||
'javelin-behavior-conpherence-drag-and-drop-photo',
|
||||
'javelin-behavior-conpherence-menu',
|
||||
'javelin-behavior-conpherence-participant-pane',
|
||||
'javelin-behavior-conpherence-pontificate',
|
||||
|
|
|
@ -157,12 +157,10 @@ return array(
|
|||
'conpherence-message-pane-css',
|
||||
'conpherence-notification-css',
|
||||
'conpherence-transaction-css',
|
||||
'conpherence-update-css',
|
||||
'conpherence-participant-pane-css',
|
||||
'conpherence-header-pane-css',
|
||||
),
|
||||
'conpherence.pkg.js' => array(
|
||||
'javelin-behavior-conpherence-drag-and-drop-photo',
|
||||
'javelin-behavior-conpherence-menu',
|
||||
'javelin-behavior-conpherence-participant-pane',
|
||||
'javelin-behavior-conpherence-pontificate',
|
||||
|
|
11
resources/sql/autopatches/20161011.conpherence.ngrams.php
Normal file
11
resources/sql/autopatches/20161011.conpherence.ngrams.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
$table = new ConpherenceThread();
|
||||
|
||||
foreach (new LiskMigrationIterator($table) as $thread) {
|
||||
PhabricatorSearchWorker::queueDocumentForIndexing(
|
||||
$thread->getPHID(),
|
||||
array(
|
||||
'force' => true,
|
||||
));
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
CREATE TABLE {$NAMESPACE}_conpherence.conpherence_threadtitle_ngrams (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
objectID INT UNSIGNED NOT NULL,
|
||||
ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
KEY `key_object` (objectID),
|
||||
KEY `key_ngram` (ngram, objectID)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
15
resources/sql/autopatches/20161012.cal.01.import.sql
Normal file
15
resources/sql/autopatches/20161012.cal.01.import.sql
Normal file
|
@ -0,0 +1,15 @@
|
|||
CREATE TABLE {$NAMESPACE}_calendar.calendar_import (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
name LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
authorPHID VARBINARY(64) NOT NULL,
|
||||
viewPolicy VARBINARY(64) NOT NULL,
|
||||
editPolicy VARBINARY(64) NOT NULL,
|
||||
engineType VARCHAR(64) NOT NULL,
|
||||
parameters LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
isDisabled BOOL NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_phid` (phid),
|
||||
KEY `key_author` (authorPHID)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
19
resources/sql/autopatches/20161012.cal.02.importxaction.sql
Normal file
19
resources/sql/autopatches/20161012.cal.02.importxaction.sql
Normal file
|
@ -0,0 +1,19 @@
|
|||
CREATE TABLE {$NAMESPACE}_calendar.calendar_importtransaction (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
authorPHID VARBINARY(64) NOT NULL,
|
||||
objectPHID VARBINARY(64) NOT NULL,
|
||||
viewPolicy VARBINARY(64) NOT NULL,
|
||||
editPolicy VARBINARY(64) NOT NULL,
|
||||
commentPHID VARBINARY(64) DEFAULT NULL,
|
||||
commentVersion INT UNSIGNED NOT NULL,
|
||||
transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_phid` (`phid`),
|
||||
KEY `key_object` (`objectPHID`)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
11
resources/sql/autopatches/20161012.cal.03.eventimport.sql
Normal file
11
resources/sql/autopatches/20161012.cal.03.eventimport.sql
Normal file
|
@ -0,0 +1,11 @@
|
|||
ALTER TABLE {$NAMESPACE}_calendar.calendar_event
|
||||
ADD importAuthorPHID VARBINARY(64);
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_calendar.calendar_event
|
||||
ADD importSourcePHID VARBINARY(64);
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_calendar.calendar_event
|
||||
ADD importUIDIndex BINARY(12);
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_calendar.calendar_event
|
||||
ADD importUID LONGTEXT COLLATE {$COLLATE_TEXT};
|
8
resources/sql/autopatches/20161013.cal.01.importlog.sql
Normal file
8
resources/sql/autopatches/20161013.cal.01.importlog.sql
Normal file
|
@ -0,0 +1,8 @@
|
|||
CREATE TABLE {$NAMESPACE}_calendar.calendar_importlog (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
importPHID VARBINARY(64) NOT NULL,
|
||||
parameters LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
KEY `key_import` (`importPHID`)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_conpherence.conpherence_thread
|
||||
DROP COLUMN imagePHIDs;
|
|
@ -4,6 +4,11 @@
|
|||
$root = dirname(dirname(dirname(__FILE__)));
|
||||
require_once $root.'/scripts/__init_script__.php';
|
||||
|
||||
$cache = PhabricatorCaches::getMutableCache();
|
||||
$authfile_key = PhabricatorAuthSSHKeyQuery::AUTHFILE_CACHEKEY;
|
||||
$authfile = $cache->getKey($authfile_key);
|
||||
|
||||
if ($authfile === null) {
|
||||
$keys = id(new PhabricatorAuthSSHKeyQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withIsActive(true)
|
||||
|
@ -73,5 +78,10 @@ foreach ($keys as $ssh_key) {
|
|||
$lines[] = $options.' '.$type.' '.$key."\n";
|
||||
}
|
||||
|
||||
echo implode('', $lines);
|
||||
$authfile = implode('', $lines);
|
||||
$ttl = phutil_units('24 hours in seconds');
|
||||
$cache->setKey($authfile_key, $authfile, $ttl);
|
||||
}
|
||||
|
||||
echo $authfile;
|
||||
exit(0);
|
||||
|
|
|
@ -140,6 +140,7 @@ phutil_register_library_map(array(
|
|||
'AphrontDialogView' => 'view/AphrontDialogView.php',
|
||||
'AphrontEpochHTTPParameterType' => 'aphront/httpparametertype/AphrontEpochHTTPParameterType.php',
|
||||
'AphrontException' => 'aphront/exception/AphrontException.php',
|
||||
'AphrontFileHTTPParameterType' => 'aphront/httpparametertype/AphrontFileHTTPParameterType.php',
|
||||
'AphrontFileResponse' => 'aphront/response/AphrontFileResponse.php',
|
||||
'AphrontFormCheckboxControl' => 'view/form/control/AphrontFormCheckboxControl.php',
|
||||
'AphrontFormControl' => 'view/form/control/AphrontFormControl.php',
|
||||
|
@ -290,7 +291,6 @@ phutil_register_library_map(array(
|
|||
'ConpherenceDAO' => 'applications/conpherence/storage/ConpherenceDAO.php',
|
||||
'ConpherenceDurableColumnView' => 'applications/conpherence/view/ConpherenceDurableColumnView.php',
|
||||
'ConpherenceEditor' => 'applications/conpherence/editor/ConpherenceEditor.php',
|
||||
'ConpherenceFormDragAndDropUploadControl' => 'applications/conpherence/view/ConpherenceFormDragAndDropUploadControl.php',
|
||||
'ConpherenceFulltextQuery' => 'applications/conpherence/query/ConpherenceFulltextQuery.php',
|
||||
'ConpherenceIndex' => 'applications/conpherence/storage/ConpherenceIndex.php',
|
||||
'ConpherenceLayoutView' => 'applications/conpherence/view/ConpherenceLayoutView.php',
|
||||
|
@ -313,13 +313,16 @@ phutil_register_library_map(array(
|
|||
'ConpherenceSchemaSpec' => 'applications/conpherence/storage/ConpherenceSchemaSpec.php',
|
||||
'ConpherenceTestCase' => 'applications/conpherence/__tests__/ConpherenceTestCase.php',
|
||||
'ConpherenceThread' => 'applications/conpherence/storage/ConpherenceThread.php',
|
||||
'ConpherenceThreadDatasource' => 'applications/conpherence/typeahead/ConpherenceThreadDatasource.php',
|
||||
'ConpherenceThreadIndexEngineExtension' => 'applications/conpherence/engineextension/ConpherenceThreadIndexEngineExtension.php',
|
||||
'ConpherenceThreadListView' => 'applications/conpherence/view/ConpherenceThreadListView.php',
|
||||
'ConpherenceThreadMailReceiver' => 'applications/conpherence/mail/ConpherenceThreadMailReceiver.php',
|
||||
'ConpherenceThreadMembersPolicyRule' => 'applications/conpherence/policyrule/ConpherenceThreadMembersPolicyRule.php',
|
||||
'ConpherenceThreadQuery' => 'applications/conpherence/query/ConpherenceThreadQuery.php',
|
||||
'ConpherenceThreadRemarkupRule' => 'applications/conpherence/remarkup/ConpherenceThreadRemarkupRule.php',
|
||||
'ConpherenceThreadSearchController' => 'applications/conpherence/controller/ConpherenceThreadSearchController.php',
|
||||
'ConpherenceThreadSearchEngine' => 'applications/conpherence/query/ConpherenceThreadSearchEngine.php',
|
||||
'ConpherenceThreadTitleNgrams' => 'applications/conpherence/storage/ConpherenceThreadTitleNgrams.php',
|
||||
'ConpherenceTransaction' => 'applications/conpherence/storage/ConpherenceTransaction.php',
|
||||
'ConpherenceTransactionComment' => 'applications/conpherence/storage/ConpherenceTransactionComment.php',
|
||||
'ConpherenceTransactionQuery' => 'applications/conpherence/query/ConpherenceTransactionQuery.php',
|
||||
|
@ -919,6 +922,7 @@ phutil_register_library_map(array(
|
|||
'DrydockAuthorizationListView' => 'applications/drydock/view/DrydockAuthorizationListView.php',
|
||||
'DrydockAuthorizationPHIDType' => 'applications/drydock/phid/DrydockAuthorizationPHIDType.php',
|
||||
'DrydockAuthorizationQuery' => 'applications/drydock/query/DrydockAuthorizationQuery.php',
|
||||
'DrydockAuthorizationSearchConduitAPIMethod' => 'applications/drydock/conduit/DrydockAuthorizationSearchConduitAPIMethod.php',
|
||||
'DrydockAuthorizationSearchEngine' => 'applications/drydock/query/DrydockAuthorizationSearchEngine.php',
|
||||
'DrydockAuthorizationViewController' => 'applications/drydock/controller/DrydockAuthorizationViewController.php',
|
||||
'DrydockBlueprint' => 'applications/drydock/storage/DrydockBlueprint.php',
|
||||
|
@ -936,6 +940,7 @@ phutil_register_library_map(array(
|
|||
'DrydockBlueprintNameNgrams' => 'applications/drydock/storage/DrydockBlueprintNameNgrams.php',
|
||||
'DrydockBlueprintPHIDType' => 'applications/drydock/phid/DrydockBlueprintPHIDType.php',
|
||||
'DrydockBlueprintQuery' => 'applications/drydock/query/DrydockBlueprintQuery.php',
|
||||
'DrydockBlueprintSearchConduitAPIMethod' => 'applications/drydock/conduit/DrydockBlueprintSearchConduitAPIMethod.php',
|
||||
'DrydockBlueprintSearchEngine' => 'applications/drydock/query/DrydockBlueprintSearchEngine.php',
|
||||
'DrydockBlueprintTransaction' => 'applications/drydock/storage/DrydockBlueprintTransaction.php',
|
||||
'DrydockBlueprintTransactionQuery' => 'applications/drydock/query/DrydockBlueprintTransactionQuery.php',
|
||||
|
@ -2095,8 +2100,49 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCalendarExportViewController' => 'applications/calendar/controller/PhabricatorCalendarExportViewController.php',
|
||||
'PhabricatorCalendarHoliday' => 'applications/calendar/storage/PhabricatorCalendarHoliday.php',
|
||||
'PhabricatorCalendarHolidayTestCase' => 'applications/calendar/storage/__tests__/PhabricatorCalendarHolidayTestCase.php',
|
||||
'PhabricatorCalendarICSFileImportEngine' => 'applications/calendar/import/PhabricatorCalendarICSFileImportEngine.php',
|
||||
'PhabricatorCalendarICSImportEngine' => 'applications/calendar/import/PhabricatorCalendarICSImportEngine.php',
|
||||
'PhabricatorCalendarICSURIImportEngine' => 'applications/calendar/import/PhabricatorCalendarICSURIImportEngine.php',
|
||||
'PhabricatorCalendarICSWriter' => 'applications/calendar/util/PhabricatorCalendarICSWriter.php',
|
||||
'PhabricatorCalendarIconSet' => 'applications/calendar/icon/PhabricatorCalendarIconSet.php',
|
||||
'PhabricatorCalendarImport' => 'applications/calendar/storage/PhabricatorCalendarImport.php',
|
||||
'PhabricatorCalendarImportDefaultLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportDefaultLogType.php',
|
||||
'PhabricatorCalendarImportDeleteController' => 'applications/calendar/controller/PhabricatorCalendarImportDeleteController.php',
|
||||
'PhabricatorCalendarImportDeleteTransaction' => 'applications/calendar/xaction/PhabricatorCalendarImportDeleteTransaction.php',
|
||||
'PhabricatorCalendarImportDisableController' => 'applications/calendar/controller/PhabricatorCalendarImportDisableController.php',
|
||||
'PhabricatorCalendarImportDisableTransaction' => 'applications/calendar/xaction/PhabricatorCalendarImportDisableTransaction.php',
|
||||
'PhabricatorCalendarImportDropController' => 'applications/calendar/controller/PhabricatorCalendarImportDropController.php',
|
||||
'PhabricatorCalendarImportDuplicateLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportDuplicateLogType.php',
|
||||
'PhabricatorCalendarImportEditController' => 'applications/calendar/controller/PhabricatorCalendarImportEditController.php',
|
||||
'PhabricatorCalendarImportEditEngine' => 'applications/calendar/editor/PhabricatorCalendarImportEditEngine.php',
|
||||
'PhabricatorCalendarImportEditor' => 'applications/calendar/editor/PhabricatorCalendarImportEditor.php',
|
||||
'PhabricatorCalendarImportEmptyLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportEmptyLogType.php',
|
||||
'PhabricatorCalendarImportEngine' => 'applications/calendar/import/PhabricatorCalendarImportEngine.php',
|
||||
'PhabricatorCalendarImportEpochLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportEpochLogType.php',
|
||||
'PhabricatorCalendarImportFetchLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportFetchLogType.php',
|
||||
'PhabricatorCalendarImportFrequencyLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportFrequencyLogType.php',
|
||||
'PhabricatorCalendarImportICSFileTransaction' => 'applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php',
|
||||
'PhabricatorCalendarImportICSLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportICSLogType.php',
|
||||
'PhabricatorCalendarImportICSURITransaction' => 'applications/calendar/xaction/PhabricatorCalendarImportICSURITransaction.php',
|
||||
'PhabricatorCalendarImportIgnoredNodeLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportIgnoredNodeLogType.php',
|
||||
'PhabricatorCalendarImportListController' => 'applications/calendar/controller/PhabricatorCalendarImportListController.php',
|
||||
'PhabricatorCalendarImportLog' => 'applications/calendar/storage/PhabricatorCalendarImportLog.php',
|
||||
'PhabricatorCalendarImportLogListController' => 'applications/calendar/controller/PhabricatorCalendarImportLogListController.php',
|
||||
'PhabricatorCalendarImportLogQuery' => 'applications/calendar/query/PhabricatorCalendarImportLogQuery.php',
|
||||
'PhabricatorCalendarImportLogSearchEngine' => 'applications/calendar/query/PhabricatorCalendarImportLogSearchEngine.php',
|
||||
'PhabricatorCalendarImportLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportLogType.php',
|
||||
'PhabricatorCalendarImportLogView' => 'applications/calendar/view/PhabricatorCalendarImportLogView.php',
|
||||
'PhabricatorCalendarImportNameTransaction' => 'applications/calendar/xaction/PhabricatorCalendarImportNameTransaction.php',
|
||||
'PhabricatorCalendarImportOriginalLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportOriginalLogType.php',
|
||||
'PhabricatorCalendarImportOrphanLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportOrphanLogType.php',
|
||||
'PhabricatorCalendarImportPHIDType' => 'applications/calendar/phid/PhabricatorCalendarImportPHIDType.php',
|
||||
'PhabricatorCalendarImportQuery' => 'applications/calendar/query/PhabricatorCalendarImportQuery.php',
|
||||
'PhabricatorCalendarImportSearchEngine' => 'applications/calendar/query/PhabricatorCalendarImportSearchEngine.php',
|
||||
'PhabricatorCalendarImportTransaction' => 'applications/calendar/storage/PhabricatorCalendarImportTransaction.php',
|
||||
'PhabricatorCalendarImportTransactionQuery' => 'applications/calendar/query/PhabricatorCalendarImportTransactionQuery.php',
|
||||
'PhabricatorCalendarImportTransactionType' => 'applications/calendar/xaction/PhabricatorCalendarImportTransactionType.php',
|
||||
'PhabricatorCalendarImportUpdateLogType' => 'applications/calendar/importlog/PhabricatorCalendarImportUpdateLogType.php',
|
||||
'PhabricatorCalendarImportViewController' => 'applications/calendar/controller/PhabricatorCalendarImportViewController.php',
|
||||
'PhabricatorCalendarRemarkupRule' => 'applications/calendar/remarkup/PhabricatorCalendarRemarkupRule.php',
|
||||
'PhabricatorCalendarReplyHandler' => 'applications/calendar/mail/PhabricatorCalendarReplyHandler.php',
|
||||
'PhabricatorCalendarSchemaSpec' => 'applications/calendar/storage/PhabricatorCalendarSchemaSpec.php',
|
||||
|
@ -2571,6 +2617,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFileDeleteController' => 'applications/files/controller/PhabricatorFileDeleteController.php',
|
||||
'PhabricatorFileDropUploadController' => 'applications/files/controller/PhabricatorFileDropUploadController.php',
|
||||
'PhabricatorFileEditController' => 'applications/files/controller/PhabricatorFileEditController.php',
|
||||
'PhabricatorFileEditField' => 'applications/transactions/editfield/PhabricatorFileEditField.php',
|
||||
'PhabricatorFileEditor' => 'applications/files/editor/PhabricatorFileEditor.php',
|
||||
'PhabricatorFileExternalRequest' => 'applications/files/storage/PhabricatorFileExternalRequest.php',
|
||||
'PhabricatorFileExternalRequestGarbageCollector' => 'applications/files/garbagecollector/PhabricatorFileExternalRequestGarbageCollector.php',
|
||||
|
@ -2993,20 +3040,30 @@ phutil_register_library_map(array(
|
|||
'PhabricatorOwnersListController' => 'applications/owners/controller/PhabricatorOwnersListController.php',
|
||||
'PhabricatorOwnersOwner' => 'applications/owners/storage/PhabricatorOwnersOwner.php',
|
||||
'PhabricatorOwnersPackage' => 'applications/owners/storage/PhabricatorOwnersPackage.php',
|
||||
'PhabricatorOwnersPackageAuditingTransaction' => 'applications/owners/xaction/PhabricatorOwnersPackageAuditingTransaction.php',
|
||||
'PhabricatorOwnersPackageAutoreviewTransaction' => 'applications/owners/xaction/PhabricatorOwnersPackageAutoreviewTransaction.php',
|
||||
'PhabricatorOwnersPackageDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php',
|
||||
'PhabricatorOwnersPackageDescriptionTransaction' => 'applications/owners/xaction/PhabricatorOwnersPackageDescriptionTransaction.php',
|
||||
'PhabricatorOwnersPackageDominionTransaction' => 'applications/owners/xaction/PhabricatorOwnersPackageDominionTransaction.php',
|
||||
'PhabricatorOwnersPackageEditEngine' => 'applications/owners/editor/PhabricatorOwnersPackageEditEngine.php',
|
||||
'PhabricatorOwnersPackageFulltextEngine' => 'applications/owners/query/PhabricatorOwnersPackageFulltextEngine.php',
|
||||
'PhabricatorOwnersPackageFunctionDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php',
|
||||
'PhabricatorOwnersPackageNameNgrams' => 'applications/owners/storage/PhabricatorOwnersPackageNameNgrams.php',
|
||||
'PhabricatorOwnersPackageNameTransaction' => 'applications/owners/xaction/PhabricatorOwnersPackageNameTransaction.php',
|
||||
'PhabricatorOwnersPackageOwnerDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php',
|
||||
'PhabricatorOwnersPackageOwnersTransaction' => 'applications/owners/xaction/PhabricatorOwnersPackageOwnersTransaction.php',
|
||||
'PhabricatorOwnersPackagePHIDType' => 'applications/owners/phid/PhabricatorOwnersPackagePHIDType.php',
|
||||
'PhabricatorOwnersPackagePathsTransaction' => 'applications/owners/xaction/PhabricatorOwnersPackagePathsTransaction.php',
|
||||
'PhabricatorOwnersPackagePrimaryTransaction' => 'applications/owners/xaction/PhabricatorOwnersPackagePrimaryTransaction.php',
|
||||
'PhabricatorOwnersPackageQuery' => 'applications/owners/query/PhabricatorOwnersPackageQuery.php',
|
||||
'PhabricatorOwnersPackageRemarkupRule' => 'applications/owners/remarkup/PhabricatorOwnersPackageRemarkupRule.php',
|
||||
'PhabricatorOwnersPackageSearchEngine' => 'applications/owners/query/PhabricatorOwnersPackageSearchEngine.php',
|
||||
'PhabricatorOwnersPackageStatusTransaction' => 'applications/owners/xaction/PhabricatorOwnersPackageStatusTransaction.php',
|
||||
'PhabricatorOwnersPackageTestCase' => 'applications/owners/storage/__tests__/PhabricatorOwnersPackageTestCase.php',
|
||||
'PhabricatorOwnersPackageTransaction' => 'applications/owners/storage/PhabricatorOwnersPackageTransaction.php',
|
||||
'PhabricatorOwnersPackageTransactionEditor' => 'applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php',
|
||||
'PhabricatorOwnersPackageTransactionQuery' => 'applications/owners/query/PhabricatorOwnersPackageTransactionQuery.php',
|
||||
'PhabricatorOwnersPackageTransactionType' => 'applications/owners/xaction/PhabricatorOwnersPackageTransactionType.php',
|
||||
'PhabricatorOwnersPath' => 'applications/owners/storage/PhabricatorOwnersPath.php',
|
||||
'PhabricatorOwnersPathsController' => 'applications/owners/controller/PhabricatorOwnersPathsController.php',
|
||||
'PhabricatorOwnersPathsSearchEngineAttachment' => 'applications/owners/engineextension/PhabricatorOwnersPathsSearchEngineAttachment.php',
|
||||
|
@ -3751,6 +3808,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSystemDAO' => 'applications/system/storage/PhabricatorSystemDAO.php',
|
||||
'PhabricatorSystemDestructionGarbageCollector' => 'applications/system/garbagecollector/PhabricatorSystemDestructionGarbageCollector.php',
|
||||
'PhabricatorSystemDestructionLog' => 'applications/system/storage/PhabricatorSystemDestructionLog.php',
|
||||
'PhabricatorSystemFaviconController' => 'applications/system/controller/PhabricatorSystemFaviconController.php',
|
||||
'PhabricatorSystemReadOnlyController' => 'applications/system/controller/PhabricatorSystemReadOnlyController.php',
|
||||
'PhabricatorSystemRemoveDestroyWorkflow' => 'applications/system/management/PhabricatorSystemRemoveDestroyWorkflow.php',
|
||||
'PhabricatorSystemRemoveLogWorkflow' => 'applications/system/management/PhabricatorSystemRemoveLogWorkflow.php',
|
||||
|
@ -4627,6 +4685,7 @@ phutil_register_library_map(array(
|
|||
),
|
||||
'AphrontEpochHTTPParameterType' => 'AphrontHTTPParameterType',
|
||||
'AphrontException' => 'Exception',
|
||||
'AphrontFileHTTPParameterType' => 'AphrontHTTPParameterType',
|
||||
'AphrontFileResponse' => 'AphrontResponse',
|
||||
'AphrontFormCheckboxControl' => 'AphrontFormControl',
|
||||
'AphrontFormControl' => 'AphrontView',
|
||||
|
@ -4785,7 +4844,6 @@ phutil_register_library_map(array(
|
|||
'ConpherenceDAO' => 'PhabricatorLiskDAO',
|
||||
'ConpherenceDurableColumnView' => 'AphrontTagView',
|
||||
'ConpherenceEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'ConpherenceFormDragAndDropUploadControl' => 'AphrontFormControl',
|
||||
'ConpherenceFulltextQuery' => 'PhabricatorOffsetPagedQuery',
|
||||
'ConpherenceIndex' => 'ConpherenceDAO',
|
||||
'ConpherenceLayoutView' => 'AphrontTagView',
|
||||
|
@ -4813,14 +4871,18 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorMentionableInterface',
|
||||
'PhabricatorDestructibleInterface',
|
||||
'PhabricatorNgramsInterface',
|
||||
),
|
||||
'ConpherenceThreadDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'ConpherenceThreadIndexEngineExtension' => 'PhabricatorIndexEngineExtension',
|
||||
'ConpherenceThreadListView' => 'AphrontView',
|
||||
'ConpherenceThreadMailReceiver' => 'PhabricatorObjectMailReceiver',
|
||||
'ConpherenceThreadMembersPolicyRule' => 'PhabricatorPolicyRule',
|
||||
'ConpherenceThreadQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'ConpherenceThreadRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
||||
'ConpherenceThreadSearchController' => 'ConpherenceController',
|
||||
'ConpherenceThreadSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'ConpherenceThreadTitleNgrams' => 'PhabricatorSearchNgrams',
|
||||
'ConpherenceTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'ConpherenceTransactionComment' => 'PhabricatorApplicationTransactionComment',
|
||||
'ConpherenceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
|
@ -5468,12 +5530,14 @@ phutil_register_library_map(array(
|
|||
'DrydockAuthorization' => array(
|
||||
'DrydockDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorConduitResultInterface',
|
||||
),
|
||||
'DrydockAuthorizationAuthorizeController' => 'DrydockController',
|
||||
'DrydockAuthorizationListController' => 'DrydockController',
|
||||
'DrydockAuthorizationListView' => 'AphrontView',
|
||||
'DrydockAuthorizationPHIDType' => 'PhabricatorPHIDType',
|
||||
'DrydockAuthorizationQuery' => 'DrydockQuery',
|
||||
'DrydockAuthorizationSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
||||
'DrydockAuthorizationSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'DrydockAuthorizationViewController' => 'DrydockController',
|
||||
'DrydockBlueprint' => array(
|
||||
|
@ -5483,6 +5547,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCustomFieldInterface',
|
||||
'PhabricatorNgramsInterface',
|
||||
'PhabricatorProjectInterface',
|
||||
'PhabricatorConduitResultInterface',
|
||||
),
|
||||
'DrydockBlueprintController' => 'DrydockController',
|
||||
'DrydockBlueprintCoreCustomField' => array(
|
||||
|
@ -5501,6 +5566,7 @@ phutil_register_library_map(array(
|
|||
'DrydockBlueprintNameNgrams' => 'PhabricatorSearchNgrams',
|
||||
'DrydockBlueprintPHIDType' => 'PhabricatorPHIDType',
|
||||
'DrydockBlueprintQuery' => 'DrydockQuery',
|
||||
'DrydockBlueprintSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
||||
'DrydockBlueprintSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'DrydockBlueprintTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'DrydockBlueprintTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
|
@ -6781,6 +6847,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCalendarEvent' => array(
|
||||
'PhabricatorCalendarDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorExtendedPolicyInterface',
|
||||
'PhabricatorProjectInterface',
|
||||
'PhabricatorMarkupInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
|
@ -6869,8 +6936,58 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCalendarExportViewController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarHoliday' => 'PhabricatorCalendarDAO',
|
||||
'PhabricatorCalendarHolidayTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorCalendarICSFileImportEngine' => 'PhabricatorCalendarICSImportEngine',
|
||||
'PhabricatorCalendarICSImportEngine' => 'PhabricatorCalendarImportEngine',
|
||||
'PhabricatorCalendarICSURIImportEngine' => 'PhabricatorCalendarICSImportEngine',
|
||||
'PhabricatorCalendarICSWriter' => 'Phobject',
|
||||
'PhabricatorCalendarIconSet' => 'PhabricatorIconSet',
|
||||
'PhabricatorCalendarImport' => array(
|
||||
'PhabricatorCalendarDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorDestructibleInterface',
|
||||
),
|
||||
'PhabricatorCalendarImportDefaultLogType' => 'PhabricatorCalendarImportLogType',
|
||||
'PhabricatorCalendarImportDeleteController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarImportDeleteTransaction' => 'PhabricatorCalendarImportTransactionType',
|
||||
'PhabricatorCalendarImportDisableController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarImportDisableTransaction' => 'PhabricatorCalendarImportTransactionType',
|
||||
'PhabricatorCalendarImportDropController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarImportDuplicateLogType' => 'PhabricatorCalendarImportLogType',
|
||||
'PhabricatorCalendarImportEditController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarImportEditEngine' => 'PhabricatorEditEngine',
|
||||
'PhabricatorCalendarImportEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'PhabricatorCalendarImportEmptyLogType' => 'PhabricatorCalendarImportLogType',
|
||||
'PhabricatorCalendarImportEngine' => 'Phobject',
|
||||
'PhabricatorCalendarImportEpochLogType' => 'PhabricatorCalendarImportLogType',
|
||||
'PhabricatorCalendarImportFetchLogType' => 'PhabricatorCalendarImportLogType',
|
||||
'PhabricatorCalendarImportFrequencyLogType' => 'PhabricatorCalendarImportLogType',
|
||||
'PhabricatorCalendarImportICSFileTransaction' => 'PhabricatorCalendarImportTransactionType',
|
||||
'PhabricatorCalendarImportICSLogType' => 'PhabricatorCalendarImportLogType',
|
||||
'PhabricatorCalendarImportICSURITransaction' => 'PhabricatorCalendarImportTransactionType',
|
||||
'PhabricatorCalendarImportIgnoredNodeLogType' => 'PhabricatorCalendarImportLogType',
|
||||
'PhabricatorCalendarImportListController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarImportLog' => array(
|
||||
'PhabricatorCalendarDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorDestructibleInterface',
|
||||
),
|
||||
'PhabricatorCalendarImportLogListController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarImportLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorCalendarImportLogSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'PhabricatorCalendarImportLogType' => 'Phobject',
|
||||
'PhabricatorCalendarImportLogView' => 'AphrontView',
|
||||
'PhabricatorCalendarImportNameTransaction' => 'PhabricatorCalendarImportTransactionType',
|
||||
'PhabricatorCalendarImportOriginalLogType' => 'PhabricatorCalendarImportLogType',
|
||||
'PhabricatorCalendarImportOrphanLogType' => 'PhabricatorCalendarImportLogType',
|
||||
'PhabricatorCalendarImportPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorCalendarImportQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorCalendarImportSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'PhabricatorCalendarImportTransaction' => 'PhabricatorModularTransaction',
|
||||
'PhabricatorCalendarImportTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'PhabricatorCalendarImportTransactionType' => 'PhabricatorModularTransactionType',
|
||||
'PhabricatorCalendarImportUpdateLogType' => 'PhabricatorCalendarImportLogType',
|
||||
'PhabricatorCalendarImportViewController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
||||
'PhabricatorCalendarReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
|
||||
'PhabricatorCalendarSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
|
@ -7418,6 +7535,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFileDeleteController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileDropUploadController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileEditController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorFileEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'PhabricatorFileExternalRequest' => array(
|
||||
'PhabricatorFileDAO',
|
||||
|
@ -7892,20 +8010,30 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFulltextInterface',
|
||||
'PhabricatorNgramsInterface',
|
||||
),
|
||||
'PhabricatorOwnersPackageAuditingTransaction' => 'PhabricatorOwnersPackageTransactionType',
|
||||
'PhabricatorOwnersPackageAutoreviewTransaction' => 'PhabricatorOwnersPackageTransactionType',
|
||||
'PhabricatorOwnersPackageDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'PhabricatorOwnersPackageDescriptionTransaction' => 'PhabricatorOwnersPackageTransactionType',
|
||||
'PhabricatorOwnersPackageDominionTransaction' => 'PhabricatorOwnersPackageTransactionType',
|
||||
'PhabricatorOwnersPackageEditEngine' => 'PhabricatorEditEngine',
|
||||
'PhabricatorOwnersPackageFulltextEngine' => 'PhabricatorFulltextEngine',
|
||||
'PhabricatorOwnersPackageFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'PhabricatorOwnersPackageNameNgrams' => 'PhabricatorSearchNgrams',
|
||||
'PhabricatorOwnersPackageNameTransaction' => 'PhabricatorOwnersPackageTransactionType',
|
||||
'PhabricatorOwnersPackageOwnerDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'PhabricatorOwnersPackageOwnersTransaction' => 'PhabricatorOwnersPackageTransactionType',
|
||||
'PhabricatorOwnersPackagePHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorOwnersPackagePathsTransaction' => 'PhabricatorOwnersPackageTransactionType',
|
||||
'PhabricatorOwnersPackagePrimaryTransaction' => 'PhabricatorOwnersPackageTransactionType',
|
||||
'PhabricatorOwnersPackageQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorOwnersPackageRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
||||
'PhabricatorOwnersPackageSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'PhabricatorOwnersPackageStatusTransaction' => 'PhabricatorOwnersPackageTransactionType',
|
||||
'PhabricatorOwnersPackageTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorOwnersPackageTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'PhabricatorOwnersPackageTransaction' => 'PhabricatorModularTransaction',
|
||||
'PhabricatorOwnersPackageTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'PhabricatorOwnersPackageTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'PhabricatorOwnersPackageTransactionType' => 'PhabricatorModularTransactionType',
|
||||
'PhabricatorOwnersPath' => 'PhabricatorOwnersDAO',
|
||||
'PhabricatorOwnersPathsController' => 'PhabricatorOwnersController',
|
||||
'PhabricatorOwnersPathsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
||||
|
@ -8815,6 +8943,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSystemDAO' => 'PhabricatorLiskDAO',
|
||||
'PhabricatorSystemDestructionGarbageCollector' => 'PhabricatorGarbageCollector',
|
||||
'PhabricatorSystemDestructionLog' => 'PhabricatorSystemDAO',
|
||||
'PhabricatorSystemFaviconController' => 'PhabricatorController',
|
||||
'PhabricatorSystemReadOnlyController' => 'PhabricatorController',
|
||||
'PhabricatorSystemRemoveDestroyWorkflow' => 'PhabricatorSystemRemoveWorkflow',
|
||||
'PhabricatorSystemRemoveLogWorkflow' => 'PhabricatorSystemRemoveWorkflow',
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
final class AphrontFileHTTPParameterType
|
||||
extends AphrontHTTPParameterType {
|
||||
|
||||
private function getFileKey($key) {
|
||||
return $key.'_raw';
|
||||
}
|
||||
|
||||
protected function getParameterExists(AphrontRequest $request, $key) {
|
||||
$file_key = $this->getFileKey($key);
|
||||
return $request->getExists($key) ||
|
||||
$request->getFileExists($file_key);
|
||||
}
|
||||
|
||||
protected function getParameterValue(AphrontRequest $request, $key) {
|
||||
$value = $request->getStrList($key);
|
||||
if ($value) {
|
||||
return head($value);
|
||||
}
|
||||
|
||||
// NOTE: At least for now, we'll attempt to read a direct upload if we
|
||||
// miss on a PHID. Currently, PHUIFormFileControl does a client-side
|
||||
// upload on workflow forms (which is good) but doesn't have a hook for
|
||||
// non-workflow forms (which isn't as good). Giving it a hook is desirable,
|
||||
// but complicated. Even if we do hook it, it may be reasonable to keep
|
||||
// this code around as a fallback if the client-side JS goes awry.
|
||||
|
||||
$file_key = $this->getFileKey($key);
|
||||
if (!$request->getFileExists($file_key)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$viewer = $this->getViewer();
|
||||
$file = PhabricatorFile::newFromPHPUpload(
|
||||
idx($_FILES, $file_key),
|
||||
array(
|
||||
'authorPHID' => $viewer->getPHID(),
|
||||
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
|
||||
));
|
||||
return $file->getPHID();
|
||||
}
|
||||
|
||||
protected function getParameterTypeName() {
|
||||
return 'file';
|
||||
}
|
||||
|
||||
protected function getParameterFormatDescriptions() {
|
||||
return array(
|
||||
pht('A file PHID.'),
|
||||
);
|
||||
}
|
||||
|
||||
protected function getParameterExamples() {
|
||||
return array(
|
||||
'v=PHID-FILE-wxyz',
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -191,6 +191,20 @@ final class PhabricatorAuthSSHKeyEditor
|
|||
return 'ssh-key-'.$object->getPHID();
|
||||
}
|
||||
|
||||
protected function applyFinalEffects(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
// After making any change to an SSH key, drop the authfile cache so it
|
||||
// is regenerated the next time anyone authenticates.
|
||||
$cache = PhabricatorCaches::getMutableCache();
|
||||
$authfile_key = PhabricatorAuthSSHKeyQuery::AUTHFILE_CACHEKEY;
|
||||
$cache->deleteKey($authfile_key);
|
||||
|
||||
return $xactions;
|
||||
}
|
||||
|
||||
|
||||
protected function getMailTo(PhabricatorLiskDAO $object) {
|
||||
return $object->getObject()->getSSHKeyNotifyPHIDs();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
final class PhabricatorAuthSSHKeyQuery
|
||||
extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||
|
||||
const AUTHFILE_CACHEKEY = 'ssh.authfile';
|
||||
|
||||
private $ids;
|
||||
private $phids;
|
||||
private $objectPHIDs;
|
||||
|
|
17
src/applications/cache/PhabricatorCaches.php
vendored
17
src/applications/cache/PhabricatorCaches.php
vendored
|
@ -99,6 +99,23 @@ final class PhabricatorCaches extends Phobject {
|
|||
return $caches;
|
||||
}
|
||||
|
||||
public static function getMutableCache() {
|
||||
static $cache;
|
||||
if (!$cache) {
|
||||
$caches = self::buildMutableCaches();
|
||||
$cache = self::newStackFromCaches($caches);
|
||||
}
|
||||
return $cache;
|
||||
}
|
||||
|
||||
private static function buildMutableCaches() {
|
||||
$caches = array();
|
||||
|
||||
$caches[] = new PhabricatorKeyValueDatabaseCache();
|
||||
|
||||
return $caches;
|
||||
}
|
||||
|
||||
|
||||
/* -( Repository Graph Cache )--------------------------------------------- */
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ final class PhabricatorKeyValueDatabaseCache
|
|||
$this->establishConnection('w'),
|
||||
'DELETE FROM %T WHERE cacheKeyHash IN (%Ls)',
|
||||
$this->getTableName(),
|
||||
$keys);
|
||||
$map);
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
|
|
@ -73,7 +73,24 @@ final class PhabricatorCalendarApplication extends PhabricatorApplication {
|
|||
=> 'PhabricatorCalendarExportICSController',
|
||||
'disable/(?P<id>[1-9]\d*)/'
|
||||
=> 'PhabricatorCalendarExportDisableController',
|
||||
|
||||
),
|
||||
'import/' => array(
|
||||
$this->getQueryRoutePattern()
|
||||
=> 'PhabricatorCalendarImportListController',
|
||||
$this->getEditRoutePattern('edit/')
|
||||
=> 'PhabricatorCalendarImportEditController',
|
||||
'(?P<id>[1-9]\d*)/'
|
||||
=> 'PhabricatorCalendarImportViewController',
|
||||
'disable/(?P<id>[1-9]\d*)/'
|
||||
=> 'PhabricatorCalendarImportDisableController',
|
||||
'delete/(?P<id>[1-9]\d*)/'
|
||||
=> 'PhabricatorCalendarImportDeleteController',
|
||||
'drop/'
|
||||
=> 'PhabricatorCalendarImportDropController',
|
||||
'log/' => array(
|
||||
$this->getQueryRoutePattern()
|
||||
=> 'PhabricatorCalendarImportLogListController',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -18,4 +18,23 @@ abstract class PhabricatorCalendarController extends PhabricatorController {
|
|||
->setContent($ics_data);
|
||||
}
|
||||
|
||||
protected function newImportedEventResponse(PhabricatorCalendarEvent $event) {
|
||||
if (!$event->isImportedEvent()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Give the user a specific, detailed message if they try to edit an
|
||||
// imported event via common web paths. Other edits (including those via
|
||||
// the API) are blocked by the normal policy system, but this makes it more
|
||||
// clear exactly why the event can't be edited.
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Can Not Edit Imported Event'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'This event has been imported from an external source and '.
|
||||
'can not be edited.'))
|
||||
->addCancelButton($event->getURI(), pht('Done'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,19 +7,27 @@ final class PhabricatorCalendarEventCancelController
|
|||
$viewer = $request->getViewer();
|
||||
$id = $request->getURIData('id');
|
||||
|
||||
// Just check CAN_VIEW first. Then we'll check if this is an import so
|
||||
// we can raise a better error.
|
||||
$event = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
if (!$event) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$response = $this->newImportedEventResponse($event);
|
||||
if ($response) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Now that we've done the import check, check for CAN_EDIT.
|
||||
PhabricatorPolicyFilter::requireCapability(
|
||||
$viewer,
|
||||
$event,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
$cancel_uri = $event->getURI();
|
||||
|
||||
$is_parent = $event->isParentEvent();
|
||||
|
|
|
@ -4,6 +4,20 @@ final class PhabricatorCalendarEventEditController
|
|||
extends PhabricatorCalendarController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$id = $request->getURIData('id');
|
||||
if ($id) {
|
||||
$event = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
$response = $this->newImportedEventResponse($event);
|
||||
if ($response) {
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
return id(new PhabricatorCalendarEventEditEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
|
|
|
@ -15,6 +15,11 @@ final class PhabricatorCalendarEventJoinController
|
|||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$response = $this->newImportedEventResponse($event);
|
||||
if ($response) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$cancel_uri = $event->getURI();
|
||||
|
||||
$action = $request->getURIData('action');
|
||||
|
|
|
@ -7,6 +7,10 @@ final class PhabricatorCalendarEventListController
|
|||
return true;
|
||||
}
|
||||
|
||||
public function isGlobalDragAndDropUploadEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$year = $request->getURIData('year');
|
||||
$month = $request->getURIData('month');
|
||||
|
@ -43,6 +47,10 @@ final class PhabricatorCalendarEventListController
|
|||
->setType(PHUIListItemView::TYPE_LABEL)
|
||||
->setName(pht('Import/Export'));
|
||||
|
||||
$items[] = id(new PHUIListItemView())
|
||||
->setName('Imports')
|
||||
->setHref('/calendar/import/');
|
||||
|
||||
$items[] = id(new PHUIListItemView())
|
||||
->setName('Exports')
|
||||
->setHref('/calendar/export/');
|
||||
|
|
|
@ -63,6 +63,19 @@ final class PhabricatorCalendarEventViewController
|
|||
->setHeader(pht('Details'));
|
||||
$recurring_header = $this->buildRecurringHeader($event);
|
||||
|
||||
// NOTE: This is a bit hacky: for imported events, we're just hiding the
|
||||
// comment form without actually preventing comments. Users could still
|
||||
// submit a request to add comments to these events. This isn't really a
|
||||
// major problem since they can't do anything truly bad and there isn't an
|
||||
// easy way to selectively disable this or some other similar behaviors
|
||||
// today, but it would probably be nice to fully disable these
|
||||
// "pseudo-edits" (like commenting and probably subscribing and awarding
|
||||
// tokens) at some point.
|
||||
if ($event->isImportedEvent()) {
|
||||
$comment_view = null;
|
||||
$timeline->setShouldTerminate(true);
|
||||
}
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setSubheader($subheader)
|
||||
|
@ -105,6 +118,16 @@ final class PhabricatorCalendarEventViewController
|
|||
->setPolicyObject($event)
|
||||
->setHeaderIcon($event->getIcon());
|
||||
|
||||
if ($event->isImportedEvent()) {
|
||||
$header->addTag(
|
||||
id(new PHUITagView())
|
||||
->setType(PHUITagView::TYPE_SHADE)
|
||||
->setName(pht('Imported'))
|
||||
->setIcon('fa-download')
|
||||
->setHref($event->getImportSource()->getURI())
|
||||
->setShade('orange'));
|
||||
}
|
||||
|
||||
foreach ($this->buildRSVPActions($event) as $action) {
|
||||
$header->addActionLink($action);
|
||||
}
|
||||
|
@ -141,12 +164,15 @@ final class PhabricatorCalendarEventViewController
|
|||
->setWorkflow(!$can_edit));
|
||||
}
|
||||
|
||||
$can_attend = !$event->isImportedEvent();
|
||||
|
||||
if ($is_attending) {
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Decline Event'))
|
||||
->setIcon('fa-user-times')
|
||||
->setHref($this->getApplicationURI("event/join/{$id}/"))
|
||||
->setDisabled(!$can_attend)
|
||||
->setWorkflow(true));
|
||||
} else {
|
||||
$curtain->addAction(
|
||||
|
@ -154,6 +180,7 @@ final class PhabricatorCalendarEventViewController
|
|||
->setName(pht('Join Event'))
|
||||
->setIcon('fa-user-plus')
|
||||
->setHref($this->getApplicationURI("event/join/{$id}/"))
|
||||
->setDisabled(!$can_attend)
|
||||
->setWorkflow(true));
|
||||
}
|
||||
|
||||
|
@ -261,6 +288,15 @@ final class PhabricatorCalendarEventViewController
|
|||
pht('None'));
|
||||
}
|
||||
|
||||
if ($event->isImportedEvent()) {
|
||||
$properties->addProperty(
|
||||
pht('Imported By'),
|
||||
pht(
|
||||
'%s from %s',
|
||||
$viewer->renderHandle($event->getImportAuthorPHID()),
|
||||
$viewer->renderHandle($event->getImportSourcePHID())));
|
||||
}
|
||||
|
||||
$properties->addProperty(
|
||||
pht('Invitees'),
|
||||
$invitee_list);
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportDeleteController
|
||||
extends PhabricatorCalendarController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
|
||||
$import = id(new PhabricatorCalendarImportQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($request->getURIData('id')))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
if (!$import) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$import_uri = $import->getURI();
|
||||
|
||||
$engine = $import->getEngine();
|
||||
if (!$engine->canDeleteAnyEvents($viewer, $import)) {
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('No Imported Events'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'No events from this source currently exist. They may have '.
|
||||
'failed to import, have been updated by another source, or '.
|
||||
'already have been deleted.'))
|
||||
->addCancelButton($import_uri, pht('Done'));
|
||||
}
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$xactions = array();
|
||||
$xactions[] = id(new PhabricatorCalendarImportTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarImportDeleteTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(true);
|
||||
|
||||
$editor = id(new PhabricatorCalendarImportEditor())
|
||||
->setActor($viewer)
|
||||
->setContinueOnNoEffect(true)
|
||||
->setContinueOnMissingFields(true)
|
||||
->setContentSourceFromRequest($request);
|
||||
|
||||
$editor->applyTransactions($import, $xactions);
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI($import_uri);
|
||||
}
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Delete Imported Events'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'Delete all the events that were imported from this source? '.
|
||||
'This action can not be undone.'))
|
||||
->addCancelButton($import_uri)
|
||||
->addSubmitButton(pht('Delete Events'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportDisableController
|
||||
extends PhabricatorCalendarController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
|
||||
$import = id(new PhabricatorCalendarImportQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($request->getURIData('id')))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
if (!$import) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$import_uri = $import->getURI();
|
||||
$is_disable = !$import->getIsDisabled();
|
||||
|
||||
if (!$import->getEngine()->canDisable($viewer, $import)) {
|
||||
$reason = $import->getEngine()->explainCanDisable($viewer, $import);
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Unable to Disable'))
|
||||
->appendParagraph($reason)
|
||||
->addCancelButton($import_uri);
|
||||
}
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$xactions = array();
|
||||
$xactions[] = id(new PhabricatorCalendarImportTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarImportDisableTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue($is_disable ? 1 : 0);
|
||||
|
||||
$editor = id(new PhabricatorCalendarImportEditor())
|
||||
->setActor($viewer)
|
||||
->setContinueOnNoEffect(true)
|
||||
->setContinueOnMissingFields(true)
|
||||
->setContentSourceFromRequest($request);
|
||||
|
||||
$editor->applyTransactions($import, $xactions);
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI($import_uri);
|
||||
}
|
||||
|
||||
if ($is_disable) {
|
||||
$title = pht('Disable Import');
|
||||
$body = pht(
|
||||
'Disable this import? Events from this source will no longer be '.
|
||||
'updated.');
|
||||
$button = pht('Disable Import');
|
||||
} else {
|
||||
$title = pht('Enable Import');
|
||||
$body = pht(
|
||||
'Enable this import? Events from this source will be updated again.');
|
||||
$button = pht('Enable Import');
|
||||
}
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle($title)
|
||||
->appendParagraph($body)
|
||||
->addCancelButton($import_uri)
|
||||
->addSubmitButton($button);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportDropController
|
||||
extends PhabricatorCalendarController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
|
||||
if (!$request->validateCSRF()) {
|
||||
return new Aphront400Response();
|
||||
}
|
||||
|
||||
$cancel_uri = $this->getApplicationURI();
|
||||
|
||||
$ids = $request->getStrList('h');
|
||||
if ($ids) {
|
||||
$files = id(new PhabricatorFileQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs($ids)
|
||||
->setRaisePolicyExceptions(true)
|
||||
->execute();
|
||||
} else {
|
||||
$files = array();
|
||||
}
|
||||
|
||||
if (!$files) {
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Nothing Uploaded'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'Drag and drop .ics files to upload them and import them into '.
|
||||
'Calendar.'))
|
||||
->addCancelButton($cancel_uri, pht('Done'));
|
||||
}
|
||||
|
||||
$engine = new PhabricatorCalendarICSImportEngine();
|
||||
$imports = array();
|
||||
foreach ($files as $file) {
|
||||
$import = PhabricatorCalendarImport::initializeNewCalendarImport(
|
||||
$viewer,
|
||||
clone $engine);
|
||||
|
||||
$xactions = array();
|
||||
$xactions[] = id(new PhabricatorCalendarImportTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarImportICSFileTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue($file->getPHID());
|
||||
|
||||
$editor = id(new PhabricatorCalendarImportEditor())
|
||||
->setActor($viewer)
|
||||
->setContinueOnNoEffect(true)
|
||||
->setContinueOnMissingFields(true)
|
||||
->setContentSourceFromRequest($request);
|
||||
|
||||
$editor->applyTransactions($import, $xactions);
|
||||
|
||||
$imports[] = $import;
|
||||
}
|
||||
|
||||
$import_phids = mpull($imports, 'getPHID');
|
||||
$events = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($viewer)
|
||||
->withImportSourcePHIDs($import_phids)
|
||||
->execute();
|
||||
|
||||
if (count($events) == 1) {
|
||||
// The user imported exactly one event. This is consistent with dropping
|
||||
// a .ics file from an email; just take them to the event.
|
||||
$event = head($events);
|
||||
$next_uri = $event->getURI();
|
||||
} else if (count($imports) > 1) {
|
||||
// The user imported multiple different files. Take them to a summary
|
||||
// list of generated import activity.
|
||||
$source_phids = implode(',', $import_phids);
|
||||
$next_uri = '/calendar/import/log/?importSourcePHIDs='.$source_phids;
|
||||
} else {
|
||||
// The user imported one file, which had zero or more than one event.
|
||||
// Take them to the import detail page.
|
||||
$import = head($imports);
|
||||
$next_uri = $import->getURI();
|
||||
}
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI($next_uri);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportEditController
|
||||
extends PhabricatorCalendarController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$engine = id(new PhabricatorCalendarImportEditEngine())
|
||||
->setController($this);
|
||||
|
||||
$id = $request->getURIData('id');
|
||||
if (!$id) {
|
||||
$list_uri = $this->getApplicationURI('import/');
|
||||
|
||||
$import_type = $request->getStr('importType');
|
||||
$import_engines = PhabricatorCalendarImportEngine::getAllImportEngines();
|
||||
if (empty($import_engines[$import_type])) {
|
||||
return $this->buildEngineTypeResponse($list_uri);
|
||||
}
|
||||
|
||||
$import_engine = $import_engines[$import_type];
|
||||
|
||||
$engine
|
||||
->addContextParameter('importType', $import_type)
|
||||
->setImportEngine($import_engine);
|
||||
}
|
||||
|
||||
return $engine->buildResponse();
|
||||
}
|
||||
|
||||
private function buildEngineTypeResponse($cancel_uri) {
|
||||
$import_engines = PhabricatorCalendarImportEngine::getAllImportEngines();
|
||||
|
||||
$request = $this->getRequest();
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$e_import = null;
|
||||
$errors = array();
|
||||
if ($request->isFormPost()) {
|
||||
$e_import = pht('Required');
|
||||
$errors[] = pht(
|
||||
'To import events, you must select a source to import from.');
|
||||
}
|
||||
|
||||
$type_control = id(new AphrontFormRadioButtonControl())
|
||||
->setLabel(pht('Import Type'))
|
||||
->setName('importType')
|
||||
->setError($e_import);
|
||||
|
||||
foreach ($import_engines as $import_engine) {
|
||||
$type_control->addButton(
|
||||
$import_engine->getImportEngineType(),
|
||||
$import_engine->getImportEngineName(),
|
||||
$import_engine->getImportEngineHint());
|
||||
}
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb(pht('New Import'));
|
||||
$crumbs->setBorder(true);
|
||||
|
||||
$title = pht('Choose Import Type');
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('New Import'))
|
||||
->setHeaderIcon('fa-upload');
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
->appendChild($type_control)
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue(pht('Continue'))
|
||||
->addCancelButton($cancel_uri));
|
||||
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setFormErrors($errors)
|
||||
->setHeaderText(pht('Import'))
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setForm($form);
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setFooter(
|
||||
array(
|
||||
$box,
|
||||
));
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
->setCrumbs($crumbs)
|
||||
->appendChild($view);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportListController
|
||||
extends PhabricatorCalendarController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
return id(new PhabricatorCalendarImportSearchEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
$crumbs->addAction(
|
||||
id(new PHUIListItemView())
|
||||
->setName(pht('Import Events'))
|
||||
->setHref($this->getApplicationURI('import/edit/'))
|
||||
->setIcon('fa-upload'));
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportLogListController
|
||||
extends PhabricatorCalendarController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
return id(new PhabricatorCalendarImportLogSearchEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportViewController
|
||||
extends PhabricatorCalendarController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
|
||||
$import = id(new PhabricatorCalendarImportQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($request->getURIData('id')))
|
||||
->executeOne();
|
||||
if (!$import) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb(
|
||||
pht('Imports'),
|
||||
'/calendar/import/');
|
||||
$crumbs->addTextCrumb(pht('Import %d', $import->getID()));
|
||||
$crumbs->setBorder(true);
|
||||
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$import,
|
||||
new PhabricatorCalendarImportTransactionQuery());
|
||||
$timeline->setShouldTerminate(true);
|
||||
|
||||
$header = $this->buildHeaderView($import);
|
||||
$curtain = $this->buildCurtain($import);
|
||||
$details = $this->buildPropertySection($import);
|
||||
|
||||
$log_messages = $this->buildLogMessages($import);
|
||||
$imported_events = $this->buildImportedEvents($import);
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setMainColumn(
|
||||
array(
|
||||
$log_messages,
|
||||
$imported_events,
|
||||
$timeline,
|
||||
))
|
||||
->setCurtain($curtain)
|
||||
->addPropertySection(pht('Details'), $details);
|
||||
|
||||
$page_title = pht(
|
||||
'Import %d %s',
|
||||
$import->getID(),
|
||||
$import->getDisplayName());
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($page_title)
|
||||
->setCrumbs($crumbs)
|
||||
->setPageObjectPHIDs(array($import->getPHID()))
|
||||
->appendChild($view);
|
||||
}
|
||||
|
||||
private function buildHeaderView(
|
||||
PhabricatorCalendarImport $import) {
|
||||
$viewer = $this->getViewer();
|
||||
$id = $import->getID();
|
||||
|
||||
if ($import->getIsDisabled()) {
|
||||
$icon = 'fa-ban';
|
||||
$color = 'red';
|
||||
$status = pht('Disabled');
|
||||
} else {
|
||||
$icon = 'fa-check';
|
||||
$color = 'bluegrey';
|
||||
$status = pht('Active');
|
||||
}
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setViewer($viewer)
|
||||
->setHeader($import->getDisplayName())
|
||||
->setStatus($icon, $color, $status)
|
||||
->setPolicyObject($import);
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
private function buildCurtain(PhabricatorCalendarImport $import) {
|
||||
$viewer = $this->getViewer();
|
||||
$id = $import->getID();
|
||||
|
||||
$curtain = $this->newCurtainView($import);
|
||||
$engine = $import->getEngine();
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$import,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
$edit_uri = "import/edit/{$id}/";
|
||||
$edit_uri = $this->getApplicationURI($edit_uri);
|
||||
|
||||
$can_disable = ($can_edit && $engine->canDisable($viewer, $import));
|
||||
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Edit Import'))
|
||||
->setIcon('fa-pencil')
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(!$can_edit)
|
||||
->setHref($edit_uri));
|
||||
|
||||
$disable_uri = "import/disable/{$id}/";
|
||||
$disable_uri = $this->getApplicationURI($disable_uri);
|
||||
if ($import->getIsDisabled()) {
|
||||
$disable_name = pht('Enable Import');
|
||||
$disable_icon = 'fa-check';
|
||||
} else {
|
||||
$disable_name = pht('Disable Import');
|
||||
$disable_icon = 'fa-ban';
|
||||
}
|
||||
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName($disable_name)
|
||||
->setIcon($disable_icon)
|
||||
->setDisabled(!$can_disable)
|
||||
->setWorkflow(true)
|
||||
->setHref($disable_uri));
|
||||
|
||||
|
||||
if ($can_edit) {
|
||||
$can_delete = $engine->canDeleteAnyEvents($viewer, $import);
|
||||
} else {
|
||||
$can_delete = false;
|
||||
}
|
||||
|
||||
$delete_uri = "import/delete/{$id}/";
|
||||
$delete_uri = $this->getApplicationURI($delete_uri);
|
||||
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Delete Imported Events'))
|
||||
->setIcon('fa-times')
|
||||
->setDisabled(!$can_delete)
|
||||
->setWorkflow(true)
|
||||
->setHref($delete_uri));
|
||||
|
||||
return $curtain;
|
||||
}
|
||||
|
||||
private function buildPropertySection(
|
||||
PhabricatorCalendarImport $import) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$properties = id(new PHUIPropertyListView())
|
||||
->setViewer($viewer);
|
||||
|
||||
$engine = $import->getEngine();
|
||||
|
||||
$properties->addProperty(
|
||||
pht('Source Type'),
|
||||
$engine->getImportEngineTypeName());
|
||||
|
||||
$engine->appendImportProperties(
|
||||
$viewer,
|
||||
$import,
|
||||
$properties);
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
private function buildLogMessages(PhabricatorCalendarImport $import) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$logs = id(new PhabricatorCalendarImportLogQuery())
|
||||
->setViewer($viewer)
|
||||
->withImportPHIDs(array($import->getPHID()))
|
||||
->setLimit(25)
|
||||
->execute();
|
||||
|
||||
$logs_view = id(new PhabricatorCalendarImportLogView())
|
||||
->setViewer($viewer)
|
||||
->setLogs($logs);
|
||||
|
||||
$all_uri = $this->getApplicationURI('import/log/');
|
||||
$all_uri = (string)id(new PhutilURI($all_uri))
|
||||
->setQueryParam('importSourcePHID', $import->getPHID());
|
||||
|
||||
$all_button = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setText(pht('View All'))
|
||||
->setIcon('fa-search')
|
||||
->setHref($all_uri);
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Log Messages'))
|
||||
->addActionLink($all_button);
|
||||
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setTable($logs_view);
|
||||
}
|
||||
|
||||
private function buildImportedEvents(PhabricatorCalendarImport $import) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$engine = id(new PhabricatorCalendarEventSearchEngine())
|
||||
->setViewer($viewer);
|
||||
|
||||
$saved = $engine->newSavedQuery()
|
||||
->setParameter('importSourcePHIDs', array($import->getPHID()));
|
||||
|
||||
$pager = $engine->newPagerForSavedQuery($saved);
|
||||
$pager->setPageSize(25);
|
||||
|
||||
$query = $engine->buildQueryFromSavedQuery($saved);
|
||||
|
||||
$results = $engine->executeQuery($query, $pager);
|
||||
$view = $engine->renderResults($results, $saved);
|
||||
$list = $view->getObjectList();
|
||||
$list->setNoDataString(pht('No imported events.'));
|
||||
|
||||
$all_uri = $this->getApplicationURI();
|
||||
$all_uri = (string)id(new PhutilURI($all_uri))
|
||||
->setQueryParam('importSourcePHID', $import->getPHID())
|
||||
->setQueryParam('display', 'list');
|
||||
|
||||
$all_button = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setText(pht('View All'))
|
||||
->setIcon('fa-search')
|
||||
->setHref($all_uri);
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Imported Events'))
|
||||
->addActionLink($all_button);
|
||||
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setObjectList($list);
|
||||
}
|
||||
|
||||
}
|
|
@ -200,6 +200,11 @@ final class PhabricatorCalendarEventEditor
|
|||
protected function shouldPublishFeedStory(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
if ($object->isImportedEvent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -210,6 +215,11 @@ final class PhabricatorCalendarEventEditor
|
|||
protected function shouldSendMail(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
if ($object->isImportedEvent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportEditEngine
|
||||
extends PhabricatorEditEngine {
|
||||
|
||||
const ENGINECONST = 'calendar.import';
|
||||
|
||||
private $importEngine;
|
||||
|
||||
public function setImportEngine(PhabricatorCalendarImportEngine $engine) {
|
||||
$this->importEngine = $engine;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getImportEngine() {
|
||||
return $this->importEngine;
|
||||
}
|
||||
|
||||
public function getEngineName() {
|
||||
return pht('Calendar Imports');
|
||||
}
|
||||
|
||||
public function isEngineConfigurable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getSummaryHeader() {
|
||||
return pht('Configure Calendar Import Forms');
|
||||
}
|
||||
|
||||
public function getSummaryText() {
|
||||
return pht('Configure how users create and edit imports.');
|
||||
}
|
||||
|
||||
public function getEngineApplicationClass() {
|
||||
return 'PhabricatorCalendarApplication';
|
||||
}
|
||||
|
||||
protected function newEditableObject() {
|
||||
$viewer = $this->getViewer();
|
||||
$engine = $this->getImportEngine();
|
||||
|
||||
return PhabricatorCalendarImport::initializeNewCalendarImport(
|
||||
$viewer,
|
||||
$engine);
|
||||
}
|
||||
|
||||
protected function newObjectQuery() {
|
||||
return new PhabricatorCalendarImportQuery();
|
||||
}
|
||||
|
||||
protected function getObjectCreateTitleText($object) {
|
||||
return pht('Create New Import');
|
||||
}
|
||||
|
||||
protected function getObjectEditTitleText($object) {
|
||||
return pht('Edit Import: %s', $object->getDisplayName());
|
||||
}
|
||||
|
||||
protected function getObjectEditShortText($object) {
|
||||
return pht('Import %d', $object->getID());
|
||||
}
|
||||
|
||||
protected function getObjectCreateShortText() {
|
||||
return pht('Create Import');
|
||||
}
|
||||
|
||||
protected function getObjectName() {
|
||||
return pht('Import');
|
||||
}
|
||||
|
||||
protected function getObjectViewURI($object) {
|
||||
return $object->getURI();
|
||||
}
|
||||
|
||||
protected function getEditorURI() {
|
||||
return $this->getApplication()->getApplicationURI('import/edit/');
|
||||
}
|
||||
|
||||
protected function buildCustomEditFields($object) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$fields = array(
|
||||
id(new PhabricatorTextEditField())
|
||||
->setKey('name')
|
||||
->setLabel(pht('Name'))
|
||||
->setDescription(pht('Name of the import.'))
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarImportNameTransaction::TRANSACTIONTYPE)
|
||||
->setConduitDescription(pht('Rename the import.'))
|
||||
->setConduitTypeDescription(pht('New import name.'))
|
||||
->setValue($object->getName()),
|
||||
id(new PhabricatorBoolEditField())
|
||||
->setKey('disabled')
|
||||
->setOptions(pht('Active'), pht('Disabled'))
|
||||
->setLabel(pht('Disabled'))
|
||||
->setDescription(pht('Disable the import.'))
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarImportDisableTransaction::TRANSACTIONTYPE)
|
||||
->setIsConduitOnly(true)
|
||||
->setConduitDescription(pht('Disable or restore the import.'))
|
||||
->setConduitTypeDescription(pht('True to cancel the import.'))
|
||||
->setValue($object->getIsDisabled()),
|
||||
);
|
||||
|
||||
$import_engine = $object->getEngine();
|
||||
foreach ($import_engine->newEditEngineFields($this, $object) as $field) {
|
||||
$fields[] = $field;
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportEditor
|
||||
extends PhabricatorApplicationTransactionEditor {
|
||||
|
||||
public function getEditorApplicationClass() {
|
||||
return 'PhabricatorCalendarApplication';
|
||||
}
|
||||
|
||||
public function getEditorObjectsDescription() {
|
||||
return pht('Calendar Imports');
|
||||
}
|
||||
|
||||
public function getCreateObjectTitle($author, $object) {
|
||||
return pht('%s created this import.', $author);
|
||||
}
|
||||
|
||||
public function getTransactionTypes() {
|
||||
$types = parent::getTransactionTypes();
|
||||
|
||||
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
protected function applyFinalEffects(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
if ($this->getIsNewObject()) {
|
||||
$actor = $this->getActor();
|
||||
|
||||
$import_engine = $object->getEngine();
|
||||
$import_engine->didCreateImport($actor, $object);
|
||||
}
|
||||
|
||||
return $xactions;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarICSFileImportEngine
|
||||
extends PhabricatorCalendarICSImportEngine {
|
||||
|
||||
const ENGINETYPE = 'icsfile';
|
||||
|
||||
public function getImportEngineName() {
|
||||
return pht('Import .ics File');
|
||||
}
|
||||
|
||||
public function getImportEngineTypeName() {
|
||||
return pht('.ics File');
|
||||
}
|
||||
|
||||
public function getImportEngineHint() {
|
||||
return pht('Import an event in ".ics" (iCalendar) format.');
|
||||
}
|
||||
|
||||
public function appendImportProperties(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImport $import,
|
||||
PHUIPropertyListView $properties) {
|
||||
|
||||
$phid_key = PhabricatorCalendarImportICSFileTransaction::PARAMKEY_FILE;
|
||||
$file_phid = $import->getParameter($phid_key);
|
||||
|
||||
$properties->addProperty(
|
||||
pht('Source File'),
|
||||
$viewer->renderHandle($file_phid));
|
||||
}
|
||||
|
||||
public function newEditEngineFields(
|
||||
PhabricatorEditEngine $engine,
|
||||
PhabricatorCalendarImport $import) {
|
||||
$fields = array();
|
||||
|
||||
if ($engine->getIsCreate()) {
|
||||
$fields[] = id(new PhabricatorFileEditField())
|
||||
->setKey('icsFilePHID')
|
||||
->setLabel(pht('ICS File'))
|
||||
->setDescription(pht('ICS file to import.'))
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarImportICSFileTransaction::TRANSACTIONTYPE)
|
||||
->setConduitDescription(pht('File PHID to import.'))
|
||||
->setConduitTypeDescription(pht('File PHID.'));
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function getDisplayName(PhabricatorCalendarImport $import) {
|
||||
$filename_key = PhabricatorCalendarImportICSFileTransaction::PARAMKEY_NAME;
|
||||
$filename = $import->getParameter($filename_key);
|
||||
if (strlen($filename)) {
|
||||
return pht('ICS File "%s"', $filename);
|
||||
} else {
|
||||
return pht('ICS File');
|
||||
}
|
||||
}
|
||||
|
||||
public function didCreateImport(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImport $import) {
|
||||
|
||||
$phid_key = PhabricatorCalendarImportICSFileTransaction::PARAMKEY_FILE;
|
||||
$file_phid = $import->getParameter($phid_key);
|
||||
|
||||
$file = id(new PhabricatorFileQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($file_phid))
|
||||
->executeOne();
|
||||
if (!$file) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unable to load file ("%s") for import.',
|
||||
$file_phid));
|
||||
}
|
||||
|
||||
$data = $file->loadFileData();
|
||||
|
||||
return $this->importICSData($viewer, $import, $data);
|
||||
}
|
||||
|
||||
|
||||
public function canDisable(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImport $import) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function explainCanDisable(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImport $import) {
|
||||
return pht(
|
||||
'You can not disable import of an ICS file because the entire import '.
|
||||
'occurs immediately when you upload the file. There is no further '.
|
||||
'activity to disable.');
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorCalendarICSImportEngine
|
||||
extends PhabricatorCalendarImportEngine {
|
||||
|
||||
final protected function importICSData(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImport $import,
|
||||
$data) {
|
||||
|
||||
$parser = new PhutilICSParser();
|
||||
|
||||
try {
|
||||
$document = $parser->parseICSData($data);
|
||||
} catch (PhutilICSParserException $ex) {
|
||||
// TODO: In theory, it would be nice to store these in a fully abstract
|
||||
// form so they can be translated at display time. As-is, we'll store the
|
||||
// error messages in whatever language we were using when the parser
|
||||
// failure occurred.
|
||||
|
||||
$import->newLogMessage(
|
||||
PhabricatorCalendarImportICSLogType::LOGTYPE,
|
||||
array(
|
||||
'ics.code' => $ex->getParserFailureCode(),
|
||||
'ics.message' => $ex->getMessage(),
|
||||
));
|
||||
|
||||
$document = null;
|
||||
}
|
||||
|
||||
return $this->importEventDocument($viewer, $import, $document);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarICSURIImportEngine
|
||||
extends PhabricatorCalendarICSImportEngine {
|
||||
|
||||
const ENGINETYPE = 'icsuri';
|
||||
|
||||
public function getImportEngineName() {
|
||||
return pht('Import .ics URI');
|
||||
}
|
||||
|
||||
public function getImportEngineTypeName() {
|
||||
return pht('.ics URI');
|
||||
}
|
||||
|
||||
public function getImportEngineHint() {
|
||||
return pht('Import or subscribe to a calendar in .ics format by URI.');
|
||||
}
|
||||
|
||||
public function appendImportProperties(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImport $import,
|
||||
PHUIPropertyListView $properties) {
|
||||
|
||||
$uri_key = PhabricatorCalendarImportICSURITransaction::PARAMKEY_URI;
|
||||
$uri = $import->getParameter($uri_key);
|
||||
|
||||
// Since the URI may contain a secret hash, don't show it to users who
|
||||
// can not edit the import.
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$import,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
if (!$can_edit) {
|
||||
$uri_display = phutil_tag('em', array(), pht('Restricted'));
|
||||
} else if (!PhabricatorEnv::isValidRemoteURIForLink($uri)) {
|
||||
$uri_display = $uri;
|
||||
} else {
|
||||
$uri_display = phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $uri,
|
||||
'target' => '_blank',
|
||||
),
|
||||
$uri);
|
||||
}
|
||||
|
||||
$properties->addProperty(pht('Source URI'), $uri_display);
|
||||
}
|
||||
|
||||
public function newEditEngineFields(
|
||||
PhabricatorEditEngine $engine,
|
||||
PhabricatorCalendarImport $import) {
|
||||
$fields = array();
|
||||
|
||||
if ($engine->getIsCreate()) {
|
||||
$fields[] = id(new PhabricatorTextEditField())
|
||||
->setKey('uri')
|
||||
->setLabel(pht('URI'))
|
||||
->setDescription(pht('URI to import.'))
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarImportICSURITransaction::TRANSACTIONTYPE)
|
||||
->setConduitDescription(pht('URI to import.'))
|
||||
->setConduitTypeDescription(pht('New URI.'));
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function getDisplayName(PhabricatorCalendarImport $import) {
|
||||
return pht('ICS URI');
|
||||
}
|
||||
|
||||
public function didCreateImport(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImport $import) {
|
||||
|
||||
$uri_key = PhabricatorCalendarImportICSURITransaction::PARAMKEY_URI;
|
||||
$uri = $import->getParameter($uri_key);
|
||||
|
||||
PhabricatorSystemActionEngine::willTakeAction(
|
||||
array($viewer->getPHID()),
|
||||
new PhabricatorFilesOutboundRequestAction(),
|
||||
1);
|
||||
|
||||
$file = PhabricatorFile::newFromFileDownload(
|
||||
$uri,
|
||||
array(
|
||||
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
|
||||
'authorPHID' => $import->getAuthorPHID(),
|
||||
'canCDN' => true,
|
||||
));
|
||||
|
||||
$import->newLogMessage(
|
||||
PhabricatorCalendarImportFetchLogType::LOGTYPE,
|
||||
array(
|
||||
'file.phid' => $file->getPHID(),
|
||||
));
|
||||
|
||||
$data = $file->loadFileData();
|
||||
|
||||
return $this->importICSData($viewer, $import, $data);
|
||||
}
|
||||
|
||||
public function canDisable(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImport $import) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,424 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorCalendarImportEngine
|
||||
extends Phobject {
|
||||
|
||||
final public function getImportEngineType() {
|
||||
return $this->getPhobjectClassConstant('ENGINETYPE', 64);
|
||||
}
|
||||
|
||||
|
||||
abstract public function getImportEngineName();
|
||||
abstract public function getImportEngineTypeName();
|
||||
abstract public function getImportEngineHint();
|
||||
|
||||
public function appendImportProperties(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImport $import,
|
||||
PHUIPropertyListView $properties) {
|
||||
return;
|
||||
}
|
||||
|
||||
abstract public function newEditEngineFields(
|
||||
PhabricatorEditEngine $engine,
|
||||
PhabricatorCalendarImport $import);
|
||||
|
||||
abstract public function getDisplayName(PhabricatorCalendarImport $import);
|
||||
|
||||
abstract public function didCreateImport(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImport $import);
|
||||
|
||||
abstract public function canDisable(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImport $import);
|
||||
|
||||
public function explainCanDisable(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImport $import) {
|
||||
throw new PhutilMethodNotImplementedException();
|
||||
}
|
||||
|
||||
final public static function getAllImportEngines() {
|
||||
return id(new PhutilClassMapQuery())
|
||||
->setAncestorClass(__CLASS__)
|
||||
->setUniqueMethod('getImportEngineType')
|
||||
->setSortMethod('getImportEngineName')
|
||||
->execute();
|
||||
}
|
||||
|
||||
final protected function importEventDocument(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImport $import,
|
||||
PhutilCalendarRootNode $root = null) {
|
||||
|
||||
$event_type = PhutilCalendarEventNode::NODETYPE;
|
||||
|
||||
$nodes = array();
|
||||
if ($root) {
|
||||
foreach ($root->getChildren() as $document) {
|
||||
foreach ($document->getChildren() as $node) {
|
||||
$node_type = $node->getNodeType();
|
||||
if ($node_type != $event_type) {
|
||||
$import->newLogMessage(
|
||||
PhabricatorCalendarImportIgnoredNodeLogType::LOGTYPE,
|
||||
array(
|
||||
'node.type' => $node_type,
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
$nodes[] = $node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reject events which have dates outside of the range of a signed
|
||||
// 32-bit integer. We'll need to accommodate a wider range of events
|
||||
// eventually, but have about 20 years until it's an issue and we'll
|
||||
// all be dead by then.
|
||||
foreach ($nodes as $key => $node) {
|
||||
$dates = array();
|
||||
$dates[] = $node->getStartDateTime();
|
||||
$dates[] = $node->getEndDateTime();
|
||||
$dates[] = $node->getCreatedDateTime();
|
||||
$dates[] = $node->getModifiedDateTime();
|
||||
$rrule = $node->getRecurrenceRule();
|
||||
if ($rrule) {
|
||||
$dates[] = $rrule->getUntil();
|
||||
}
|
||||
|
||||
$bad_date = false;
|
||||
foreach ($dates as $date) {
|
||||
if ($date === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$year = $date->getYear();
|
||||
if ($year < 1970 || $year > 2037) {
|
||||
$bad_date = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($bad_date) {
|
||||
$import->newLogMessage(
|
||||
PhabricatorCalendarImportEpochLogType::LOGTYPE,
|
||||
array());
|
||||
unset($nodes[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
// Reject events which occur too frequently. Users do not normally define
|
||||
// these events and the UI and application make many assumptions which are
|
||||
// incompatible with events recurring once per second.
|
||||
foreach ($nodes as $key => $node) {
|
||||
$rrule = $node->getRecurrenceRule();
|
||||
if (!$rrule) {
|
||||
// This is not a recurring event, so we don't need to check the
|
||||
// frequency.
|
||||
continue;
|
||||
}
|
||||
$scale = $rrule->getFrequencyScale();
|
||||
if ($scale >= PhutilCalendarRecurrenceRule::SCALE_DAILY) {
|
||||
// This is a daily, weekly, monthly, or yearly event. These are
|
||||
// supported.
|
||||
} else {
|
||||
// This is an hourly, minutely, or secondly event.
|
||||
$import->newLogMessage(
|
||||
PhabricatorCalendarImportFrequencyLogType::LOGTYPE,
|
||||
array(
|
||||
'frequency' => $rrule->getFrequency(),
|
||||
));
|
||||
unset($nodes[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$node_map = array();
|
||||
foreach ($nodes as $node) {
|
||||
$full_uid = $this->getFullNodeUID($node);
|
||||
if (isset($node_map[$full_uid])) {
|
||||
$import->newLogMessage(
|
||||
PhabricatorCalendarImportDuplicateLogType::LOGTYPE,
|
||||
array(
|
||||
'uid.full' => $full_uid,
|
||||
));
|
||||
continue;
|
||||
}
|
||||
$node_map[$full_uid] = $node;
|
||||
}
|
||||
|
||||
// If we already know about some of these events and they were created
|
||||
// here, we're not going to import it again. This can happen if a user
|
||||
// exports an event and then tries to import it again. This is probably
|
||||
// not what they meant to do and this pathway generally leads to madness.
|
||||
$likely_phids = array();
|
||||
foreach ($node_map as $full_uid => $node) {
|
||||
$uid = $node->getUID();
|
||||
$matches = null;
|
||||
if (preg_match('/^(PHID-.*)@(.*)\z/', $uid, $matches)) {
|
||||
$likely_phids[$full_uid] = $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
if ($likely_phids) {
|
||||
// NOTE: We're using the omnipotent viewer here because we don't want
|
||||
// to collide with events that already exist, even if you can't see
|
||||
// them.
|
||||
$events = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withPHIDs($likely_phids)
|
||||
->execute();
|
||||
$events = mpull($events, null, 'getPHID');
|
||||
foreach ($node_map as $full_uid => $node) {
|
||||
$phid = idx($likely_phids, $full_uid);
|
||||
if (!$phid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$event = idx($events, $phid);
|
||||
if (!$event) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$import->newLogMessage(
|
||||
PhabricatorCalendarImportOriginalLogType::LOGTYPE,
|
||||
array(
|
||||
'phid' => $event->getPHID(),
|
||||
));
|
||||
|
||||
unset($node_map[$full_uid]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($node_map) {
|
||||
$events = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($viewer)
|
||||
->withImportAuthorPHIDs(array($viewer->getPHID()))
|
||||
->withImportUIDs(array_keys($node_map))
|
||||
->execute();
|
||||
$events = mpull($events, null, 'getImportUID');
|
||||
} else {
|
||||
$events = null;
|
||||
}
|
||||
|
||||
$xactions = array();
|
||||
$update_map = array();
|
||||
foreach ($node_map as $full_uid => $node) {
|
||||
$event = idx($events, $full_uid);
|
||||
if (!$event) {
|
||||
$event = PhabricatorCalendarEvent::initializeNewCalendarEvent($viewer);
|
||||
}
|
||||
|
||||
$event
|
||||
->setImportAuthorPHID($viewer->getPHID())
|
||||
->setImportSourcePHID($import->getPHID())
|
||||
->setImportUID($full_uid)
|
||||
->attachImportSource($import);
|
||||
|
||||
$this->updateEventFromNode($viewer, $event, $node);
|
||||
$xactions[$full_uid] = $this->newUpdateTransactions($event, $node);
|
||||
$update_map[$full_uid] = $event;
|
||||
}
|
||||
|
||||
// Reorder events so we create parents first. This allows us to populate
|
||||
// "instanceOfEventPHID" correctly.
|
||||
$insert_order = array();
|
||||
foreach ($update_map as $full_uid => $event) {
|
||||
$parent_uid = $this->getParentNodeUID($node_map[$full_uid]);
|
||||
if ($parent_uid === null) {
|
||||
$insert_order[$full_uid] = $full_uid;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($update_map[$parent_uid])) {
|
||||
// The parent was not present in this import, which means it either
|
||||
// does not exist or we're going to delete it anyway. We just drop
|
||||
// this node.
|
||||
|
||||
$import->newLogMessage(
|
||||
PhabricatorCalendarImportOrphanLogType::LOGTYPE,
|
||||
array(
|
||||
'uid.full' => $full_uid,
|
||||
'uid.parent' => $parent_uid,
|
||||
));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, we're going to insert the parent first, then insert
|
||||
// the child.
|
||||
$insert_order[$parent_uid] = $parent_uid;
|
||||
$insert_order[$full_uid] = $full_uid;
|
||||
}
|
||||
|
||||
// TODO: Define per-engine content sources so this can say "via Upload" or
|
||||
// whatever.
|
||||
$content_source = PhabricatorContentSource::newForSource(
|
||||
PhabricatorWebContentSource::SOURCECONST);
|
||||
|
||||
// NOTE: We're using the omnipotent user here because imported events are
|
||||
// otherwise immutable.
|
||||
$edit_actor = PhabricatorUser::getOmnipotentUser();
|
||||
|
||||
$update_map = array_select_keys($update_map, $insert_order);
|
||||
foreach ($update_map as $full_uid => $event) {
|
||||
$parent_uid = $this->getParentNodeUID($node_map[$full_uid]);
|
||||
if ($parent_uid) {
|
||||
$parent_phid = $update_map[$full_uid]->getPHID();
|
||||
} else {
|
||||
$parent_phid = null;
|
||||
}
|
||||
|
||||
$event->setInstanceOfEventPHID($parent_phid);
|
||||
|
||||
$event_xactions = $xactions[$full_uid];
|
||||
|
||||
$editor = id(new PhabricatorCalendarEventEditor())
|
||||
->setActor($edit_actor)
|
||||
->setActingAsPHID($import->getPHID())
|
||||
->setContentSource($content_source)
|
||||
->setContinueOnNoEffect(true)
|
||||
->setContinueOnMissingFields(true);
|
||||
|
||||
$is_new = !$event->getID();
|
||||
|
||||
$editor->applyTransactions($event, $event_xactions);
|
||||
|
||||
$import->newLogMessage(
|
||||
PhabricatorCalendarImportUpdateLogType::LOGTYPE,
|
||||
array(
|
||||
'new' => $is_new,
|
||||
'phid' => $event->getPHID(),
|
||||
));
|
||||
}
|
||||
|
||||
if (!$update_map) {
|
||||
$import->newLogMessage(
|
||||
PhabricatorCalendarImportEmptyLogType::LOGTYPE,
|
||||
array());
|
||||
}
|
||||
|
||||
// TODO: When the source is a subscription-based ICS file or some other
|
||||
// similar source, we should load all events from the source here and
|
||||
// destroy the ones we didn't update. These are events that have been
|
||||
// deleted.
|
||||
}
|
||||
|
||||
private function getFullNodeUID(PhutilCalendarEventNode $node) {
|
||||
$uid = $node->getUID();
|
||||
$instance_epoch = $this->getNodeInstanceEpoch($node);
|
||||
$full_uid = $uid.'/'.$instance_epoch;
|
||||
|
||||
return $full_uid;
|
||||
}
|
||||
|
||||
private function getParentNodeUID(PhutilCalendarEventNode $node) {
|
||||
$recurrence_id = $node->getRecurrenceID();
|
||||
|
||||
if (!strlen($recurrence_id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $node->getUID().'/';
|
||||
}
|
||||
|
||||
private function getNodeInstanceEpoch(PhutilCalendarEventNode $node) {
|
||||
$instance_iso = $node->getRecurrenceID();
|
||||
if (strlen($instance_iso)) {
|
||||
$instance_datetime = PhutilCalendarAbsoluteDateTime::newFromISO8601(
|
||||
$instance_iso);
|
||||
$instance_epoch = $instance_datetime->getEpoch();
|
||||
} else {
|
||||
$instance_epoch = null;
|
||||
}
|
||||
|
||||
return $instance_epoch;
|
||||
}
|
||||
|
||||
private function newUpdateTransactions(
|
||||
PhabricatorCalendarEvent $event,
|
||||
PhutilCalendarEventNode $node) {
|
||||
|
||||
$xactions = array();
|
||||
$uid = $node->getUID();
|
||||
|
||||
$name = $node->getName();
|
||||
if (!strlen($name)) {
|
||||
if (strlen($uid)) {
|
||||
$name = pht('Unnamed Event "%s"', $uid);
|
||||
} else {
|
||||
$name = pht('Unnamed Imported Event');
|
||||
}
|
||||
}
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventNameTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue($name);
|
||||
|
||||
$description = $node->getDescription();
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventDescriptionTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue((string)$description);
|
||||
|
||||
$is_recurring = (bool)$node->getRecurrenceRule();
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventRecurringTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue($is_recurring);
|
||||
|
||||
return $xactions;
|
||||
}
|
||||
|
||||
private function updateEventFromNode(
|
||||
PhabricatorUser $actor,
|
||||
PhabricatorCalendarEvent $event,
|
||||
PhutilCalendarEventNode $node) {
|
||||
|
||||
$instance_epoch = $this->getNodeInstanceEpoch($node);
|
||||
$event->setUTCInstanceEpoch($instance_epoch);
|
||||
|
||||
$timezone = $actor->getTimezoneIdentifier();
|
||||
|
||||
// TODO: These should be transactional, but the transaction only accepts
|
||||
// epoch timestamps right now.
|
||||
$start_datetime = $node->getStartDateTime()
|
||||
->setViewerTimezone($timezone);
|
||||
$end_datetime = $node->getEndDateTime()
|
||||
->setViewerTimezone($timezone);
|
||||
|
||||
$event
|
||||
->setStartDateTime($start_datetime)
|
||||
->setEndDateTime($end_datetime);
|
||||
|
||||
// TODO: This should be transactional, but the transaction only accepts
|
||||
// simple frequency rules right now.
|
||||
$rrule = $node->getRecurrenceRule();
|
||||
if ($rrule) {
|
||||
$event->setRecurrenceRule($rrule);
|
||||
|
||||
$until_datetime = $rrule->getUntil();
|
||||
if ($until_datetime) {
|
||||
$until_datetime->setViewerTimezone($timezone);
|
||||
$event->setUntilDateTime($until_datetime);
|
||||
}
|
||||
}
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
public function canDeleteAnyEvents(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImport $import) {
|
||||
|
||||
$any_event = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($viewer)
|
||||
->withImportSourcePHIDs(array($import->getPHID()))
|
||||
->setLimit(1)
|
||||
->execute();
|
||||
|
||||
return (bool)$any_event;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportDefaultLogType
|
||||
extends PhabricatorCalendarImportLogType {
|
||||
|
||||
const LOGTYPE = 'default';
|
||||
|
||||
public function getDisplayType(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
|
||||
$type = $log->getParameter('type');
|
||||
if (strlen($type)) {
|
||||
return pht('Unknown Message "%s"', $type);
|
||||
} else {
|
||||
return pht('Unknown Message');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportDuplicateLogType
|
||||
extends PhabricatorCalendarImportLogType {
|
||||
|
||||
const LOGTYPE = 'duplicate';
|
||||
|
||||
public function getDisplayType(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return pht('Duplicate Event');
|
||||
}
|
||||
|
||||
public function getDisplayDescription(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
$duplicate_uid = $log->getParameter('uid.full');
|
||||
return pht(
|
||||
'Ignored duplicate event "%s" present in source.',
|
||||
$duplicate_uid);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportEmptyLogType
|
||||
extends PhabricatorCalendarImportLogType {
|
||||
|
||||
const LOGTYPE = 'empty';
|
||||
|
||||
public function getDisplayType(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return pht('No Events Imported');
|
||||
}
|
||||
|
||||
public function getDisplayDescription(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return pht('Found no valid events to import.');
|
||||
}
|
||||
|
||||
public function getDisplayIcon(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return 'fa-ban';
|
||||
}
|
||||
|
||||
public function getDisplayColor(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return 'red';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportEpochLogType
|
||||
extends PhabricatorCalendarImportLogType {
|
||||
|
||||
const LOGTYPE = 'epoch';
|
||||
|
||||
public function getDisplayType(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return pht('Out of Range');
|
||||
}
|
||||
|
||||
public function getDisplayDescription(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return pht(
|
||||
'Ignored an event with an out-of-range date. Only dates between '.
|
||||
'1970 and 2037 are supported.');
|
||||
}
|
||||
|
||||
public function getDisplayIcon(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return 'fa-clock-o';
|
||||
}
|
||||
|
||||
public function getDisplayColor(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return 'red';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportFetchLogType
|
||||
extends PhabricatorCalendarImportLogType {
|
||||
|
||||
const LOGTYPE = 'fetch';
|
||||
|
||||
public function getDisplayType(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return pht('Fetched Calendar');
|
||||
}
|
||||
|
||||
public function getDisplayDescription(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
|
||||
return $viewer->renderHandle($log->getParameter('file.phid'));
|
||||
}
|
||||
|
||||
public function getDisplayIcon(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return 'fa-download';
|
||||
}
|
||||
|
||||
public function getDisplayColor(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return 'green';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportFrequencyLogType
|
||||
extends PhabricatorCalendarImportLogType {
|
||||
|
||||
const LOGTYPE = 'frequency';
|
||||
|
||||
public function getDisplayType(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return pht('Too Frequent');
|
||||
}
|
||||
|
||||
public function getDisplayDescription(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
|
||||
$frequency = $log->getParameter('frequency');
|
||||
|
||||
return pht(
|
||||
'Ignored an event with an unsupported frequency rule ("%s"). Events '.
|
||||
'which repeat more frequently than daily are not supported.',
|
||||
$frequency);
|
||||
}
|
||||
|
||||
public function getDisplayIcon(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return 'fa-clock-o';
|
||||
}
|
||||
|
||||
public function getDisplayColor(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return 'red';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportICSLogType
|
||||
extends PhabricatorCalendarImportLogType {
|
||||
|
||||
const LOGTYPE = 'ics';
|
||||
|
||||
public function getDisplayType(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return pht('ICS Parse Error');
|
||||
}
|
||||
|
||||
public function getDisplayDescription(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return pht(
|
||||
'Failed to parse ICS data ("%s"): %s',
|
||||
$log->getParameter('ics.code'),
|
||||
$log->getParameter('ics.message'));
|
||||
}
|
||||
|
||||
|
||||
public function getDisplayIcon(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return 'fa-file';
|
||||
}
|
||||
|
||||
public function getDisplayColor(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return 'red';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportIgnoredNodeLogType
|
||||
extends PhabricatorCalendarImportLogType {
|
||||
|
||||
const LOGTYPE = 'nodetype';
|
||||
|
||||
public function getDisplayType(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return pht('Ignored Node');
|
||||
}
|
||||
|
||||
public function getDisplayDescription(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
$node_type = $log->getParameter('node.type');
|
||||
return pht(
|
||||
'Ignored unsupported "%s" node present in source.',
|
||||
$node_type);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorCalendarImportLogType
|
||||
extends Phobject {
|
||||
|
||||
final public function getLogTypeConstant() {
|
||||
return $this->getPhobjectClassConstant('LOGTYPE', 64);
|
||||
}
|
||||
|
||||
final public static function getAllLogTypes() {
|
||||
return id(new PhutilClassMapQuery())
|
||||
->setAncestorClass(__CLASS__)
|
||||
->setUniqueMethod('getLogTypeConstant')
|
||||
->execute();
|
||||
}
|
||||
|
||||
abstract public function getDisplayType(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log);
|
||||
|
||||
public function getDisplayIcon(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return 'fa-warning';
|
||||
}
|
||||
|
||||
public function getDisplayColor(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return 'yellow';
|
||||
}
|
||||
|
||||
public function getDisplayDescription(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportOriginalLogType
|
||||
extends PhabricatorCalendarImportLogType {
|
||||
|
||||
const LOGTYPE = 'original';
|
||||
|
||||
public function getDisplayType(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return pht('Original Event');
|
||||
}
|
||||
|
||||
public function getDisplayDescription(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
|
||||
$phid = $log->getParameter('phid');
|
||||
|
||||
return pht(
|
||||
'Ignored an event (%s) because the original version of this event '.
|
||||
'was created here.',
|
||||
$viewer->renderHandle($phid));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportOrphanLogType
|
||||
extends PhabricatorCalendarImportLogType {
|
||||
|
||||
const LOGTYPE = 'orphan';
|
||||
|
||||
public function getDisplayType(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return pht('Orphan');
|
||||
}
|
||||
|
||||
public function getDisplayDescription(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
$child_uid = $log->getParameter('uid.full');
|
||||
$parent_uid = $log->getParameter('uid.parent');
|
||||
return pht(
|
||||
'Found orphaned child event ("%s") without a parent event ("%s").',
|
||||
$child_uid,
|
||||
$parent_uid);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportUpdateLogType
|
||||
extends PhabricatorCalendarImportLogType {
|
||||
|
||||
const LOGTYPE = 'update';
|
||||
|
||||
public function getDisplayType(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
$is_new = $log->getParameter('new');
|
||||
if ($is_new) {
|
||||
return pht('Imported Event');
|
||||
} else {
|
||||
return pht('Updated Event');
|
||||
}
|
||||
}
|
||||
|
||||
public function getDisplayDescription(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
$event_phid = $log->getParameter('phid');
|
||||
return $viewer->renderHandle($event_phid);
|
||||
}
|
||||
|
||||
public function getDisplayIcon(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return 'fa-upload';
|
||||
}
|
||||
|
||||
public function getDisplayColor(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorCalendarImportLog $log) {
|
||||
return 'green';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportPHIDType extends PhabricatorPHIDType {
|
||||
|
||||
const TYPECONST = 'CIMP';
|
||||
|
||||
public function getTypeName() {
|
||||
return pht('Calendar Import');
|
||||
}
|
||||
|
||||
public function newObject() {
|
||||
return new PhabricatorCalendarImport();
|
||||
}
|
||||
|
||||
public function getPHIDTypeApplicationClass() {
|
||||
return 'PhabricatorCalendarApplication';
|
||||
}
|
||||
|
||||
protected function buildQueryForObjects(
|
||||
PhabricatorObjectQuery $query,
|
||||
array $phids) {
|
||||
|
||||
return id(new PhabricatorCalendarImportQuery())
|
||||
->withPHIDs($phids);
|
||||
}
|
||||
|
||||
public function loadHandles(
|
||||
PhabricatorHandleQuery $query,
|
||||
array $handles,
|
||||
array $objects) {
|
||||
|
||||
foreach ($handles as $phid => $handle) {
|
||||
$import = $objects[$phid];
|
||||
|
||||
$id = $import->getID();
|
||||
$name = $import->getDisplayName();
|
||||
$uri = $import->getURI();
|
||||
|
||||
$handle
|
||||
->setName($name)
|
||||
->setFullName(pht('Calendar Import %s: %s', $id, $name))
|
||||
->setURI($uri);
|
||||
|
||||
if ($import->getIsDisabled()) {
|
||||
$handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -14,6 +14,9 @@ final class PhabricatorCalendarEventQuery
|
|||
private $instanceSequencePairs;
|
||||
private $isStub;
|
||||
private $parentEventPHIDs;
|
||||
private $importSourcePHIDs;
|
||||
private $importAuthorPHIDs;
|
||||
private $importUIDs;
|
||||
|
||||
private $generateGhosts = false;
|
||||
|
||||
|
@ -77,6 +80,21 @@ final class PhabricatorCalendarEventQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withImportSourcePHIDs(array $import_phids) {
|
||||
$this->importSourcePHIDs = $import_phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withImportAuthorPHIDs(array $author_phids) {
|
||||
$this->importAuthorPHIDs = $author_phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withImportUIDs(array $uids) {
|
||||
$this->importUIDs = $uids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getDefaultOrderVector() {
|
||||
return array('start', 'id');
|
||||
}
|
||||
|
@ -411,6 +429,27 @@ final class PhabricatorCalendarEventQuery
|
|||
$this->parentEventPHIDs);
|
||||
}
|
||||
|
||||
if ($this->importSourcePHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'event.importSourcePHID IN (%Ls)',
|
||||
$this->importSourcePHIDs);
|
||||
}
|
||||
|
||||
if ($this->importAuthorPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'event.importAuthorPHID IN (%Ls)',
|
||||
$this->importAuthorPHIDs);
|
||||
}
|
||||
|
||||
if ($this->importUIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'event.importUID IN (%Ls)',
|
||||
$this->importUIDs);
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
|
@ -441,6 +480,42 @@ final class PhabricatorCalendarEventQuery
|
|||
|
||||
$events = $this->getEventsInRange($events);
|
||||
|
||||
$import_phids = array();
|
||||
foreach ($events as $event) {
|
||||
$import_phid = $event->getImportSourcePHID();
|
||||
if ($import_phid !== null) {
|
||||
$import_phids[$import_phid] = $import_phid;
|
||||
}
|
||||
}
|
||||
|
||||
if ($import_phids) {
|
||||
$imports = id(new PhabricatorCalendarImportQuery())
|
||||
->setParentQuery($this)
|
||||
->setViewer($viewer)
|
||||
->withPHIDs($import_phids)
|
||||
->execute();
|
||||
$imports = mpull($imports, null, 'getPHID');
|
||||
} else {
|
||||
$imports = array();
|
||||
}
|
||||
|
||||
foreach ($events as $key => $event) {
|
||||
$import_phid = $event->getImportSourcePHID();
|
||||
if ($import_phid === null) {
|
||||
$event->attachImportSource(null);
|
||||
continue;
|
||||
}
|
||||
|
||||
$import = idx($imports, $import_phid);
|
||||
if (!$import) {
|
||||
unset($events[$key]);
|
||||
$this->didRejectResult($event);
|
||||
continue;
|
||||
}
|
||||
|
||||
$event->attachImportSource($import);
|
||||
}
|
||||
|
||||
$phids = array();
|
||||
|
||||
foreach ($events as $event) {
|
||||
|
@ -561,5 +636,4 @@ final class PhabricatorCalendarEventQuery
|
|||
return $raw_limit;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -51,6 +51,10 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
->setKey('isCancelled')
|
||||
->setOptions($this->getCancelledOptions())
|
||||
->setDefault('active'),
|
||||
id(new PhabricatorPHIDsSearchField())
|
||||
->setLabel(pht('Import Sources'))
|
||||
->setKey('importSourcePHIDs')
|
||||
->setAliases(array('importSourcePHID')),
|
||||
id(new PhabricatorSearchSelectField())
|
||||
->setLabel(pht('Display Options'))
|
||||
->setKey('display')
|
||||
|
@ -114,6 +118,10 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
break;
|
||||
}
|
||||
|
||||
if ($map['importSourcePHIDs']) {
|
||||
$query->withImportSourcePHIDs($map['importSourcePHIDs']);
|
||||
}
|
||||
|
||||
// Generate ghosts (and ignore stub events) if we aren't querying for
|
||||
// specific events or exporting.
|
||||
if (!empty($map['export'])) {
|
||||
|
@ -313,11 +321,9 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
$list->addItem($item);
|
||||
}
|
||||
|
||||
$result = new PhabricatorApplicationSearchResultView();
|
||||
$result->setObjectList($list);
|
||||
$result->setNoDataString(pht('No events found.'));
|
||||
|
||||
return $result;
|
||||
return $this->newResultView()
|
||||
->setObjectList($list)
|
||||
->setNoDataString(pht('No events found.'));
|
||||
}
|
||||
|
||||
private function buildCalendarMonthView(
|
||||
|
@ -385,10 +391,9 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
->setProfileHeader(true)
|
||||
->setHeader($from->format('F Y'));
|
||||
|
||||
return id(new PhabricatorApplicationSearchResultView())
|
||||
return $this->newResultView($month_view)
|
||||
->setCrumbs($crumbs)
|
||||
->setHeader($header)
|
||||
->setContent($month_view);
|
||||
->setHeader($header);
|
||||
}
|
||||
|
||||
private function buildCalendarDayView(
|
||||
|
@ -459,10 +464,9 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
->setProfileHeader(true)
|
||||
->setHeader($from->format('D, F jS'));
|
||||
|
||||
return id(new PhabricatorApplicationSearchResultView())
|
||||
return $this->newResultView($day_view)
|
||||
->setCrumbs($crumbs)
|
||||
->setHeader($header)
|
||||
->setContent($day_view);
|
||||
->setHeader($header);
|
||||
}
|
||||
|
||||
private function getDisplayYearAndMonthAndDay(
|
||||
|
@ -588,4 +592,26 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
private function newResultView($content = null) {
|
||||
// If we aren't rendering a dashboard panel, activate global drag-and-drop
|
||||
// so you can import ".ics" files by dropping them directly onto the
|
||||
// calendar.
|
||||
if (!$this->isPanelContext()) {
|
||||
$drop_upload = id(new PhabricatorGlobalUploadTargetView())
|
||||
->setViewer($this->requireViewer())
|
||||
->setHintText("\xE2\x87\xAA ".pht('Drop .ics Files to Import'))
|
||||
->setSubmitURI('/calendar/import/drop/')
|
||||
->setViewPolicy(PhabricatorPolicies::POLICY_NOONE);
|
||||
|
||||
$content = array(
|
||||
$drop_upload,
|
||||
$content,
|
||||
);
|
||||
}
|
||||
|
||||
return id(new PhabricatorApplicationSearchResultView())
|
||||
->setContent($content);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportLogQuery
|
||||
extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||
|
||||
private $ids;
|
||||
private $phids;
|
||||
private $importPHIDs;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withPHIDs(array $phids) {
|
||||
$this->phids = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withImportPHIDs(array $phids) {
|
||||
$this->importPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new PhabricatorCalendarImportLog();
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
return $this->loadStandardPage($this->newResultObject());
|
||||
}
|
||||
|
||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'log.id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'log.phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->importPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'log.importPHID IN (%Ls)',
|
||||
$this->importPHIDs);
|
||||
}
|
||||
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
protected function willFilterPage(array $page) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$type_map = PhabricatorCalendarImportLogType::getAllLogTypes();
|
||||
foreach ($page as $log) {
|
||||
$type_constant = $log->getParameter('type');
|
||||
|
||||
$type_object = idx($type_map, $type_constant);
|
||||
if (!$type_object) {
|
||||
$type_object = new PhabricatorCalendarImportDefaultLogType();
|
||||
}
|
||||
|
||||
$type_object = clone $type_object;
|
||||
$log->attachLogType($type_object);
|
||||
}
|
||||
|
||||
$import_phids = mpull($page, 'getImportPHID');
|
||||
|
||||
if ($import_phids) {
|
||||
$imports = id(new PhabricatorCalendarImportQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs($import_phids)
|
||||
->execute();
|
||||
$imports = mpull($imports, null, 'getPHID');
|
||||
} else {
|
||||
$imports = array();
|
||||
}
|
||||
|
||||
foreach ($page as $key => $log) {
|
||||
$import = idx($imports, $log->getImportPHID());
|
||||
if (!$import) {
|
||||
$this->didRejectResult($import);
|
||||
unset($page[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$log->attachImport($import);
|
||||
}
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
protected function getPrimaryTableAlias() {
|
||||
return 'log';
|
||||
}
|
||||
|
||||
public function getQueryApplicationClass() {
|
||||
return 'PhabricatorCalendarApplication';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportLogSearchEngine
|
||||
extends PhabricatorApplicationSearchEngine {
|
||||
|
||||
public function getResultTypeDescription() {
|
||||
return pht('Calendar Import Logs');
|
||||
}
|
||||
|
||||
public function getApplicationClassName() {
|
||||
return 'PhabricatorCalendarApplication';
|
||||
}
|
||||
|
||||
public function newQuery() {
|
||||
return new PhabricatorCalendarImportLogQuery();
|
||||
}
|
||||
|
||||
protected function buildCustomSearchFields() {
|
||||
return array(
|
||||
id(new PhabricatorPHIDsSearchField())
|
||||
->setLabel(pht('Import Sources'))
|
||||
->setKey('importSourcePHIDs')
|
||||
->setAliases(array('importSourcePHID')),
|
||||
);
|
||||
}
|
||||
|
||||
protected function buildQueryFromParameters(array $map) {
|
||||
$query = $this->newQuery();
|
||||
|
||||
if ($map['importSourcePHIDs']) {
|
||||
$query->withImportPHIDs($map['importSourcePHIDs']);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function getURI($path) {
|
||||
return '/calendar/import/log/'.$path;
|
||||
}
|
||||
|
||||
protected function getBuiltinQueryNames() {
|
||||
$names = array(
|
||||
'all' => pht('All Logs'),
|
||||
);
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
public function buildSavedQueryFromBuiltin($query_key) {
|
||||
$query = $this->newSavedQuery();
|
||||
$query->setQueryKey($query_key);
|
||||
|
||||
switch ($query_key) {
|
||||
case 'all':
|
||||
return $query;
|
||||
}
|
||||
|
||||
return parent::buildSavedQueryFromBuiltin($query_key);
|
||||
}
|
||||
|
||||
protected function renderResultList(
|
||||
array $logs,
|
||||
PhabricatorSavedQuery $query,
|
||||
array $handles) {
|
||||
|
||||
assert_instances_of($logs, 'PhabricatorCalendarImportLog');
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$view = id(new PhabricatorCalendarImportLogView())
|
||||
->setShowImportSources(true)
|
||||
->setViewer($viewer)
|
||||
->setLogs($logs);
|
||||
|
||||
return id(new PhabricatorApplicationSearchResultView())
|
||||
->setTable($view->newTable());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportQuery
|
||||
extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||
|
||||
private $ids;
|
||||
private $phids;
|
||||
private $authorPHIDs;
|
||||
private $isDisabled;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withPHIDs(array $phids) {
|
||||
$this->phids = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withAuthorPHIDs(array $phids) {
|
||||
$this->authorPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withIsDisabled($is_disabled) {
|
||||
$this->isDisabled = $is_disabled;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new PhabricatorCalendarImport();
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
return $this->loadStandardPage($this->newResultObject());
|
||||
}
|
||||
|
||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'import.id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'import.phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->authorPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'import.authorPHID IN (%Ls)',
|
||||
$this->authorPHIDs);
|
||||
}
|
||||
|
||||
if ($this->isDisabled !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'import.isDisabled = %d',
|
||||
(int)$this->isDisabled);
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
protected function willFilterPage(array $page) {
|
||||
$engines = PhabricatorCalendarImportEngine::getAllImportEngines();
|
||||
foreach ($page as $key => $import) {
|
||||
$engine_type = $import->getEngineType();
|
||||
$engine = idx($engines, $engine_type);
|
||||
|
||||
if (!$engine) {
|
||||
unset($page[$key]);
|
||||
$this->didRejectResult($import);
|
||||
continue;
|
||||
}
|
||||
|
||||
$import->attachEngine(clone $engine);
|
||||
}
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
protected function getPrimaryTableAlias() {
|
||||
return 'import';
|
||||
}
|
||||
|
||||
public function getQueryApplicationClass() {
|
||||
return 'PhabricatorCalendarApplication';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportSearchEngine
|
||||
extends PhabricatorApplicationSearchEngine {
|
||||
|
||||
public function getResultTypeDescription() {
|
||||
return pht('Calendar Imports');
|
||||
}
|
||||
|
||||
public function getApplicationClassName() {
|
||||
return 'PhabricatorCalendarApplication';
|
||||
}
|
||||
|
||||
public function newQuery() {
|
||||
return new PhabricatorCalendarImportQuery();
|
||||
}
|
||||
|
||||
protected function buildCustomSearchFields() {
|
||||
return array();
|
||||
}
|
||||
|
||||
protected function buildQueryFromParameters(array $map) {
|
||||
$query = $this->newQuery();
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function getURI($path) {
|
||||
return '/calendar/import/'.$path;
|
||||
}
|
||||
|
||||
protected function getBuiltinQueryNames() {
|
||||
$names = array(
|
||||
'all' => pht('All Imports'),
|
||||
);
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
public function buildSavedQueryFromBuiltin($query_key) {
|
||||
$query = $this->newSavedQuery();
|
||||
$query->setQueryKey($query_key);
|
||||
|
||||
switch ($query_key) {
|
||||
case 'all':
|
||||
return $query;
|
||||
}
|
||||
|
||||
return parent::buildSavedQueryFromBuiltin($query_key);
|
||||
}
|
||||
|
||||
protected function renderResultList(
|
||||
array $imports,
|
||||
PhabricatorSavedQuery $query,
|
||||
array $handles) {
|
||||
|
||||
assert_instances_of($imports, 'PhabricatorCalendarImport');
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$list = new PHUIObjectItemListView();
|
||||
foreach ($imports as $import) {
|
||||
$item = id(new PHUIObjectItemView())
|
||||
->setViewer($viewer)
|
||||
->setObjectName(pht('Import %d', $import->getID()))
|
||||
->setHeader($import->getDisplayName())
|
||||
->setHref($import->getURI());
|
||||
|
||||
if ($import->getIsDisabled()) {
|
||||
$item->setDisabled(true);
|
||||
}
|
||||
|
||||
$list->addItem($item);
|
||||
}
|
||||
|
||||
$result = new PhabricatorApplicationSearchResultView();
|
||||
$result->setObjectList($list);
|
||||
$result->setNoDataString(pht('No imports found.'));
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportTransactionQuery
|
||||
extends PhabricatorApplicationTransactionQuery {
|
||||
|
||||
public function getTemplateApplicationTransaction() {
|
||||
return new PhabricatorCalendarImportTransaction();
|
||||
}
|
||||
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
||||
implements
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorExtendedPolicyInterface,
|
||||
PhabricatorProjectInterface,
|
||||
PhabricatorMarkupInterface,
|
||||
PhabricatorApplicationTransactionInterface,
|
||||
|
@ -40,8 +41,14 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
protected $utcInstanceEpoch;
|
||||
protected $parameters = array();
|
||||
|
||||
protected $importAuthorPHID;
|
||||
protected $importSourcePHID;
|
||||
protected $importUIDIndex;
|
||||
protected $importUID;
|
||||
|
||||
private $parentEvent = self::ATTACHABLE;
|
||||
private $invitees = self::ATTACHABLE;
|
||||
private $importSource = self::ATTACHABLE;
|
||||
|
||||
private $viewerTimezone;
|
||||
|
||||
|
@ -74,6 +81,7 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
$datetime_end = $datetime_start->newRelativeDateTime('PT1H');
|
||||
|
||||
return id(new PhabricatorCalendarEvent())
|
||||
->setDescription('')
|
||||
->setHostPHID($actor->getPHID())
|
||||
->setIsCancelled(0)
|
||||
->setIsAllDay(0)
|
||||
|
@ -90,6 +98,7 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
->setAllDayDateTo(0)
|
||||
->setStartDateTime($datetime_start)
|
||||
->setEndDateTime($datetime_end)
|
||||
->attachImportSource(null)
|
||||
->applyViewerTimezone($actor);
|
||||
}
|
||||
|
||||
|
@ -170,11 +179,10 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
->setName($parent->getName())
|
||||
->setDescription($parent->getDescription());
|
||||
|
||||
$sequence = $this->getSequenceIndex();
|
||||
|
||||
if ($start) {
|
||||
$start_datetime = $start;
|
||||
} else {
|
||||
$sequence = $this->getSequenceIndex();
|
||||
$start_datetime = $parent->newSequenceIndexDateTime($sequence);
|
||||
|
||||
if (!$start_datetime) {
|
||||
|
@ -192,6 +200,19 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
->setStartDateTime($start_datetime)
|
||||
->setEndDateTime($end_datetime);
|
||||
|
||||
if ($parent->isImportedEvent()) {
|
||||
$full_uid = $parent->getImportUID().'/'.$start_datetime->getEpoch();
|
||||
|
||||
// NOTE: We don't attach the import source because this gets called
|
||||
// from CalendarEventQuery while building ghosts, before we've loaded
|
||||
// and attached sources. Possibly this sequence should be flipped.
|
||||
|
||||
$this
|
||||
->setImportAuthorPHID($parent->getImportAuthorPHID())
|
||||
->setImportSourcePHID($parent->getImportSourcePHID())
|
||||
->setImportUID($full_uid);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -306,6 +327,14 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
$this->mailKey = Filesystem::readRandomCharacters(20);
|
||||
}
|
||||
|
||||
$import_uid = $this->getImportUID();
|
||||
if ($import_uid !== null) {
|
||||
$index = PhabricatorHash::digestForIndex($import_uid);
|
||||
} else {
|
||||
$index = null;
|
||||
}
|
||||
$this->setImportUIDIndex($index);
|
||||
|
||||
$this->updateUTCEpochs();
|
||||
|
||||
return parent::save();
|
||||
|
@ -344,6 +373,11 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
'utcUntilEpoch' => 'epoch?',
|
||||
'utcInstanceEpoch' => 'epoch?',
|
||||
|
||||
'importAuthorPHID' => 'phid?',
|
||||
'importSourcePHID' => 'phid?',
|
||||
'importUIDIndex' => 'bytes12?',
|
||||
'importUID' => 'text?',
|
||||
|
||||
// TODO: DEPRECATED.
|
||||
'allDayDateFrom' => 'epoch',
|
||||
'allDayDateTo' => 'epoch',
|
||||
|
@ -554,6 +588,10 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
}
|
||||
}
|
||||
|
||||
if ($this->isImportedEvent()) {
|
||||
return 'fa-download';
|
||||
}
|
||||
|
||||
return $this->getIcon();
|
||||
}
|
||||
|
||||
|
@ -562,6 +600,10 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
return 'red';
|
||||
}
|
||||
|
||||
if ($this->isImportedEvent()) {
|
||||
return 'orange';
|
||||
}
|
||||
|
||||
if ($viewer->isLoggedIn()) {
|
||||
$status = $this->getUserInviteStatus($viewer->getPHID());
|
||||
switch ($status) {
|
||||
|
@ -885,6 +927,21 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
return $set;
|
||||
}
|
||||
|
||||
public function isImportedEvent() {
|
||||
return (bool)$this->getImportSourcePHID();
|
||||
}
|
||||
|
||||
public function getImportSource() {
|
||||
return $this->assertAttached($this->importSource);
|
||||
}
|
||||
|
||||
public function attachImportSource(
|
||||
PhabricatorCalendarImport $import = null) {
|
||||
$this->importSource = $import;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/* -( Markup Interface )--------------------------------------------------- */
|
||||
|
||||
|
||||
|
@ -947,11 +1004,19 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
case PhabricatorPolicyCapability::CAN_VIEW:
|
||||
return $this->getViewPolicy();
|
||||
case PhabricatorPolicyCapability::CAN_EDIT:
|
||||
if ($this->getImportSource()) {
|
||||
return PhabricatorPolicies::POLICY_NOONE;
|
||||
} else {
|
||||
return $this->getEditPolicy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||
if ($this->getImportSource()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The host of an event can always view and edit it.
|
||||
$user_phid = $this->getHostPHID();
|
||||
if ($user_phid) {
|
||||
|
@ -974,12 +1039,40 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
}
|
||||
|
||||
public function describeAutomaticCapability($capability) {
|
||||
if ($this->getImportSource()) {
|
||||
return pht(
|
||||
'Events imported from external sources can not be edited in '.
|
||||
'Phabricator.');
|
||||
}
|
||||
|
||||
return pht(
|
||||
'The host of an event can always view and edit it. Users who are '.
|
||||
'invited to an event can always view it.');
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */
|
||||
|
||||
|
||||
public function getExtendedPolicy($capability, PhabricatorUser $viewer) {
|
||||
$extended = array();
|
||||
|
||||
switch ($capability) {
|
||||
case PhabricatorPolicyCapability::CAN_VIEW:
|
||||
$import_source = $this->getImportSource();
|
||||
if ($import_source) {
|
||||
$extended[] = array(
|
||||
$import_source,
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $extended;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
||||
|
||||
|
||||
|
@ -1056,13 +1149,31 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
->setKey('description')
|
||||
->setType('string')
|
||||
->setDescription(pht('The event description.')),
|
||||
id(new PhabricatorConduitSearchFieldSpecification())
|
||||
->setKey('isAllDay')
|
||||
->setType('bool')
|
||||
->setDescription(pht('True if the event is an all day event.')),
|
||||
id(new PhabricatorConduitSearchFieldSpecification())
|
||||
->setKey('startDateTime')
|
||||
->setType('datetime')
|
||||
->setDescription(pht('Start date and time of the event.')),
|
||||
id(new PhabricatorConduitSearchFieldSpecification())
|
||||
->setKey('endDateTime')
|
||||
->setType('datetime')
|
||||
->setDescription(pht('End date and time of the event.')),
|
||||
);
|
||||
}
|
||||
|
||||
public function getFieldValuesForConduit() {
|
||||
$start_datetime = $this->newStartDateTime();
|
||||
$end_datetime = $this->newEndDateTime();
|
||||
|
||||
return array(
|
||||
'name' => $this->getName(),
|
||||
'description' => $this->getDescription(),
|
||||
'isAllDay' => (bool)$this->getIsAllDay(),
|
||||
'startDateTime' => $this->getConduitDateTime($start_datetime),
|
||||
'endDateTime' => $this->getConduitDateTime($end_datetime),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1070,4 +1181,26 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
return array();
|
||||
}
|
||||
|
||||
private function getConduitDateTime($datetime) {
|
||||
if (!$datetime) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$epoch = $datetime->getEpoch();
|
||||
|
||||
// TODO: Possibly pass the actual viewer in from the Conduit stuff, or
|
||||
// retain it when setting the viewer timezone?
|
||||
$viewer = id(new PhabricatorUser())
|
||||
->overrideTimezoneIdentifier($this->viewerTimezone);
|
||||
|
||||
return array(
|
||||
'epoch' => (int)$epoch,
|
||||
'display' => array(
|
||||
'default' => phabricator_datetime($epoch, $viewer),
|
||||
),
|
||||
'iso8601' => $datetime->getISO8601(),
|
||||
'timezone' => $this->viewerTimezone,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
178
src/applications/calendar/storage/PhabricatorCalendarImport.php
Normal file
178
src/applications/calendar/storage/PhabricatorCalendarImport.php
Normal file
|
@ -0,0 +1,178 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImport
|
||||
extends PhabricatorCalendarDAO
|
||||
implements
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorApplicationTransactionInterface,
|
||||
PhabricatorDestructibleInterface {
|
||||
|
||||
protected $name;
|
||||
protected $authorPHID;
|
||||
protected $viewPolicy;
|
||||
protected $editPolicy;
|
||||
protected $engineType;
|
||||
protected $parameters = array();
|
||||
protected $isDisabled = 0;
|
||||
|
||||
private $engine = self::ATTACHABLE;
|
||||
|
||||
public static function initializeNewCalendarImport(
|
||||
PhabricatorUser $actor,
|
||||
PhabricatorCalendarImportEngine $engine) {
|
||||
return id(new self())
|
||||
->setName('')
|
||||
->setAuthorPHID($actor->getPHID())
|
||||
->setViewPolicy($actor->getPHID())
|
||||
->setEditPolicy($actor->getPHID())
|
||||
->setIsDisabled(0)
|
||||
->setEngineType($engine->getImportEngineType())
|
||||
->attachEngine($engine);
|
||||
}
|
||||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
self::CONFIG_SERIALIZATION => array(
|
||||
'parameters' => self::SERIALIZATION_JSON,
|
||||
),
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'name' => 'text',
|
||||
'engineType' => 'text64',
|
||||
'isDisabled' => 'bool',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'key_author' => array(
|
||||
'columns' => array('authorPHID'),
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
public function getPHIDType() {
|
||||
return PhabricatorCalendarImportPHIDType::TYPECONST;
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
$id = $this->getID();
|
||||
return "/calendar/import/{$id}/";
|
||||
}
|
||||
|
||||
public function attachEngine(PhabricatorCalendarImportEngine $engine) {
|
||||
$this->engine = $engine;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEngine() {
|
||||
return $this->assertAttached($this->engine);
|
||||
}
|
||||
|
||||
public function getParameter($key, $default = null) {
|
||||
return idx($this->parameters, $key, $default);
|
||||
}
|
||||
|
||||
public function setParameter($key, $value) {
|
||||
$this->parameters[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDisplayName() {
|
||||
$name = $this->getName();
|
||||
if (strlen($name)) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
return $this->getEngine()->getDisplayName($this);
|
||||
}
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
||||
public function getCapabilities() {
|
||||
return array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
);
|
||||
}
|
||||
|
||||
public function getPolicy($capability) {
|
||||
switch ($capability) {
|
||||
case PhabricatorPolicyCapability::CAN_VIEW:
|
||||
return $this->getViewPolicy();
|
||||
case PhabricatorPolicyCapability::CAN_EDIT:
|
||||
return $this->getEditPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function describeAutomaticCapability($capability) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
||||
|
||||
|
||||
public function getApplicationTransactionEditor() {
|
||||
return new PhabricatorCalendarImportEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new PhabricatorCalendarImportTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
public function newLogMessage($type, array $parameters) {
|
||||
$parameters = array(
|
||||
'type' => $type,
|
||||
) + $parameters;
|
||||
|
||||
return id(new PhabricatorCalendarImportLog())
|
||||
->setImportPHID($this->getPHID())
|
||||
->setParameters($parameters)
|
||||
->save();
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||
|
||||
|
||||
public function destroyObjectPermanently(
|
||||
PhabricatorDestructionEngine $engine) {
|
||||
$viewer = $engine->getViewer();
|
||||
|
||||
$this->openTransaction();
|
||||
|
||||
$events = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($viewer)
|
||||
->withImportSourcePHIDs(array($this->getPHID()))
|
||||
->execute();
|
||||
foreach ($events as $event) {
|
||||
$engine->destroyObject($event);
|
||||
}
|
||||
|
||||
$logs = id(new PhabricatorCalendarImportLogQuery())
|
||||
->setViewer($viewer)
|
||||
->withImportPHIDs(array($this->getPHID()))
|
||||
->execute();
|
||||
foreach ($logs as $log) {
|
||||
$engine->destroyObject($log);
|
||||
}
|
||||
|
||||
$this->delete();
|
||||
$this->saveTransaction();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportLog
|
||||
extends PhabricatorCalendarDAO
|
||||
implements
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorDestructibleInterface {
|
||||
|
||||
protected $importPHID;
|
||||
protected $parameters = array();
|
||||
|
||||
private $import = self::ATTACHABLE;
|
||||
private $logType = self::ATTACHABLE;
|
||||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_SERIALIZATION => array(
|
||||
'parameters' => self::SERIALIZATION_JSON,
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'key_import' => array(
|
||||
'columns' => array('importPHID'),
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
public function getParameter($key, $default = null) {
|
||||
return idx($this->parameters, $key, $default);
|
||||
}
|
||||
|
||||
public function setParameter($key, $value) {
|
||||
$this->parameters[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getImport() {
|
||||
return $this->assertAttached($this->import);
|
||||
}
|
||||
|
||||
public function attachImport(PhabricatorCalendarImport $import) {
|
||||
$this->import = $import;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDisplayIcon(PhabricatorUser $viewer) {
|
||||
return $this->getLogType()->getDisplayIcon($viewer, $this);
|
||||
}
|
||||
|
||||
public function getDisplayColor(PhabricatorUser $viewer) {
|
||||
return $this->getLogType()->getDisplayColor($viewer, $this);
|
||||
}
|
||||
|
||||
public function getDisplayType(PhabricatorUser $viewer) {
|
||||
return $this->getLogType()->getDisplayType($viewer, $this);
|
||||
}
|
||||
|
||||
public function getDisplayDescription(PhabricatorUser $viewer) {
|
||||
return $this->getLogType()->getDisplayDescription($viewer, $this);
|
||||
}
|
||||
|
||||
public function getLogType() {
|
||||
return $this->assertAttached($this->logType);
|
||||
}
|
||||
|
||||
public function attachLogType(PhabricatorCalendarImportLogType $type) {
|
||||
$this->logType = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
||||
public function getCapabilities() {
|
||||
return array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
);
|
||||
}
|
||||
|
||||
public function getPolicy($capability) {
|
||||
return PhabricatorPolicies::getMostOpenPolicy();
|
||||
}
|
||||
|
||||
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function describeAutomaticCapability($capability) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||
|
||||
|
||||
public function destroyObjectPermanently(
|
||||
PhabricatorDestructionEngine $engine) {
|
||||
$viewer = $engine->getViewer();
|
||||
$this->delete();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportTransaction
|
||||
extends PhabricatorModularTransaction {
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'calendar';
|
||||
}
|
||||
|
||||
public function getApplicationTransactionType() {
|
||||
return PhabricatorCalendarImportPHIDType::TYPECONST;
|
||||
}
|
||||
|
||||
public function getBaseTransactionClass() {
|
||||
return 'PhabricatorCalendarImportTransactionType';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportLogView extends AphrontView {
|
||||
|
||||
private $logs = array();
|
||||
private $showImportSources = false;
|
||||
|
||||
public function setLogs(array $logs) {
|
||||
assert_instances_of($logs, 'PhabricatorCalendarImportLog');
|
||||
$this->logs = $logs;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLogs() {
|
||||
return $this->logs;
|
||||
}
|
||||
|
||||
public function setShowImportSources($show_import_sources) {
|
||||
$this->showImportSources = $show_import_sources;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getShowImportSources() {
|
||||
return $this->showImportSources;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
return $this->newTable();
|
||||
}
|
||||
|
||||
public function newTable() {
|
||||
$viewer = $this->getViewer();
|
||||
$logs = $this->getLogs();
|
||||
|
||||
$show_sources = $this->getShowImportSources();
|
||||
|
||||
$rows = array();
|
||||
foreach ($logs as $log) {
|
||||
$icon = $log->getDisplayIcon($viewer);
|
||||
$color = $log->getDisplayColor($viewer);
|
||||
$name = $log->getDisplayType($viewer);
|
||||
$description = $log->getDisplayDescription($viewer);
|
||||
|
||||
$rows[] = array(
|
||||
$log->getID(),
|
||||
($show_sources
|
||||
? $viewer->renderHandle($log->getImport()->getPHID())
|
||||
: null),
|
||||
id(new PHUIIconView())->setIcon($icon, $color),
|
||||
$name,
|
||||
phutil_escape_html_newlines($description),
|
||||
phabricator_datetime($log->getDateCreated(), $viewer),
|
||||
);
|
||||
}
|
||||
|
||||
$table = id(new AphrontTableView($rows))
|
||||
->setHeaders(
|
||||
array(
|
||||
pht('ID'),
|
||||
pht('Source'),
|
||||
null,
|
||||
pht('Type'),
|
||||
pht('Message'),
|
||||
pht('Date'),
|
||||
))
|
||||
->setColumnVisibility(
|
||||
array(
|
||||
true,
|
||||
$show_sources,
|
||||
))
|
||||
->setColumnClasses(
|
||||
array(
|
||||
'top',
|
||||
'top',
|
||||
'top',
|
||||
'top pri',
|
||||
'top wide',
|
||||
'top',
|
||||
));
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportDeleteTransaction
|
||||
extends PhabricatorCalendarImportTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'calendar.import.delete';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function applyExternalEffects($object, $value) {
|
||||
$events = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($this->getActor())
|
||||
->withImportSourcePHIDs(array($object->getPHID()))
|
||||
->execute();
|
||||
|
||||
$engine = new PhabricatorDestructionEngine();
|
||||
foreach ($events as $event) {
|
||||
$engine->destroyObject($event);
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s deleted imported events from this source.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportDisableTransaction
|
||||
extends PhabricatorCalendarImportTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'calendar.import.disable';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return (int)$object->getIsDisabled();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setIsDisabled((int)$value);
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
if ($this->getNewValue()) {
|
||||
return pht(
|
||||
'%s disabled this import.',
|
||||
$this->renderAuthor());
|
||||
} else {
|
||||
return pht(
|
||||
'%s enabled this import.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportICSFileTransaction
|
||||
extends PhabricatorCalendarImportTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'calendar.import.ics.file';
|
||||
const PARAMKEY_FILE = 'ics.filePHID';
|
||||
const PARAMKEY_NAME = 'ics.fileName';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->getParameter(self::PARAMKEY_FILE);
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setParameter(self::PARAMKEY_FILE, $value);
|
||||
|
||||
$viewer = $this->getActor();
|
||||
$file = id(new PhabricatorFileQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($value))
|
||||
->executeOne();
|
||||
if ($file) {
|
||||
$object->setParameter(self::PARAMKEY_NAME, $file->getName());
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s imported an ICS file.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
$viewer = $this->getActor();
|
||||
$errors = array();
|
||||
|
||||
$ics_type = PhabricatorCalendarICSFileImportEngine::ENGINETYPE;
|
||||
$import_type = $object->getEngine()->getImportEngineType();
|
||||
if ($import_type != $ics_type) {
|
||||
if (!$xactions) {
|
||||
return $errors;
|
||||
}
|
||||
|
||||
$errors[] = $this->newInvalidError(
|
||||
pht(
|
||||
'You can not attach an ICS file to an import type other than '.
|
||||
'an ICS import (type is "%s").',
|
||||
$import_type));
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
$new_value = $object->getParameter(self::PARAMKEY_FILE);
|
||||
foreach ($xactions as $xaction) {
|
||||
$new_value = $xaction->getNewValue();
|
||||
if (!strlen($new_value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$file = id(new PhabricatorFileQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($new_value))
|
||||
->executeOne();
|
||||
if (!$file) {
|
||||
$errors[] = $this->newInvalidError(
|
||||
pht(
|
||||
'File PHID "%s" is not valid or not visible.',
|
||||
$new_value),
|
||||
$xaction);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$new_value) {
|
||||
$errors[] = $this->newRequiredError(
|
||||
pht('You must select an ".ics" file to import.'));
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportICSURITransaction
|
||||
extends PhabricatorCalendarImportTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'calendar.import.ics.uri';
|
||||
const PARAMKEY_URI = 'ics.uri';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->getParameter(self::PARAMKEY_URI);
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setParameter(self::PARAMKEY_URI, $value);
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
// NOTE: This transaction intentionally does not disclose the actual
|
||||
// URI.
|
||||
return pht(
|
||||
'%s updated the import URI.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
$viewer = $this->getActor();
|
||||
$errors = array();
|
||||
|
||||
$ics_type = PhabricatorCalendarICSURIImportEngine::ENGINETYPE;
|
||||
$import_type = $object->getEngine()->getImportEngineType();
|
||||
if ($import_type != $ics_type) {
|
||||
if (!$xactions) {
|
||||
return $errors;
|
||||
}
|
||||
|
||||
$errors[] = $this->newInvalidError(
|
||||
pht(
|
||||
'You can not attach an ICS URI to an import type other than '.
|
||||
'an ICS URI import (type is "%s").',
|
||||
$import_type));
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
$new_value = $object->getParameter(self::PARAMKEY_URI);
|
||||
foreach ($xactions as $xaction) {
|
||||
$new_value = $xaction->getNewValue();
|
||||
if (!strlen($new_value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
PhabricatorEnv::requireValidRemoteURIForFetch(
|
||||
$new_value,
|
||||
array(
|
||||
'http',
|
||||
'https',
|
||||
));
|
||||
} catch (Exception $ex) {
|
||||
$errors[] = $this->newInvalidError(
|
||||
$ex->getMessage(),
|
||||
$xaction);
|
||||
}
|
||||
}
|
||||
|
||||
if (!strlen($new_value)) {
|
||||
$errors[] = $this->newRequiredError(
|
||||
pht('You must select an ".ics" URI to import.'));
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarImportNameTransaction
|
||||
extends PhabricatorCalendarImportTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'calendar.import.name';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->getName();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setName($value);
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
if (!strlen($old)) {
|
||||
return pht(
|
||||
'%s named this import %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderNewValue());
|
||||
} else if (!strlen($new)) {
|
||||
return pht(
|
||||
'%s removed the name of this import (was: %s).',
|
||||
$this->renderAuthor(),
|
||||
$this->renderOldValue());
|
||||
} else {
|
||||
return pht(
|
||||
'%s renamed this import from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderOldValue(),
|
||||
$this->renderNewValue());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorCalendarImportTransactionType
|
||||
extends PhabricatorModularTransactionType {}
|
|
@ -145,6 +145,7 @@ abstract class CelerityResourceController extends PhabricatorController {
|
|||
'eot' => 'font/eot',
|
||||
'ttf' => 'font/ttf',
|
||||
'mp3' => 'audio/mpeg',
|
||||
'ico' => 'image/x-icon',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ abstract class CelerityResourcesOnDisk extends CelerityPhysicalResources {
|
|||
'ttf',
|
||||
'eot',
|
||||
'mp3',
|
||||
'ico',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ final class ConduitCall extends Phobject {
|
|||
private $request;
|
||||
private $user;
|
||||
|
||||
public function __construct($method, array $params) {
|
||||
public function __construct($method, array $params, $strictly_typed = true) {
|
||||
$this->method = $method;
|
||||
$this->handler = $this->buildMethodHandler($method);
|
||||
|
||||
|
@ -41,7 +41,7 @@ final class ConduitCall extends Phobject {
|
|||
"'".implode("', '", array_keys($invalid_params))."'"));
|
||||
}
|
||||
|
||||
$this->request = new ConduitAPIRequest($params);
|
||||
$this->request = new ConduitAPIRequest($params, $strictly_typed);
|
||||
}
|
||||
|
||||
public function getAPIRequest() {
|
||||
|
|
|
@ -25,9 +25,11 @@ final class PhabricatorConduitAPIController
|
|||
|
||||
try {
|
||||
|
||||
list($metadata, $params) = $this->decodeConduitParams($request, $method);
|
||||
list($metadata, $params, $strictly_typed) = $this->decodeConduitParams(
|
||||
$request,
|
||||
$method);
|
||||
|
||||
$call = new ConduitCall($method, $params);
|
||||
$call = new ConduitCall($method, $params, $strictly_typed);
|
||||
$method_implementation = $call->getMethodImplementation();
|
||||
|
||||
$result = null;
|
||||
|
@ -638,7 +640,7 @@ final class PhabricatorConduitAPIController
|
|||
$metadata = idx($params, '__conduit__', array());
|
||||
unset($params['__conduit__']);
|
||||
|
||||
return array($metadata, $params);
|
||||
return array($metadata, $params, true);
|
||||
}
|
||||
|
||||
// Otherwise, look for a single parameter called 'params' which has the
|
||||
|
@ -659,7 +661,7 @@ final class PhabricatorConduitAPIController
|
|||
$metadata = idx($params, '__conduit__', array());
|
||||
unset($params['__conduit__']);
|
||||
|
||||
return array($metadata, $params);
|
||||
return array($metadata, $params, true);
|
||||
}
|
||||
|
||||
// If we do not have `params`, assume this is a simple HTTP request with
|
||||
|
@ -675,7 +677,7 @@ final class PhabricatorConduitAPIController
|
|||
}
|
||||
}
|
||||
|
||||
return array($metadata, $params);
|
||||
return array($metadata, $params, false);
|
||||
}
|
||||
|
||||
private function authorizeOAuthMethodAccess(
|
||||
|
|
|
@ -3,17 +3,9 @@
|
|||
final class ConduitBoolParameterType
|
||||
extends ConduitParameterType {
|
||||
|
||||
protected function getParameterValue(array $request, $key) {
|
||||
$value = parent::getParameterValue($request, $key);
|
||||
|
||||
if (!is_bool($value)) {
|
||||
$this->raiseValidationException(
|
||||
$request,
|
||||
$key,
|
||||
pht('Expected boolean (true or false), got something else.'));
|
||||
}
|
||||
|
||||
return $value;
|
||||
protected function getParameterValue(array $request, $key, $strict) {
|
||||
$value = parent::getParameterValue($request, $key, $strict);
|
||||
return $this->parseBoolValue($request, $key, $value, $strict);
|
||||
}
|
||||
|
||||
protected function getParameterTypeName() {
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
final class ConduitColumnsParameterType
|
||||
extends ConduitParameterType {
|
||||
|
||||
protected function getParameterValue(array $request, $key) {
|
||||
protected function getParameterValue(array $request, $key, $strict) {
|
||||
// We don't do any meaningful validation here because the transaction
|
||||
// itself validates everything and the input format is flexible.
|
||||
return parent::getParameterValue($request, $key);
|
||||
return parent::getParameterValue($request, $key, $strict);
|
||||
}
|
||||
|
||||
protected function getParameterTypeName() {
|
||||
|
|
|
@ -3,15 +3,9 @@
|
|||
final class ConduitEpochParameterType
|
||||
extends ConduitParameterType {
|
||||
|
||||
protected function getParameterValue(array $request, $key) {
|
||||
$value = parent::getParameterValue($request, $key);
|
||||
|
||||
if (!is_int($value)) {
|
||||
$this->raiseValidationException(
|
||||
$request,
|
||||
$key,
|
||||
pht('Expected epoch timestamp as integer, got something else.'));
|
||||
}
|
||||
protected function getParameterValue(array $request, $key, $strict) {
|
||||
$value = parent::getParameterValue($request, $key, $strict);
|
||||
$value = $this->parseIntValue($request, $key, $value, $strict);
|
||||
|
||||
if ($value <= 0) {
|
||||
$this->raiseValidationException(
|
||||
|
|
|
@ -3,19 +3,15 @@
|
|||
final class ConduitIntListParameterType
|
||||
extends ConduitListParameterType {
|
||||
|
||||
protected function getParameterValue(array $request, $key) {
|
||||
$list = parent::getParameterValue($request, $key);
|
||||
protected function getParameterValue(array $request, $key, $strict) {
|
||||
$list = parent::getParameterValue($request, $key, $strict);
|
||||
|
||||
foreach ($list as $idx => $item) {
|
||||
if (!is_int($item)) {
|
||||
$this->raiseValidationException(
|
||||
$list[$idx] = $this->parseIntValue(
|
||||
$request,
|
||||
$key,
|
||||
pht(
|
||||
'Expected a list of integers, but item with index "%s" is '.
|
||||
'not an integer.',
|
||||
$idx));
|
||||
}
|
||||
$key.'['.$idx.']',
|
||||
$item,
|
||||
$strict);
|
||||
}
|
||||
|
||||
return $list;
|
||||
|
|
|
@ -3,17 +3,9 @@
|
|||
final class ConduitIntParameterType
|
||||
extends ConduitParameterType {
|
||||
|
||||
protected function getParameterValue(array $request, $key) {
|
||||
$value = parent::getParameterValue($request, $key);
|
||||
|
||||
if (!is_int($value)) {
|
||||
$this->raiseValidationException(
|
||||
$request,
|
||||
$key,
|
||||
pht('Expected integer, got something else.'));
|
||||
}
|
||||
|
||||
return $value;
|
||||
protected function getParameterValue(array $request, $key, $strict) {
|
||||
$value = parent::getParameterValue($request, $key, $strict);
|
||||
return $this->parseIntValue($request, $key, $value, $strict);
|
||||
}
|
||||
|
||||
protected function getParameterTypeName() {
|
||||
|
|
|
@ -14,8 +14,8 @@ abstract class ConduitListParameterType
|
|||
return $this->allowEmptyList;
|
||||
}
|
||||
|
||||
protected function getParameterValue(array $request, $key) {
|
||||
$value = parent::getParameterValue($request, $key);
|
||||
protected function getParameterValue(array $request, $key, $strict) {
|
||||
$value = parent::getParameterValue($request, $key, $strict);
|
||||
|
||||
if (!is_array($value)) {
|
||||
$this->raiseValidationException(
|
||||
|
@ -48,17 +48,18 @@ abstract class ConduitListParameterType
|
|||
return $value;
|
||||
}
|
||||
|
||||
protected function validateStringList(array $request, $key, array $list) {
|
||||
foreach ($list as $idx => $item) {
|
||||
if (!is_string($item)) {
|
||||
$this->raiseValidationException(
|
||||
$request,
|
||||
protected function parseStringList(
|
||||
array $request,
|
||||
$key,
|
||||
pht(
|
||||
'Expected a list of strings, but item with index "%s" is '.
|
||||
'not a string.',
|
||||
$idx));
|
||||
}
|
||||
array $list,
|
||||
$strict) {
|
||||
|
||||
foreach ($list as $idx => $item) {
|
||||
$list[$idx] = $this->parseStringValue(
|
||||
$request,
|
||||
$key.'['.$idx.']',
|
||||
$item,
|
||||
$strict);
|
||||
}
|
||||
|
||||
return $list;
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
final class ConduitPHIDListParameterType
|
||||
extends ConduitListParameterType {
|
||||
|
||||
protected function getParameterValue(array $request, $key) {
|
||||
$list = parent::getParameterValue($request, $key);
|
||||
return $this->validateStringList($request, $key, $list);
|
||||
protected function getParameterValue(array $request, $key, $strict) {
|
||||
$list = parent::getParameterValue($request, $key, $strict);
|
||||
return $this->parseStringList($request, $key, $list, $strict);
|
||||
}
|
||||
|
||||
protected function getParameterTypeName() {
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
final class ConduitPHIDParameterType
|
||||
extends ConduitParameterType {
|
||||
|
||||
protected function getParameterValue(array $request, $key) {
|
||||
$value = parent::getParameterValue($request, $key);
|
||||
protected function getParameterValue(array $request, $key, $strict) {
|
||||
$value = parent::getParameterValue($request, $key, $strict);
|
||||
|
||||
if (!is_string($value)) {
|
||||
$this->raiseValidationException(
|
||||
|
|
|
@ -30,12 +30,12 @@ abstract class ConduitParameterType extends Phobject {
|
|||
}
|
||||
|
||||
|
||||
final public function getValue(array $request, $key) {
|
||||
final public function getValue(array $request, $key, $strict = true) {
|
||||
if (!$this->getExists($request, $key)) {
|
||||
return $this->getParameterDefault();
|
||||
}
|
||||
|
||||
return $this->getParameterValue($request, $key);
|
||||
return $this->getParameterValue($request, $key, $strict);
|
||||
}
|
||||
|
||||
final public function getKeys($key) {
|
||||
|
@ -85,7 +85,7 @@ abstract class ConduitParameterType extends Phobject {
|
|||
return array_key_exists($key, $request);
|
||||
}
|
||||
|
||||
protected function getParameterValue(array $request, $key) {
|
||||
protected function getParameterValue(array $request, $key, $strict) {
|
||||
return $request[$key];
|
||||
}
|
||||
|
||||
|
@ -93,6 +93,53 @@ abstract class ConduitParameterType extends Phobject {
|
|||
return array($key);
|
||||
}
|
||||
|
||||
protected function parseStringValue(array $request, $key, $value, $strict) {
|
||||
if (!is_string($value)) {
|
||||
$this->raiseValidationException(
|
||||
$request,
|
||||
$key,
|
||||
pht('Expected string, got something else.'));
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function parseIntValue(array $request, $key, $value, $strict) {
|
||||
if (!$strict && is_string($value) && ctype_digit($value)) {
|
||||
$value = $value + 0;
|
||||
if (!is_int($value)) {
|
||||
$this->raiseValidationException(
|
||||
$request,
|
||||
$key,
|
||||
pht('Integer overflow.'));
|
||||
}
|
||||
} else if (!is_int($value)) {
|
||||
$this->raiseValidationException(
|
||||
$request,
|
||||
$key,
|
||||
pht('Expected integer, got something else.'));
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function parseBoolValue(array $request, $key, $value, $strict) {
|
||||
$bool_strings = array(
|
||||
'0' => false,
|
||||
'1' => true,
|
||||
'false' => false,
|
||||
'true' => true,
|
||||
);
|
||||
|
||||
if (!$strict && is_string($value) && isset($bool_strings[$value])) {
|
||||
$value = $bool_strings[$value];
|
||||
} else if (!is_bool($value)) {
|
||||
$this->raiseValidationException(
|
||||
$request,
|
||||
$key,
|
||||
pht('Expected boolean (true or false), got something else.'));
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
abstract protected function getParameterTypeName();
|
||||
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
final class ConduitPointsParameterType
|
||||
extends ConduitParameterType {
|
||||
|
||||
protected function getParameterValue(array $request, $key) {
|
||||
$value = parent::getParameterValue($request, $key);
|
||||
protected function getParameterValue(array $request, $key, $strict) {
|
||||
$value = parent::getParameterValue($request, $key, $strict);
|
||||
|
||||
if (($value !== null) && !is_numeric($value)) {
|
||||
$this->raiseValidationException(
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
final class ConduitProjectListParameterType
|
||||
extends ConduitListParameterType {
|
||||
|
||||
protected function getParameterValue(array $request, $key) {
|
||||
$list = parent::getParameterValue($request, $key);
|
||||
$list = $this->validateStringList($request, $key, $list);
|
||||
protected function getParameterValue(array $request, $key, $strict) {
|
||||
$list = parent::getParameterValue($request, $key, $strict);
|
||||
$list = $this->parseStringList($request, $key, $list, $strict);
|
||||
return id(new PhabricatorProjectPHIDResolver())
|
||||
->setViewer($this->getViewer())
|
||||
->resolvePHIDs($list);
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
final class ConduitStringListParameterType
|
||||
extends ConduitListParameterType {
|
||||
|
||||
protected function getParameterValue(array $request, $key) {
|
||||
$list = parent::getParameterValue($request, $key);
|
||||
return $this->validateStringList($request, $key, $list);
|
||||
protected function getParameterValue(array $request, $key, $strict) {
|
||||
$list = parent::getParameterValue($request, $key, $strict);
|
||||
return $this->parseStringList($request, $key, $list, $strict);
|
||||
}
|
||||
|
||||
protected function getParameterTypeName() {
|
||||
|
|
|
@ -3,17 +3,9 @@
|
|||
final class ConduitStringParameterType
|
||||
extends ConduitParameterType {
|
||||
|
||||
protected function getParameterValue(array $request, $key) {
|
||||
$value = parent::getParameterValue($request, $key);
|
||||
|
||||
if (!is_string($value)) {
|
||||
$this->raiseValidationException(
|
||||
$request,
|
||||
$key,
|
||||
pht('Expected string, got something else.'));
|
||||
}
|
||||
|
||||
return $value;
|
||||
protected function getParameterValue(array $request, $key, $strict) {
|
||||
$value = parent::getParameterValue($request, $key, $strict);
|
||||
return $this->parseStringValue($request, $key, $value, $strict);
|
||||
}
|
||||
|
||||
protected function getParameterTypeName() {
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
final class ConduitUserListParameterType
|
||||
extends ConduitListParameterType {
|
||||
|
||||
protected function getParameterValue(array $request, $key) {
|
||||
$list = parent::getParameterValue($request, $key);
|
||||
$list = $this->validateStringList($request, $key, $list);
|
||||
protected function getParameterValue(array $request, $key, $strict) {
|
||||
$list = parent::getParameterValue($request, $key, $strict);
|
||||
$list = $this->parseStringList($request, $key, $list, $strict);
|
||||
return id(new PhabricatorUserPHIDResolver())
|
||||
->setViewer($this->getViewer())
|
||||
->resolvePHIDs($list);
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
final class ConduitUserParameterType
|
||||
extends ConduitParameterType {
|
||||
|
||||
protected function getParameterValue(array $request, $key) {
|
||||
$value = parent::getParameterValue($request, $key);
|
||||
protected function getParameterValue(array $request, $key, $strict) {
|
||||
$value = parent::getParameterValue($request, $key, $strict);
|
||||
|
||||
if ($value === null) {
|
||||
return null;
|
||||
|
|
|
@ -6,9 +6,11 @@ final class ConduitAPIRequest extends Phobject {
|
|||
private $user;
|
||||
private $isClusterRequest = false;
|
||||
private $oauthToken;
|
||||
private $isStrictlyTyped = true;
|
||||
|
||||
public function __construct(array $params) {
|
||||
public function __construct(array $params, $strictly_typed) {
|
||||
$this->params = $params;
|
||||
$this->isStrictlyTyped = $strictly_typed;
|
||||
}
|
||||
|
||||
public function getValue($key, $default = null) {
|
||||
|
@ -68,6 +70,10 @@ final class ConduitAPIRequest extends Phobject {
|
|||
return $this->isClusterRequest;
|
||||
}
|
||||
|
||||
public function getIsStrictlyTyped() {
|
||||
return $this->isStrictlyTyped;
|
||||
}
|
||||
|
||||
public function newContentSource() {
|
||||
return PhabricatorContentSource::newForSource(
|
||||
PhabricatorConduitContentSource::SOURCECONST);
|
||||
|
|
|
@ -5,7 +5,6 @@ final class ConpherenceTransactionRenderer extends Phobject {
|
|||
public static function renderTransactions(
|
||||
PhabricatorUser $user,
|
||||
ConpherenceThread $conpherence,
|
||||
$full_display = true,
|
||||
$marker_type = 'older') {
|
||||
|
||||
$transactions = $conpherence->getTransactions();
|
||||
|
@ -74,8 +73,7 @@ final class ConpherenceTransactionRenderer extends Phobject {
|
|||
->setUser($user)
|
||||
->setConpherenceThread($conpherence)
|
||||
->setHandles($handles)
|
||||
->setMarkupEngine($engine)
|
||||
->setFullDisplay($full_display);
|
||||
->setMarkupEngine($engine);
|
||||
|
||||
foreach ($transactions as $transaction) {
|
||||
$collapsed = false;
|
||||
|
|
|
@ -37,6 +37,8 @@ final class PhabricatorConpherenceApplication extends PhabricatorApplication {
|
|||
=> 'ConpherenceListController',
|
||||
'thread/(?P<id>[1-9]\d*)/'
|
||||
=> 'ConpherenceListController',
|
||||
'threadsearch/(?P<id>[1-9]\d*)/'
|
||||
=> 'ConpherenceThreadSearchController',
|
||||
'(?P<id>[1-9]\d*)/'
|
||||
=> 'ConpherenceViewController',
|
||||
'(?P<id>[1-9]\d*)/(?P<messageID>[1-9]\d*)/'
|
||||
|
|
|
@ -9,7 +9,7 @@ final class ConpherenceColumnViewController extends
|
|||
$latest_conpherences = array();
|
||||
$latest_participant = id(new ConpherenceParticipantQuery())
|
||||
->withParticipantPHIDs(array($user->getPHID()))
|
||||
->setLimit(6)
|
||||
->setLimit(8)
|
||||
->execute();
|
||||
if ($latest_participant) {
|
||||
$conpherence_phids = mpull($latest_participant, 'getConpherencePHID');
|
||||
|
|
|
@ -18,6 +18,12 @@ abstract class ConpherenceController extends PhabricatorController {
|
|||
|
||||
// Local Links
|
||||
if ($conpherence) {
|
||||
$nav->addMenuItem(
|
||||
id(new PHUIListItemView())
|
||||
->setName(pht('Joined Rooms'))
|
||||
->setType(PHUIListItemView::TYPE_LINK)
|
||||
->setHref($this->getApplicationURI()));
|
||||
|
||||
$nav->addMenuItem(
|
||||
id(new PHUIListItemView())
|
||||
->setName(pht('Edit Room'))
|
||||
|
@ -113,6 +119,16 @@ abstract class ConpherenceController extends PhabricatorController {
|
|||
->setHref('#')
|
||||
->addClass('conpherence-participant-toggle'));
|
||||
|
||||
Javelin::initBehavior('conpherence-search');
|
||||
|
||||
$header->addActionItem(
|
||||
id(new PHUIIconCircleView())
|
||||
->addSigil('conpherence-search-toggle')
|
||||
->setIcon('fa-search')
|
||||
->setHref('#')
|
||||
->setColor('green')
|
||||
->addClass('conpherence-search-toggle'));
|
||||
|
||||
if ($can_join && !$participating) {
|
||||
$action = ConpherenceUpdateActions::JOIN_ROOM;
|
||||
$uri = $this->getApplicationURI('update/'.$conpherence->getID().'/');
|
||||
|
@ -149,4 +165,61 @@ abstract class ConpherenceController extends PhabricatorController {
|
|||
return $header;
|
||||
}
|
||||
|
||||
public function buildSearchForm() {
|
||||
$viewer = $this->getViewer();
|
||||
$conpherence = $this->conpherence;
|
||||
$name = $conpherence->getTitle();
|
||||
|
||||
$bar = javelin_tag(
|
||||
'input',
|
||||
array(
|
||||
'type' => 'text',
|
||||
'id' => 'conpherence-search-input',
|
||||
'name' => 'fulltext',
|
||||
'class' => 'conpherence-search-input',
|
||||
'sigil' => 'conpherence-search-input',
|
||||
'placeholder' => pht('Search %s...', $name),
|
||||
));
|
||||
|
||||
$id = $conpherence->getID();
|
||||
$form = phabricator_form(
|
||||
$viewer,
|
||||
array(
|
||||
'method' => 'POST',
|
||||
'action' => '/conpherence/threadsearch/'.$id.'/',
|
||||
'sigil' => 'conpherence-search-form',
|
||||
'class' => 'conpherence-search-form',
|
||||
'id' => 'conpherence-search-form',
|
||||
),
|
||||
array(
|
||||
$bar,
|
||||
));
|
||||
|
||||
$form_view = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'conpherence-search-form-view',
|
||||
),
|
||||
$form);
|
||||
|
||||
$results = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'id' => 'conpherence-search-results',
|
||||
'class' => 'conpherence-search-results',
|
||||
));
|
||||
|
||||
$view = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'conpherence-search-window',
|
||||
),
|
||||
array(
|
||||
$form_view,
|
||||
$results,
|
||||
));
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ final class ConpherenceNotificationPanelController
|
|||
->withPHIDs(array_keys($participant_data))
|
||||
->needProfileImage(true)
|
||||
->needTransactions(true)
|
||||
->setTransactionLimit(3 * 5)
|
||||
->setTransactionLimit(50)
|
||||
->needParticipantCache(true)
|
||||
->execute();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
final class ConpherenceThreadSearchController
|
||||
extends ConpherenceController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
$conpherence_id = $request->getURIData('id');
|
||||
$fulltext = $request->getStr('fulltext');
|
||||
|
||||
$conpherence = id(new ConpherenceThreadQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($conpherence_id))
|
||||
->executeOne();
|
||||
|
||||
if (!$conpherence) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$engine = new ConpherenceThreadSearchEngine();
|
||||
$engine->setViewer($viewer);
|
||||
$saved = $engine->buildSavedQueryFromBuiltin('all')
|
||||
->setParameter('phids', array($conpherence->getPHID()))
|
||||
->setParameter('fulltext', $fulltext);
|
||||
|
||||
$pager = $engine->newPagerForSavedQuery($saved);
|
||||
$pager->setPageSize(15);
|
||||
|
||||
$query = $engine->buildQueryFromSavedQuery($saved);
|
||||
|
||||
$results = $engine->executeQuery($query, $pager);
|
||||
$view = $engine->renderResults($results, $saved);
|
||||
|
||||
return id(new AphrontAjaxResponse())
|
||||
->setContent($view->getContent());
|
||||
}
|
||||
}
|
|
@ -327,7 +327,6 @@ final class ConpherenceUpdateController
|
|||
->setUser($user)
|
||||
->setDatasource(new PhabricatorPeopleDatasource()));
|
||||
|
||||
require_celerity_resource('conpherence-update-css');
|
||||
$view = id(new AphrontDialogView())
|
||||
->setTitle(pht('Add Participants'))
|
||||
->addHiddenInput('action', 'add_person')
|
||||
|
@ -336,9 +335,6 @@ final class ConpherenceUpdateController
|
|||
$request->getInt('latest_transaction_id'))
|
||||
->appendForm($form);
|
||||
|
||||
if ($request->getExists('minimal_display')) {
|
||||
$view->addHiddenInput('minimal_display', true);
|
||||
}
|
||||
return $view;
|
||||
}
|
||||
|
||||
|
@ -407,8 +403,6 @@ final class ConpherenceUpdateController
|
|||
}
|
||||
}
|
||||
|
||||
require_celerity_resource('conpherence-update-css');
|
||||
|
||||
$dialog = id(new AphrontDialogView())
|
||||
->setTitle($title)
|
||||
->addHiddenInput('action', 'remove_person')
|
||||
|
@ -471,7 +465,6 @@ final class ConpherenceUpdateController
|
|||
->setCapability(PhabricatorPolicyCapability::CAN_JOIN)
|
||||
->setPolicies($policies));
|
||||
|
||||
require_celerity_resource('conpherence-update-css');
|
||||
$view = id(new AphrontDialogView())
|
||||
->setTitle($title)
|
||||
->addHiddenInput('action', 'metadata')
|
||||
|
@ -481,9 +474,6 @@ final class ConpherenceUpdateController
|
|||
->addHiddenInput('__continue__', true)
|
||||
->appendChild($form);
|
||||
|
||||
if ($request->getExists('minimal_display')) {
|
||||
$view->addHiddenInput('minimal_display', true);
|
||||
}
|
||||
if ($request->getExists('force_ajax')) {
|
||||
$view->addHiddenInput('force_ajax', true);
|
||||
}
|
||||
|
@ -496,7 +486,6 @@ final class ConpherenceUpdateController
|
|||
$conpherence_id,
|
||||
$latest_transaction_id) {
|
||||
|
||||
$minimal_display = $this->getRequest()->getExists('minimal_display');
|
||||
$need_transactions = false;
|
||||
$need_participant_cache = true;
|
||||
switch ($action) {
|
||||
|
@ -529,8 +518,7 @@ final class ConpherenceUpdateController
|
|||
if ($need_transactions && $conpherence->getTransactions()) {
|
||||
$data = ConpherenceTransactionRenderer::renderTransactions(
|
||||
$user,
|
||||
$conpherence,
|
||||
!$minimal_display);
|
||||
$conpherence);
|
||||
$key = PhabricatorConpherenceColumnMinimizeSetting::SETTINGKEY;
|
||||
$minimized = $user->getUserSetting($key);
|
||||
if (!$minimized) {
|
||||
|
@ -551,7 +539,6 @@ final class ConpherenceUpdateController
|
|||
$nav_item = null;
|
||||
$header = null;
|
||||
$people_widget = null;
|
||||
if (!$minimal_display) {
|
||||
switch ($action) {
|
||||
case ConpherenceUpdateActions::METADATA:
|
||||
$policy_objects = id(new PhabricatorPolicyQuery())
|
||||
|
@ -580,7 +567,6 @@ final class ConpherenceUpdateController
|
|||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
$data = $conpherence->getDisplayData($user);
|
||||
$dropdown_query = id(new AphlictDropdownDataQuery())
|
||||
->setViewer($user);
|
||||
|
|
|
@ -73,7 +73,6 @@ final class ConpherenceViewController extends
|
|||
$data = ConpherenceTransactionRenderer::renderTransactions(
|
||||
$user,
|
||||
$conpherence,
|
||||
$full_display = true,
|
||||
$marker_type);
|
||||
$messages = ConpherenceTransactionRenderer::renderMessagePaneContent(
|
||||
$data['transactions'],
|
||||
|
@ -89,9 +88,11 @@ final class ConpherenceViewController extends
|
|||
->setObject($conpherence)
|
||||
->execute();
|
||||
$header = $this->buildHeaderPaneContent($conpherence, $policy_objects);
|
||||
$search = $this->buildSearchForm();
|
||||
$form = $this->renderFormContent();
|
||||
$content = array(
|
||||
'header' => $header,
|
||||
'search' => $search,
|
||||
'transactions' => $messages,
|
||||
'form' => $form,
|
||||
);
|
||||
|
@ -128,6 +129,7 @@ final class ConpherenceViewController extends
|
|||
->setBaseURI($this->getApplicationURI())
|
||||
->setThread($conpherence)
|
||||
->setHeader($header)
|
||||
->setSearch($search)
|
||||
->setMessages($messages)
|
||||
->setReplyForm($form)
|
||||
->setLatestTransactionID($data['latest_transaction_id'])
|
||||
|
|
|
@ -76,6 +76,12 @@ final class ConpherenceThreadQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withTitleNgrams($ngrams) {
|
||||
return $this->withNgramsConstraint(
|
||||
id(new ConpherenceThreadTitleNgrams()),
|
||||
$ngrams);
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$table = new ConpherenceThread();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
|
@ -145,61 +151,100 @@ final class ConpherenceThreadQuery
|
|||
}
|
||||
}
|
||||
|
||||
protected function buildJoinClause(AphrontDatabaseConnection $conn_r) {
|
||||
$joins = array();
|
||||
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$joins = parent::buildJoinClauseParts($conn);
|
||||
|
||||
if ($this->participantPHIDs !== null) {
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'JOIN %T p ON p.conpherencePHID = thread.phid',
|
||||
id(new ConpherenceParticipant())->getTableName());
|
||||
}
|
||||
|
||||
if (strlen($this->fulltext)) {
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'JOIN %T idx ON idx.threadPHID = thread.phid',
|
||||
id(new ConpherenceIndex())->getTableName());
|
||||
}
|
||||
|
||||
$joins[] = $this->buildApplicationSearchJoinClause($conn_r);
|
||||
return implode(' ', $joins);
|
||||
// See note in buildWhereClauseParts() about this optimization.
|
||||
$viewer = $this->getViewer();
|
||||
if (!$viewer->isOmnipotent() && $viewer->isLoggedIn()) {
|
||||
$joins[] = qsprintf(
|
||||
$conn,
|
||||
'LEFT JOIN %T vp ON vp.conpherencePHID = thread.phid
|
||||
AND vp.participantPHID = %s',
|
||||
id(new ConpherenceParticipant())->getTableName(),
|
||||
$viewer->getPHID());
|
||||
}
|
||||
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||
$where = array();
|
||||
return $joins;
|
||||
}
|
||||
|
||||
$where[] = $this->buildPagingClause($conn_r);
|
||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
// Optimize policy filtering of private rooms. If we are not looking for
|
||||
// particular rooms by ID or PHID, we can just skip over any rooms with
|
||||
// "View Policy: Room Participants" if the viewer isn't a participant: we
|
||||
// know they won't be able to see the room.
|
||||
// This avoids overheating browse/search queries, since it's common for
|
||||
// a large number of rooms to be private and have this view policy.
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$can_optimize =
|
||||
!$viewer->isOmnipotent() &&
|
||||
($this->ids === null) &&
|
||||
($this->phids === null);
|
||||
|
||||
if ($can_optimize) {
|
||||
$members_policy = id(new ConpherenceThreadMembersPolicyRule())
|
||||
->getObjectPolicyFullKey();
|
||||
|
||||
if ($viewer->isLoggedIn()) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'thread.viewPolicy != %s OR vp.participantPHID = %s',
|
||||
$members_policy,
|
||||
$viewer->getPHID());
|
||||
} else {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'thread.viewPolicy != %s',
|
||||
$members_policy);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'thread.id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'thread.phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->participantPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'p.participantPHID IN (%Ls)',
|
||||
$this->participantPHIDs);
|
||||
}
|
||||
|
||||
if (strlen($this->fulltext)) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'MATCH(idx.corpus) AGAINST (%s IN BOOLEAN MODE)',
|
||||
$this->fulltext);
|
||||
}
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
return $where;
|
||||
}
|
||||
|
||||
private function loadParticipantsAndInitHandles(array $conpherences) {
|
||||
|
|
|
@ -13,7 +13,8 @@ final class ConpherenceThreadSearchEngine
|
|||
|
||||
public function newQuery() {
|
||||
return id(new ConpherenceThreadQuery())
|
||||
->needParticipantCache(true);
|
||||
->needParticipantCache(true)
|
||||
->needProfileImage(true);
|
||||
}
|
||||
|
||||
protected function buildCustomSearchFields() {
|
||||
|
@ -22,8 +23,13 @@ final class ConpherenceThreadSearchEngine
|
|||
->setLabel(pht('Participants'))
|
||||
->setKey('participants')
|
||||
->setAliases(array('participant')),
|
||||
id(new PhabricatorSearchDatasourceField())
|
||||
->setLabel(pht('Rooms'))
|
||||
->setKey('phids')
|
||||
->setDescription(pht('Search by room titles.'))
|
||||
->setDatasource(id(new ConpherenceThreadDatasource())),
|
||||
id(new PhabricatorSearchTextField())
|
||||
->setLabel(pht('Contains Words'))
|
||||
->setLabel(pht('Room Contains Words'))
|
||||
->setKey('fulltext'),
|
||||
);
|
||||
}
|
||||
|
@ -47,7 +53,9 @@ final class ConpherenceThreadSearchEngine
|
|||
if ($map['fulltext']) {
|
||||
$query->withFulltext($map['fulltext']);
|
||||
}
|
||||
|
||||
if ($map['phids']) {
|
||||
$query->withPHIDs($map['phids']);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
@ -140,6 +148,7 @@ final class ConpherenceThreadSearchEngine
|
|||
$context = array();
|
||||
}
|
||||
|
||||
$content = array();
|
||||
$list = new PHUIObjectItemListView();
|
||||
$list->setUser($viewer);
|
||||
foreach ($conpherences as $conpherence_phid => $conpherence) {
|
||||
|
@ -150,11 +159,14 @@ final class ConpherenceThreadSearchEngine
|
|||
$icon_name = $conpherence->getPolicyIconName($policy_objects);
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIcon($icon_name);
|
||||
|
||||
if (!strlen($fulltext)) {
|
||||
$item = id(new PHUIObjectItemView())
|
||||
->setObjectName($conpherence->getMonogram())
|
||||
->setHeader($title)
|
||||
->setHref('/'.$conpherence->getMonogram())
|
||||
->setObject($conpherence)
|
||||
->setImageURI($conpherence->getProfileImageURI())
|
||||
->addIcon('none', $created)
|
||||
->addIcon(
|
||||
'none',
|
||||
|
@ -167,8 +179,11 @@ final class ConpherenceThreadSearchEngine
|
|||
'Last updated %s',
|
||||
phabricator_datetime($conpherence->getDateModified(), $viewer)),
|
||||
));
|
||||
|
||||
$list->addItem($item);
|
||||
} else {
|
||||
$messages = idx($context, $conpherence_phid);
|
||||
$box = array();
|
||||
$list = null;
|
||||
if ($messages) {
|
||||
foreach ($messages as $group) {
|
||||
$rows = array();
|
||||
|
@ -184,7 +199,7 @@ final class ConpherenceThreadSearchEngine
|
|||
->setMarkupEngine($engines[$conpherence_phid])
|
||||
->setConpherenceThread($conpherence)
|
||||
->setConpherenceTransaction($xaction)
|
||||
->setFullDisplay(false)
|
||||
->setSearchResult(true)
|
||||
->addClass('conpherence-fulltext-result');
|
||||
|
||||
if ($message['match']) {
|
||||
|
@ -193,20 +208,33 @@ final class ConpherenceThreadSearchEngine
|
|||
|
||||
$rows[] = $view;
|
||||
}
|
||||
|
||||
$box = id(new PHUIBoxView())
|
||||
$box[] = id(new PHUIBoxView())
|
||||
->appendChild($rows)
|
||||
->addClass('conpherence-fulltext-results');
|
||||
$item->appendChild($box);
|
||||
}
|
||||
}
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader($title)
|
||||
->setHeaderIcon($icon_name)
|
||||
->setHref('/'.$monogram);
|
||||
|
||||
$content[] = id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->appendChild($box);
|
||||
}
|
||||
}
|
||||
|
||||
$list->addItem($item);
|
||||
if ($list) {
|
||||
$content = $list;
|
||||
} else {
|
||||
$content = id(new PHUIBoxView())
|
||||
->addClass('conpherence-search-room-results')
|
||||
->appendChild($content);
|
||||
}
|
||||
|
||||
$result = new PhabricatorApplicationSearchResultView();
|
||||
$result->setObjectList($list);
|
||||
$result->setNoDataString(pht('No threads found.'));
|
||||
$result->setContent($content);
|
||||
$result->setNoDataString(pht('No results found.'));
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
|
|
@ -5,11 +5,11 @@ final class ConpherenceThread extends ConpherenceDAO
|
|||
PhabricatorPolicyInterface,
|
||||
PhabricatorApplicationTransactionInterface,
|
||||
PhabricatorMentionableInterface,
|
||||
PhabricatorDestructibleInterface {
|
||||
PhabricatorDestructibleInterface,
|
||||
PhabricatorNgramsInterface {
|
||||
|
||||
protected $title;
|
||||
protected $topic;
|
||||
protected $imagePHIDs = array(); // TODO; nuke after migrations
|
||||
protected $profileImagePHID;
|
||||
protected $messageCount;
|
||||
protected $recentParticipantPHIDs = array();
|
||||
|
@ -41,7 +41,6 @@ final class ConpherenceThread extends ConpherenceDAO
|
|||
self::CONFIG_AUX_PHID => true,
|
||||
self::CONFIG_SERIALIZATION => array(
|
||||
'recentParticipantPHIDs' => self::SERIALIZATION_JSON,
|
||||
'imagePHIDs' => self::SERIALIZATION_JSON,
|
||||
),
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'title' => 'text255?',
|
||||
|
@ -427,6 +426,16 @@ final class ConpherenceThread extends ConpherenceDAO
|
|||
return $timeline;
|
||||
}
|
||||
|
||||
/* -( PhabricatorNgramInterface )------------------------------------------ */
|
||||
|
||||
|
||||
public function newNgrams() {
|
||||
return array(
|
||||
id(new ConpherenceThreadTitleNgrams())
|
||||
->setValue($this->getTitle()),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
final class ConpherenceThreadTitleNgrams
|
||||
extends PhabricatorSearchNgrams {
|
||||
|
||||
public function getNgramKey() {
|
||||
return 'threadtitle';
|
||||
}
|
||||
|
||||
public function getColumnName() {
|
||||
return 'title';
|
||||
}
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'conpherence';
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue