diff --git a/resources/celerity/map.php b/resources/celerity/map.php
index d5c3eafeed..43366591b1 100644
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -1,2209 +1,2209 @@
array(
- 'core.pkg.css' => '20d3b672',
+ 'core.pkg.css' => 'ad650d52',
'core.pkg.js' => '0c88e7f6',
'darkconsole.pkg.js' => '8ab24e01',
'differential.pkg.css' => '3500921f',
'differential.pkg.js' => 'c0506961',
'diffusion.pkg.css' => '591664fa',
'diffusion.pkg.js' => 'bfc0737b',
'maniphest.pkg.css' => '68d4dd3d',
'maniphest.pkg.js' => 'df4aa49f',
'rsrc/css/aphront/aphront-bars.css' => '231ac33c',
'rsrc/css/aphront/dark-console.css' => '6378ef3d',
'rsrc/css/aphront/dialog-view.css' => 'd2e76b88',
'rsrc/css/aphront/lightbox-attachment.css' => '7acac05d',
- 'rsrc/css/aphront/list-filter-view.css' => '2ae43867',
+ 'rsrc/css/aphront/list-filter-view.css' => '18b7e144',
'rsrc/css/aphront/multi-column.css' => 'fd18389d',
'rsrc/css/aphront/notification.css' => '9c279160',
'rsrc/css/aphront/pager-view.css' => '2e3539af',
'rsrc/css/aphront/panel-view.css' => '8427b78d',
'rsrc/css/aphront/phabricator-nav-view.css' => '7aeaf435',
'rsrc/css/aphront/table-view.css' => '3e77fefe',
'rsrc/css/aphront/tokenizer.css' => '82ce2142',
'rsrc/css/aphront/tooltip.css' => '7672b60f',
'rsrc/css/aphront/transaction.css' => '042fc4bb',
'rsrc/css/aphront/two-column.css' => '16ab3ad2',
'rsrc/css/aphront/typeahead.css' => '0e403212',
'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af',
'rsrc/css/application/auth/auth.css' => '1e655982',
- 'rsrc/css/application/base/main-menu-view.css' => 'cfa2ec0f',
+ 'rsrc/css/application/base/main-menu-view.css' => '361a51f9',
'rsrc/css/application/base/notification-menu.css' => '6aa0a74b',
'rsrc/css/application/base/phabricator-application-launch-view.css' => '16ca323f',
'rsrc/css/application/base/standard-page-view.css' => 'd2a6518d',
'rsrc/css/application/chatlog/chatlog.css' => '852140ff',
'rsrc/css/application/config/config-options.css' => '7fedf08b',
'rsrc/css/application/config/config-template.css' => '8e6c6fcd',
'rsrc/css/application/config/config-welcome.css' => '6abd79be',
'rsrc/css/application/config/setup-issue.css' => '22270af2',
'rsrc/css/application/config/unhandled-exception.css' => '37d4f9a2',
'rsrc/css/application/conpherence/durable-column.css' => 'caa12d4a',
- 'rsrc/css/application/conpherence/menu.css' => 'beef0920',
+ 'rsrc/css/application/conpherence/menu.css' => '7c900089',
'rsrc/css/application/conpherence/message-pane.css' => '44154798',
'rsrc/css/application/conpherence/notification.css' => '04a6e10a',
'rsrc/css/application/conpherence/update.css' => '1099a660',
'rsrc/css/application/conpherence/widget-pane.css' => '1979ee8c',
'rsrc/css/application/contentsource/content-source-view.css' => '4b8b05d4',
'rsrc/css/application/countdown/timer.css' => '86b7b0a0',
'rsrc/css/application/dashboard/dashboard.css' => '17937d22',
'rsrc/css/application/diff/inline-comment-summary.css' => 'eb5f8e8c',
'rsrc/css/application/differential/add-comment.css' => 'c47f8c40',
'rsrc/css/application/differential/changeset-view.css' => 'e19cfd6e',
'rsrc/css/application/differential/core.css' => '7ac3cabc',
'rsrc/css/application/differential/phui-inline-comment.css' => '7277be53',
'rsrc/css/application/differential/results-table.css' => '181aa9d9',
'rsrc/css/application/differential/revision-comment.css' => '024dda6b',
'rsrc/css/application/differential/revision-history.css' => '0e8eb855',
'rsrc/css/application/differential/revision-list.css' => 'f3c47d33',
'rsrc/css/application/differential/table-of-contents.css' => '63f3ef4a',
'rsrc/css/application/diffusion/diffusion-icons.css' => '9c5828da',
'rsrc/css/application/diffusion/diffusion-source.css' => '66fdf661',
'rsrc/css/application/feed/feed.css' => 'b513b5f4',
'rsrc/css/application/files/global-drag-and-drop.css' => '697324ad',
'rsrc/css/application/flag/flag.css' => '5337623f',
'rsrc/css/application/harbormaster/harbormaster.css' => '49d64eb4',
'rsrc/css/application/herald/herald-test.css' => '778b008e',
'rsrc/css/application/herald/herald.css' => '826075fa',
'rsrc/css/application/home/home.css' => 'e34bf140',
'rsrc/css/application/maniphest/batch-editor.css' => '8f380ebc',
'rsrc/css/application/maniphest/report.css' => 'f6931fdf',
'rsrc/css/application/maniphest/task-edit.css' => '8e23031b',
'rsrc/css/application/maniphest/task-summary.css' => 'ab2fc691',
'rsrc/css/application/objectselector/object-selector.css' => '029a133d',
'rsrc/css/application/owners/owners-path-editor.css' => '2f00933b',
'rsrc/css/application/paste/paste.css' => 'eb997ddd',
'rsrc/css/application/people/people-profile.css' => '25970776',
'rsrc/css/application/phame/phame.css' => '88bd4705',
'rsrc/css/application/pholio/pholio-edit.css' => '3ad9d1ee',
'rsrc/css/application/pholio/pholio-inline-comments.css' => '8e545e49',
'rsrc/css/application/pholio/pholio.css' => '95174bdd',
'rsrc/css/application/phortune/phortune-credit-card-form.css' => '8391eb02',
'rsrc/css/application/phortune/phortune.css' => '9149f103',
'rsrc/css/application/phrequent/phrequent.css' => 'ffc185ad',
'rsrc/css/application/phriction/phriction-document-css.css' => '0d16bc9a',
'rsrc/css/application/policy/policy-edit.css' => '815c66f7',
'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43',
'rsrc/css/application/policy/policy.css' => '957ea14c',
'rsrc/css/application/ponder/comments.css' => '6cdccea7',
'rsrc/css/application/ponder/feed.css' => 'e62615b6',
'rsrc/css/application/ponder/post.css' => '9d415218',
'rsrc/css/application/ponder/vote.css' => '8ed6ed8b',
'rsrc/css/application/profile/profile-view.css' => '1a20dcbf',
'rsrc/css/application/projects/project-icon.css' => 'c2ecb7f1',
'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733',
'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5',
'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '8d8b92cd',
'rsrc/css/application/releeph/releeph-request-typeahead.css' => '667a48ae',
'rsrc/css/application/search/search-results.css' => '15c71110',
'rsrc/css/application/slowvote/slowvote.css' => '266df6a1',
'rsrc/css/application/tokens/tokens.css' => '3d0f239e',
'rsrc/css/application/uiexample/example.css' => '528b19de',
'rsrc/css/core/core.css' => 'd17c2099',
'rsrc/css/core/remarkup.css' => 'bc65f3cc',
'rsrc/css/core/syntax.css' => '56c1ba38',
'rsrc/css/core/z-index.css' => '2db67397',
'rsrc/css/diviner/diviner-shared.css' => '38813222',
'rsrc/css/font/font-awesome.css' => 'ae9a7b4d',
'rsrc/css/font/font-source-sans-pro.css' => '4a2430d7',
'rsrc/css/font/phui-font-icon-base.css' => '3dad2ae3',
'rsrc/css/layout/phabricator-filetree-view.css' => 'fccf9f82',
'rsrc/css/layout/phabricator-hovercard-view.css' => '893f4783',
'rsrc/css/layout/phabricator-side-menu-view.css' => '7e8c6341',
'rsrc/css/layout/phabricator-source-code-view.css' => '2ceee894',
'rsrc/css/phui/calendar/phui-calendar-day.css' => 'de035c8a',
'rsrc/css/phui/calendar/phui-calendar-list.css' => 'c1d0ca59',
'rsrc/css/phui/calendar/phui-calendar-month.css' => 'a92e47d2',
'rsrc/css/phui/calendar/phui-calendar.css' => '8675968e',
'rsrc/css/phui/phui-action-header-view.css' => '89c497e7',
'rsrc/css/phui/phui-action-list.css' => '9ee9910a',
'rsrc/css/phui/phui-action-panel.css' => '3ee9afd5',
'rsrc/css/phui/phui-box.css' => '7b3a2eed',
'rsrc/css/phui/phui-button.css' => '53f55a72',
'rsrc/css/phui/phui-crumbs-view.css' => '594d719e',
'rsrc/css/phui/phui-document.css' => '0f83a7df',
'rsrc/css/phui/phui-feed-story.css' => 'c9f3a0b5',
'rsrc/css/phui/phui-fontkit.css' => '1fa79503',
'rsrc/css/phui/phui-form-view.css' => '78d729fe',
'rsrc/css/phui/phui-form.css' => 'f535f938',
'rsrc/css/phui/phui-header-view.css' => '083669db',
'rsrc/css/phui/phui-icon.css' => 'd35aa857',
'rsrc/css/phui/phui-image-mask.css' => '5a8b09c8',
'rsrc/css/phui/phui-info-panel.css' => '27ea50a1',
'rsrc/css/phui/phui-info-view.css' => 'c6f0aef8',
'rsrc/css/phui/phui-list.css' => '2e25ebfb',
'rsrc/css/phui/phui-object-box.css' => 'd68ce5dc',
'rsrc/css/phui/phui-object-item-list-view.css' => '9db65899',
'rsrc/css/phui/phui-pinboard-view.css' => '3dd4a269',
'rsrc/css/phui/phui-property-list-view.css' => '51480060',
'rsrc/css/phui/phui-remarkup-preview.css' => '19ad512b',
'rsrc/css/phui/phui-spacing.css' => '042804d6',
'rsrc/css/phui/phui-status.css' => '888cedb8',
'rsrc/css/phui/phui-tag-view.css' => 'ea469f3a',
'rsrc/css/phui/phui-text.css' => 'cf019f54',
'rsrc/css/phui/phui-timeline-view.css' => 'b0fbc4d7',
'rsrc/css/phui/phui-workboard-view.css' => '8896938c',
'rsrc/css/phui/phui-workpanel-view.css' => 'e495a5cc',
'rsrc/css/sprite-gradient.css' => '4bdb98a7',
'rsrc/css/sprite-login.css' => 'a355d921',
'rsrc/css/sprite-main-header.css' => '28d01b0b',
'rsrc/css/sprite-menu.css' => '9ef76324',
'rsrc/css/sprite-projects.css' => 'b0d9e24f',
'rsrc/css/sprite-tokens.css' => '1706b943',
'rsrc/externals/font/fontawesome/fontawesome-webfont.eot' => '5fb6fb0e',
'rsrc/externals/font/fontawesome/fontawesome-webfont.ttf' => 'a653cb11',
'rsrc/externals/font/fontawesome/fontawesome-webfont.woff' => '80526fc8',
'rsrc/externals/font/fontawesome/fontawesome-webfont.woff2' => '4924d54d',
'rsrc/externals/font/sourcesans/SourceSansPro-Bold.woff2' => '165f5f74',
'rsrc/externals/font/sourcesans/SourceSansPro-BoldIt.woff' => 'd09a7d54',
'rsrc/externals/font/sourcesans/SourceSansPro-BoldIt.woff2' => 'd2e33102',
'rsrc/externals/font/sourcesans/SourceSansPro-It.woff' => '3f21af52',
'rsrc/externals/font/sourcesans/SourceSansPro-It.woff2' => '30a7cf60',
'rsrc/externals/font/sourcesans/SourceSansPro-Regular.woff2' => 'e89b04b1',
'rsrc/externals/font/sourcesans/SourceSansPro.woff' => '3614608c',
'rsrc/externals/font/sourcesans/SourceSansProBold.woff' => 'cbf46566',
'rsrc/externals/javelin/core/Event.js' => '85ea0626',
'rsrc/externals/javelin/core/Stratcom.js' => '6c53634d',
'rsrc/externals/javelin/core/__tests__/event-stop-and-kill.js' => '717554e4',
'rsrc/externals/javelin/core/__tests__/install.js' => 'c432ee85',
'rsrc/externals/javelin/core/__tests__/stratcom.js' => '88bf7313',
'rsrc/externals/javelin/core/__tests__/util.js' => 'e251703d',
'rsrc/externals/javelin/core/init.js' => '3010e992',
'rsrc/externals/javelin/core/init_node.js' => 'c234aded',
'rsrc/externals/javelin/core/install.js' => '05270951',
'rsrc/externals/javelin/core/util.js' => '93cc50d6',
'rsrc/externals/javelin/docs/Base.js' => '74676256',
'rsrc/externals/javelin/docs/onload.js' => 'e819c479',
'rsrc/externals/javelin/ext/fx/Color.js' => '7e41274a',
'rsrc/externals/javelin/ext/fx/FX.js' => '54b612ba',
'rsrc/externals/javelin/ext/reactor/core/DynVal.js' => 'f6555212',
'rsrc/externals/javelin/ext/reactor/core/Reactor.js' => '2b8de964',
'rsrc/externals/javelin/ext/reactor/core/ReactorNode.js' => '1ad0a787',
'rsrc/externals/javelin/ext/reactor/core/ReactorNodeCalmer.js' => '76f4ebed',
'rsrc/externals/javelin/ext/reactor/dom/RDOM.js' => 'c90a04fc',
'rsrc/externals/javelin/ext/view/HTMLView.js' => 'fe287620',
'rsrc/externals/javelin/ext/view/View.js' => '0f764c35',
'rsrc/externals/javelin/ext/view/ViewInterpreter.js' => 'f829edb3',
'rsrc/externals/javelin/ext/view/ViewPlaceholder.js' => '47830651',
'rsrc/externals/javelin/ext/view/ViewRenderer.js' => '6c2b09a2',
'rsrc/externals/javelin/ext/view/ViewVisitor.js' => 'efe49472',
'rsrc/externals/javelin/ext/view/__tests__/HTMLView.js' => 'f92d7bcb',
'rsrc/externals/javelin/ext/view/__tests__/View.js' => '6450b38b',
'rsrc/externals/javelin/ext/view/__tests__/ViewInterpreter.js' => '7a94d6a5',
'rsrc/externals/javelin/ext/view/__tests__/ViewRenderer.js' => '6ea96ac9',
'rsrc/externals/javelin/lib/Cookie.js' => '62dfea03',
'rsrc/externals/javelin/lib/DOM.js' => '6f7962d5',
'rsrc/externals/javelin/lib/History.js' => 'd4505101',
'rsrc/externals/javelin/lib/JSON.js' => '69adf288',
'rsrc/externals/javelin/lib/Leader.js' => '331b1611',
'rsrc/externals/javelin/lib/Mask.js' => '8a41885b',
'rsrc/externals/javelin/lib/Quicksand.js' => '2bb920b6',
'rsrc/externals/javelin/lib/Request.js' => '94b750d2',
'rsrc/externals/javelin/lib/Resource.js' => '44959b73',
'rsrc/externals/javelin/lib/Routable.js' => 'b3e7d692',
'rsrc/externals/javelin/lib/Router.js' => '29274e2b',
'rsrc/externals/javelin/lib/Scrollbar.js' => '798fdb63',
'rsrc/externals/javelin/lib/Sound.js' => '949c0fe5',
'rsrc/externals/javelin/lib/URI.js' => '6eff08aa',
'rsrc/externals/javelin/lib/Vector.js' => '2caa8fb8',
'rsrc/externals/javelin/lib/WebSocket.js' => 'e292eaf4',
'rsrc/externals/javelin/lib/Workflow.js' => '5b2e3e2b',
'rsrc/externals/javelin/lib/__tests__/Cookie.js' => '5ed109e8',
'rsrc/externals/javelin/lib/__tests__/DOM.js' => 'c984504b',
'rsrc/externals/javelin/lib/__tests__/JSON.js' => '837a7d68',
'rsrc/externals/javelin/lib/__tests__/URI.js' => '1e45fda9',
'rsrc/externals/javelin/lib/__tests__/behavior.js' => '1ea62783',
'rsrc/externals/javelin/lib/behavior.js' => '61cbc29a',
'rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js' => '7644823e',
'rsrc/externals/javelin/lib/control/typeahead/Typeahead.js' => '70baed2f',
'rsrc/externals/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js' => '6f7a9da8',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js' => '503e17fd',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js' => '8b3fd187',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js' => '54f314a0',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js' => '2818f5ce',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadStaticSource.js' => '316b8fa1',
'rsrc/externals/raphael/g.raphael.js' => '40dde778',
'rsrc/externals/raphael/g.raphael.line.js' => '40da039e',
'rsrc/externals/raphael/raphael.js' => '51ee6b43',
'rsrc/favicons/apple-touch-icon-120x120.png' => '43742962',
'rsrc/favicons/apple-touch-icon-152x152.png' => '669eaec3',
'rsrc/favicons/apple-touch-icon-76x76.png' => 'ecdef672',
'rsrc/favicons/favicon-128.png' => '47cdff03',
'rsrc/favicons/favicon-16x16.png' => 'ee2523ac',
'rsrc/favicons/favicon-32x32.png' => 'b6a8150e',
'rsrc/favicons/favicon-96x96.png' => '8f7ea177',
'rsrc/image/BFCFDA.png' => 'd5ec91f4',
'rsrc/image/actions/edit.png' => '2fc41442',
'rsrc/image/avatar.png' => '3eb28cd9',
'rsrc/image/checker_dark.png' => 'd8e65881',
'rsrc/image/checker_light.png' => 'a0155918',
'rsrc/image/checker_lighter.png' => 'd5da91b6',
'rsrc/image/darkload.gif' => '1ffd3ec6',
'rsrc/image/divot.png' => '94dded62',
'rsrc/image/examples/hero.png' => '979a86ae',
'rsrc/image/grippy_texture.png' => 'aca81e2f',
'rsrc/image/icon/fatcow/arrow_branch.png' => '2537c01c',
'rsrc/image/icon/fatcow/arrow_merge.png' => '21b660e0',
'rsrc/image/icon/fatcow/bullet_black.png' => 'ff190031',
'rsrc/image/icon/fatcow/bullet_orange.png' => 'e273e5bb',
'rsrc/image/icon/fatcow/bullet_red.png' => 'c0b75434',
'rsrc/image/icon/fatcow/calendar_edit.png' => '24632275',
'rsrc/image/icon/fatcow/document_black.png' => '45fe1c60',
'rsrc/image/icon/fatcow/flag_blue.png' => 'a01abb1d',
'rsrc/image/icon/fatcow/flag_finish.png' => '67825cee',
'rsrc/image/icon/fatcow/flag_ghost.png' => '20ca8783',
'rsrc/image/icon/fatcow/flag_green.png' => '7e0eaa7a',
'rsrc/image/icon/fatcow/flag_orange.png' => '9e73df66',
'rsrc/image/icon/fatcow/flag_pink.png' => '7e92f3b2',
'rsrc/image/icon/fatcow/flag_purple.png' => 'cc517522',
'rsrc/image/icon/fatcow/flag_red.png' => '04ec726f',
'rsrc/image/icon/fatcow/flag_yellow.png' => '73946fd4',
'rsrc/image/icon/fatcow/folder.png' => '95a435af',
'rsrc/image/icon/fatcow/folder_go.png' => '001cbc94',
'rsrc/image/icon/fatcow/key_question.png' => '52a0c26a',
'rsrc/image/icon/fatcow/link.png' => '7afd4d5e',
'rsrc/image/icon/fatcow/page_white_edit.png' => '39a2eed8',
'rsrc/image/icon/fatcow/page_white_link.png' => 'a90023c7',
'rsrc/image/icon/fatcow/page_white_put.png' => '08c95a0c',
'rsrc/image/icon/fatcow/page_white_text.png' => '1e1f79c3',
'rsrc/image/icon/fatcow/source/conduit.png' => '4ea01d2f',
'rsrc/image/icon/fatcow/source/email.png' => '9bab3239',
'rsrc/image/icon/fatcow/source/fax.png' => '04195e68',
'rsrc/image/icon/fatcow/source/mobile.png' => 'f1321264',
'rsrc/image/icon/fatcow/source/tablet.png' => '49396799',
'rsrc/image/icon/fatcow/source/web.png' => '136ccb5d',
'rsrc/image/icon/fatcow/thumbnails/default.p100.png' => '7d490b01',
'rsrc/image/icon/fatcow/thumbnails/default160x120.png' => 'f2e8a2eb',
'rsrc/image/icon/fatcow/thumbnails/default280x210.png' => '43e8926a',
'rsrc/image/icon/fatcow/thumbnails/default60x45.png' => '0118abed',
'rsrc/image/icon/fatcow/thumbnails/image.p100.png' => 'da23cf97',
'rsrc/image/icon/fatcow/thumbnails/image160x120.png' => '79bb556a',
'rsrc/image/icon/fatcow/thumbnails/image280x210.png' => '91ae054a',
'rsrc/image/icon/fatcow/thumbnails/image60x45.png' => 'c5e1685e',
'rsrc/image/icon/fatcow/thumbnails/pdf.p100.png' => '87d5e065',
'rsrc/image/icon/fatcow/thumbnails/pdf160x120.png' => 'ac9edbf5',
'rsrc/image/icon/fatcow/thumbnails/pdf280x210.png' => '1c585653',
'rsrc/image/icon/fatcow/thumbnails/pdf60x45.png' => 'c0db4143',
'rsrc/image/icon/fatcow/thumbnails/zip.p100.png' => '6ea5aae4',
'rsrc/image/icon/fatcow/thumbnails/zip160x120.png' => '75f9cd0f',
'rsrc/image/icon/fatcow/thumbnails/zip280x210.png' => 'dfda5b8e',
'rsrc/image/icon/fatcow/thumbnails/zip60x45.png' => 'af11bf3e',
'rsrc/image/icon/lightbox/close-2.png' => 'cc40e7c8',
'rsrc/image/icon/lightbox/close-hover-2.png' => 'fb5d6d9e',
'rsrc/image/icon/lightbox/left-arrow-2.png' => '8426133b',
'rsrc/image/icon/lightbox/left-arrow-hover-2.png' => '701e5ee3',
'rsrc/image/icon/lightbox/right-arrow-2.png' => '6d5519a0',
'rsrc/image/icon/lightbox/right-arrow-hover-2.png' => '3a04aa21',
'rsrc/image/icon/subscribe.png' => 'd03ed5a5',
'rsrc/image/icon/tango/attachment.png' => 'ecc8022e',
'rsrc/image/icon/tango/edit.png' => '929a1363',
'rsrc/image/icon/tango/go-down.png' => '96d95e43',
'rsrc/image/icon/tango/log.png' => 'b08cc63a',
'rsrc/image/icon/tango/upload.png' => '7bbb7984',
'rsrc/image/icon/unsubscribe.png' => '25725013',
'rsrc/image/lightblue-header.png' => '5c168b6d',
'rsrc/image/loading.gif' => '75d384cc',
'rsrc/image/loading/boating_24.gif' => '5c90f086',
'rsrc/image/loading/compass_24.gif' => 'b36b4f46',
'rsrc/image/loading/loading_24.gif' => '26bc9adc',
'rsrc/image/loading/loading_48.gif' => '6a4994c7',
'rsrc/image/loading/loading_d48.gif' => 'cdcbe900',
'rsrc/image/loading/loading_w24.gif' => '7662fa2b',
'rsrc/image/main_texture.png' => '29a2c5ad',
'rsrc/image/menu_texture.png' => '5a17580d',
'rsrc/image/people/harding.png' => '45aa614e',
'rsrc/image/people/jefferson.png' => 'afca0e53',
'rsrc/image/people/lincoln.png' => '9369126d',
'rsrc/image/people/mckinley.png' => 'fb8f16ce',
'rsrc/image/people/taft.png' => 'd7bc402c',
'rsrc/image/people/washington.png' => '40dd301c',
'rsrc/image/phrequent_active.png' => 'a466a8ed',
'rsrc/image/phrequent_inactive.png' => 'bfc15a69',
'rsrc/image/search-white.png' => '64cc0d45',
'rsrc/image/search.png' => '82625a7e',
'rsrc/image/sprite-gradient.png' => 'ec15a417',
'rsrc/image/sprite-login-X2.png' => '5ae6de3a',
'rsrc/image/sprite-login.png' => '07f2c67c',
'rsrc/image/sprite-main-header.png' => '39419fa6',
'rsrc/image/sprite-menu-X2.png' => '1c90d7bc',
'rsrc/image/sprite-menu.png' => '619781ee',
'rsrc/image/sprite-projects-X2.png' => '8c91c839',
'rsrc/image/sprite-projects.png' => 'ef9dc9b5',
'rsrc/image/sprite-tokens-X2.png' => 'b4776580',
'rsrc/image/sprite-tokens.png' => '25b75533',
'rsrc/image/texture/card-gradient.png' => '815f26e8',
'rsrc/image/texture/dark-menu-hover.png' => '5fa7ece8',
'rsrc/image/texture/dark-menu.png' => '7e22296e',
'rsrc/image/texture/grip.png' => '719404f3',
'rsrc/image/texture/panel-header-gradient.png' => 'e3b8dcfe',
'rsrc/image/texture/phlnx-bg.png' => '8d819209',
'rsrc/image/texture/pholio-background.gif' => 'ba29239c',
'rsrc/image/texture/table_header.png' => '5c433037',
'rsrc/image/texture/table_header_hover.png' => '038ec3b9',
'rsrc/image/texture/table_header_tall.png' => 'd56b434f',
'rsrc/js/application/aphlict/Aphlict.js' => '2be71d56',
'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => '00def500',
'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => 'bdf2226d',
'rsrc/js/application/aphlict/behavior-aphlict-status.js' => 'ea681761',
'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18',
'rsrc/js/application/config/behavior-reorder-fields.js' => '14a827de',
'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => 'bb928342',
'rsrc/js/application/conpherence/behavior-durable-column.js' => 'a0216452',
- 'rsrc/js/application/conpherence/behavior-menu.js' => 'de5579b4',
+ 'rsrc/js/application/conpherence/behavior-menu.js' => 'cda6de80',
'rsrc/js/application/conpherence/behavior-pontificate.js' => '21ba5861',
'rsrc/js/application/conpherence/behavior-quicksand-blacklist.js' => '7927a7d3',
'rsrc/js/application/conpherence/behavior-widget-pane.js' => '1ec93bcf',
'rsrc/js/application/countdown/timer.js' => 'e4cc26b3',
'rsrc/js/application/dashboard/behavior-dashboard-async-panel.js' => '469c0d9e',
'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '82439934',
'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375',
'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63',
'rsrc/js/application/differential/ChangesetViewManager.js' => '58562350',
'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => '2529c82d',
'rsrc/js/application/differential/behavior-add-reviewers-and-ccs.js' => 'e10f8e18',
'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d',
'rsrc/js/application/differential/behavior-comment-preview.js' => '8e1389b5',
'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1',
'rsrc/js/application/differential/behavior-dropdown-menus.js' => '2035b9cb',
'rsrc/js/application/differential/behavior-edit-inline-comments.js' => 'e723c323',
'rsrc/js/application/differential/behavior-keyboard-nav.js' => '2c426492',
'rsrc/js/application/differential/behavior-populate.js' => '8694b1df',
'rsrc/js/application/differential/behavior-show-field-details.js' => 'bba9eedf',
'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb',
'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d',
'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => 'b42eddc7',
'rsrc/js/application/diffusion/behavior-audit-preview.js' => 'd835b03a',
'rsrc/js/application/diffusion/behavior-commit-branches.js' => 'bdaf4d04',
'rsrc/js/application/diffusion/behavior-commit-graph.js' => 'f7f1289f',
'rsrc/js/application/diffusion/behavior-jump-to.js' => '73d09eef',
'rsrc/js/application/diffusion/behavior-load-blame.js' => '42126667',
'rsrc/js/application/diffusion/behavior-locate-file.js' => '6d3e1947',
'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => '2b228192',
'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => 'e5822781',
'rsrc/js/application/files/behavior-icon-composer.js' => '8ef9ab58',
'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888',
'rsrc/js/application/herald/HeraldRuleEditor.js' => '6e2de6f2',
'rsrc/js/application/herald/PathTypeahead.js' => 'f7fc67ec',
'rsrc/js/application/herald/herald-rule-editor.js' => '7ebaeed3',
'rsrc/js/application/maniphest/behavior-batch-editor.js' => 'f24f3253',
'rsrc/js/application/maniphest/behavior-batch-selector.js' => '7b98d7c5',
'rsrc/js/application/maniphest/behavior-line-chart.js' => '88f0c5b3',
'rsrc/js/application/maniphest/behavior-list-edit.js' => 'a9f88de2',
'rsrc/js/application/maniphest/behavior-subpriorityeditor.js' => '84845b5b',
'rsrc/js/application/maniphest/behavior-transaction-controls.js' => '44168bad',
'rsrc/js/application/maniphest/behavior-transaction-expand.js' => '5fefb143',
'rsrc/js/application/maniphest/behavior-transaction-preview.js' => 'f8248bc5',
'rsrc/js/application/owners/OwnersPathEditor.js' => 'aa1733d0',
'rsrc/js/application/owners/owners-path-editor.js' => '7a68dda3',
'rsrc/js/application/passphrase/passphrase-credential-control.js' => '3cb0b2fc',
'rsrc/js/application/phame/phame-post-preview.js' => 'be807912',
'rsrc/js/application/pholio/behavior-pholio-mock-edit.js' => '9c2623f4',
'rsrc/js/application/pholio/behavior-pholio-mock-view.js' => 'e58bf807',
'rsrc/js/application/phortune/behavior-stripe-payment-form.js' => '3f5d6dbf',
'rsrc/js/application/phortune/behavior-test-payment-form.js' => 'fc91ab6c',
'rsrc/js/application/phortune/phortune-credit-card-form.js' => '2290aeef',
'rsrc/js/application/policy/behavior-policy-control.js' => 'f3fef818',
'rsrc/js/application/policy/behavior-policy-rule-editor.js' => 'fe9a552f',
'rsrc/js/application/ponder/behavior-votebox.js' => '4e9b766b',
'rsrc/js/application/projects/behavior-boards-dropdown.js' => '0ec56e1d',
'rsrc/js/application/projects/behavior-project-boards.js' => '87cb6b51',
'rsrc/js/application/projects/behavior-project-create.js' => '065227cc',
'rsrc/js/application/projects/behavior-reorder-columns.js' => 'e1d25dfb',
'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf',
'rsrc/js/application/releeph/releeph-request-state-change.js' => 'a0b57eb8',
'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'de2e896f',
'rsrc/js/application/repository/repository-crossreference.js' => 'f9539603',
'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08',
'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f',
'rsrc/js/application/transactions/behavior-show-older-transactions.js' => 'dbbf48b6',
'rsrc/js/application/transactions/behavior-transaction-comment-form.js' => '9f7309fb',
'rsrc/js/application/transactions/behavior-transaction-list.js' => '13c739ea',
'rsrc/js/application/uiexample/JavelinViewExample.js' => 'd4a14807',
'rsrc/js/application/uiexample/ReactorButtonExample.js' => 'd19198c8',
'rsrc/js/application/uiexample/ReactorCheckboxExample.js' => '519705ea',
'rsrc/js/application/uiexample/ReactorFocusExample.js' => '40a6a403',
'rsrc/js/application/uiexample/ReactorInputExample.js' => '886fd850',
'rsrc/js/application/uiexample/ReactorMouseoverExample.js' => '47c794d8',
'rsrc/js/application/uiexample/ReactorRadioExample.js' => '988040b4',
'rsrc/js/application/uiexample/ReactorSelectExample.js' => 'a155550f',
'rsrc/js/application/uiexample/ReactorSendClassExample.js' => '1def2711',
'rsrc/js/application/uiexample/ReactorSendPropertiesExample.js' => 'b1f0ccee',
'rsrc/js/application/uiexample/busy-example.js' => '60479091',
'rsrc/js/application/uiexample/gesture-example.js' => '558829c2',
'rsrc/js/application/uiexample/notification-example.js' => '8ce821c5',
'rsrc/js/core/Busy.js' => 'f13d6d1d',
'rsrc/js/core/DragAndDropFileUpload.js' => '7fa4b248',
'rsrc/js/core/DraggableList.js' => 'a16ec1c6',
'rsrc/js/core/FileUpload.js' => '477359c8',
'rsrc/js/core/Hovercard.js' => '7e8468ae',
'rsrc/js/core/KeyboardShortcut.js' => '1ae869f2',
'rsrc/js/core/KeyboardShortcutManager.js' => 'c1700f6f',
'rsrc/js/core/MultirowRowManager.js' => 'b5d57730',
'rsrc/js/core/Notification.js' => '0c6946e7',
'rsrc/js/core/Prefab.js' => '72da38cc',
'rsrc/js/core/ShapedRequest.js' => '7cbe244b',
'rsrc/js/core/TextAreaUtils.js' => '5c93c52c',
'rsrc/js/core/Title.js' => '5c1c758c',
'rsrc/js/core/ToolTip.js' => '1d298e3a',
'rsrc/js/core/behavior-active-nav.js' => 'e379b58e',
'rsrc/js/core/behavior-audio-source.js' => '59b251eb',
'rsrc/js/core/behavior-autofocus.js' => '7319e029',
'rsrc/js/core/behavior-choose-control.js' => '6153c708',
'rsrc/js/core/behavior-crop.js' => 'fa0f4fc2',
'rsrc/js/core/behavior-dark-console.js' => '08883e8b',
'rsrc/js/core/behavior-device.js' => 'a205cf28',
'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '6d49590e',
'rsrc/js/core/behavior-error-log.js' => '6882e80a',
'rsrc/js/core/behavior-fancy-datepicker.js' => 'c51ae228',
'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' => 'bbdf75ca',
'rsrc/js/core/behavior-high-security-warning.js' => '8fc1c918',
'rsrc/js/core/behavior-history-install.js' => '7ee2b591',
'rsrc/js/core/behavior-hovercard.js' => 'f36e01af',
'rsrc/js/core/behavior-keyboard-pager.js' => 'a8da01f0',
'rsrc/js/core/behavior-keyboard-shortcuts.js' => 'd75709e6',
'rsrc/js/core/behavior-lightbox-attachments.js' => 'f8ba29d7',
'rsrc/js/core/behavior-line-linker.js' => '1499a8cb',
'rsrc/js/core/behavior-more.js' => 'a80d0378',
'rsrc/js/core/behavior-object-selector.js' => '49b73b36',
'rsrc/js/core/behavior-oncopy.js' => '2926fff2',
'rsrc/js/core/behavior-phabricator-nav.js' => '14d7a8b8',
'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => 'e32d14ab',
'rsrc/js/core/behavior-refresh-csrf.js' => '7814b593',
'rsrc/js/core/behavior-remarkup-preview.js' => 'f7379f45',
'rsrc/js/core/behavior-reorder-applications.js' => '76b9fc3e',
'rsrc/js/core/behavior-reveal-content.js' => '60821bc7',
'rsrc/js/core/behavior-scrollbar.js' => '834a1173',
'rsrc/js/core/behavior-search-typeahead.js' => '724b1247',
'rsrc/js/core/behavior-select-on-click.js' => '4e3e79a6',
'rsrc/js/core/behavior-toggle-class.js' => 'e566f52c',
'rsrc/js/core/behavior-tokenizer.js' => 'b3a4b884',
'rsrc/js/core/behavior-tooltip.js' => '3ee3408b',
'rsrc/js/core/behavior-watch-anchor.js' => '9f36c42d',
'rsrc/js/core/behavior-workflow.js' => '0a3f3021',
'rsrc/js/core/phtize.js' => 'd254d646',
'rsrc/js/phui/behavior-phui-object-box-tabs.js' => '2bfa2836',
'rsrc/js/phui/behavior-phui-timeline-dropdown-menu.js' => '4d94d9c3',
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
'rsrc/js/phuix/PHUIXActionView.js' => '6e8cefa4',
'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bd4c8dca',
),
'symbols' => array(
'almanac-css' => 'dbb9b3af',
'aphront-bars' => '231ac33c',
'aphront-dark-console-css' => '6378ef3d',
'aphront-dialog-view-css' => 'd2e76b88',
- 'aphront-list-filter-view-css' => '2ae43867',
+ 'aphront-list-filter-view-css' => '18b7e144',
'aphront-multi-column-view-css' => 'fd18389d',
'aphront-pager-view-css' => '2e3539af',
'aphront-panel-view-css' => '8427b78d',
'aphront-table-view-css' => '3e77fefe',
'aphront-tokenizer-control-css' => '82ce2142',
'aphront-tooltip-css' => '7672b60f',
'aphront-two-column-view-css' => '16ab3ad2',
'aphront-typeahead-control-css' => '0e403212',
'auth-css' => '1e655982',
'changeset-view-manager' => '58562350',
'config-options-css' => '7fedf08b',
'config-welcome-css' => '6abd79be',
'conpherence-durable-column-view' => 'caa12d4a',
- 'conpherence-menu-css' => 'beef0920',
+ 'conpherence-menu-css' => '7c900089',
'conpherence-message-pane-css' => '44154798',
'conpherence-notification-css' => '04a6e10a',
'conpherence-thread-manager' => 'bb928342',
'conpherence-update-css' => '1099a660',
'conpherence-widget-pane-css' => '1979ee8c',
'differential-changeset-view-css' => 'e19cfd6e',
'differential-core-view-css' => '7ac3cabc',
'differential-inline-comment-editor' => '2529c82d',
'differential-results-table-css' => '181aa9d9',
'differential-revision-add-comment-css' => 'c47f8c40',
'differential-revision-comment-css' => '024dda6b',
'differential-revision-history-css' => '0e8eb855',
'differential-revision-list-css' => 'f3c47d33',
'differential-table-of-contents-css' => '63f3ef4a',
'diffusion-icons-css' => '9c5828da',
'diffusion-source-css' => '66fdf661',
'diviner-shared-css' => '38813222',
'font-fontawesome' => 'ae9a7b4d',
'font-source-sans-pro' => '4a2430d7',
'global-drag-and-drop-css' => '697324ad',
'harbormaster-css' => '49d64eb4',
'herald-css' => '826075fa',
'herald-rule-editor' => '6e2de6f2',
'herald-test-css' => '778b008e',
'homepage-panel-css' => 'e34bf140',
'inline-comment-summary-css' => 'eb5f8e8c',
'javelin-aphlict' => '2be71d56',
'javelin-behavior' => '61cbc29a',
'javelin-behavior-aphlict-dropdown' => '00def500',
'javelin-behavior-aphlict-listen' => 'bdf2226d',
'javelin-behavior-aphlict-status' => 'ea681761',
'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884',
'javelin-behavior-aphront-crop' => 'fa0f4fc2',
'javelin-behavior-aphront-drag-and-drop-textarea' => '6d49590e',
'javelin-behavior-aphront-form-disable-on-submit' => '5c54cbf3',
'javelin-behavior-aphront-more' => 'a80d0378',
'javelin-behavior-audio-source' => '59b251eb',
'javelin-behavior-audit-preview' => 'd835b03a',
'javelin-behavior-boards-dropdown' => '0ec56e1d',
'javelin-behavior-choose-control' => '6153c708',
'javelin-behavior-config-reorder-fields' => '14a827de',
- 'javelin-behavior-conpherence-menu' => 'de5579b4',
+ 'javelin-behavior-conpherence-menu' => 'cda6de80',
'javelin-behavior-conpherence-pontificate' => '21ba5861',
'javelin-behavior-conpherence-widget-pane' => '1ec93bcf',
'javelin-behavior-countdown-timer' => 'e4cc26b3',
'javelin-behavior-dark-console' => '08883e8b',
'javelin-behavior-dashboard-async-panel' => '469c0d9e',
'javelin-behavior-dashboard-move-panels' => '82439934',
'javelin-behavior-dashboard-query-panel-select' => '453c5375',
'javelin-behavior-dashboard-tab-panel' => 'd4eecc63',
'javelin-behavior-device' => 'a205cf28',
'javelin-behavior-differential-add-reviewers-and-ccs' => 'e10f8e18',
'javelin-behavior-differential-comment-jump' => '4fdb476d',
'javelin-behavior-differential-diff-radios' => 'e1ff79b1',
'javelin-behavior-differential-dropdown-menus' => '2035b9cb',
'javelin-behavior-differential-edit-inline-comments' => 'e723c323',
'javelin-behavior-differential-feedback-preview' => '8e1389b5',
'javelin-behavior-differential-keyboard-navigation' => '2c426492',
'javelin-behavior-differential-populate' => '8694b1df',
'javelin-behavior-differential-show-field-details' => 'bba9eedf',
'javelin-behavior-differential-toggle-files' => 'ca3f91eb',
'javelin-behavior-differential-user-select' => 'a8d8459d',
'javelin-behavior-diffusion-commit-branches' => 'bdaf4d04',
'javelin-behavior-diffusion-commit-graph' => 'f7f1289f',
'javelin-behavior-diffusion-jump-to' => '73d09eef',
'javelin-behavior-diffusion-locate-file' => '6d3e1947',
'javelin-behavior-diffusion-pull-lastmodified' => '2b228192',
'javelin-behavior-doorkeeper-tag' => 'e5822781',
'javelin-behavior-durable-column' => 'a0216452',
'javelin-behavior-error-log' => '6882e80a',
'javelin-behavior-fancy-datepicker' => 'c51ae228',
'javelin-behavior-global-drag-and-drop' => 'bbdf75ca',
'javelin-behavior-herald-rule-editor' => '7ebaeed3',
'javelin-behavior-high-security-warning' => '8fc1c918',
'javelin-behavior-history-install' => '7ee2b591',
'javelin-behavior-icon-composer' => '8ef9ab58',
'javelin-behavior-launch-icon-composer' => '48086888',
'javelin-behavior-lightbox-attachments' => 'f8ba29d7',
'javelin-behavior-line-chart' => '88f0c5b3',
'javelin-behavior-load-blame' => '42126667',
'javelin-behavior-maniphest-batch-editor' => 'f24f3253',
'javelin-behavior-maniphest-batch-selector' => '7b98d7c5',
'javelin-behavior-maniphest-list-editor' => 'a9f88de2',
'javelin-behavior-maniphest-subpriority-editor' => '84845b5b',
'javelin-behavior-maniphest-transaction-controls' => '44168bad',
'javelin-behavior-maniphest-transaction-expand' => '5fefb143',
'javelin-behavior-maniphest-transaction-preview' => 'f8248bc5',
'javelin-behavior-owners-path-editor' => '7a68dda3',
'javelin-behavior-passphrase-credential-control' => '3cb0b2fc',
'javelin-behavior-persona-login' => '9414ff18',
'javelin-behavior-phabricator-active-nav' => 'e379b58e',
'javelin-behavior-phabricator-autofocus' => '7319e029',
'javelin-behavior-phabricator-busy-example' => '60479091',
'javelin-behavior-phabricator-file-tree' => '88236f00',
'javelin-behavior-phabricator-gesture' => '3ab51e2c',
'javelin-behavior-phabricator-gesture-example' => '558829c2',
'javelin-behavior-phabricator-hovercards' => 'f36e01af',
'javelin-behavior-phabricator-keyboard-pager' => 'a8da01f0',
'javelin-behavior-phabricator-keyboard-shortcuts' => 'd75709e6',
'javelin-behavior-phabricator-line-linker' => '1499a8cb',
'javelin-behavior-phabricator-nav' => '14d7a8b8',
'javelin-behavior-phabricator-notification-example' => '8ce821c5',
'javelin-behavior-phabricator-object-selector' => '49b73b36',
'javelin-behavior-phabricator-oncopy' => '2926fff2',
'javelin-behavior-phabricator-remarkup-assist' => 'e32d14ab',
'javelin-behavior-phabricator-reveal-content' => '60821bc7',
'javelin-behavior-phabricator-search-typeahead' => '724b1247',
'javelin-behavior-phabricator-show-older-transactions' => 'dbbf48b6',
'javelin-behavior-phabricator-tooltips' => '3ee3408b',
'javelin-behavior-phabricator-transaction-comment-form' => '9f7309fb',
'javelin-behavior-phabricator-transaction-list' => '13c739ea',
'javelin-behavior-phabricator-watch-anchor' => '9f36c42d',
'javelin-behavior-phame-post-preview' => 'be807912',
'javelin-behavior-pholio-mock-edit' => '9c2623f4',
'javelin-behavior-pholio-mock-view' => 'e58bf807',
'javelin-behavior-phui-object-box-tabs' => '2bfa2836',
'javelin-behavior-phui-timeline-dropdown-menu' => '4d94d9c3',
'javelin-behavior-policy-control' => 'f3fef818',
'javelin-behavior-policy-rule-editor' => 'fe9a552f',
'javelin-behavior-ponder-votebox' => '4e9b766b',
'javelin-behavior-project-boards' => '87cb6b51',
'javelin-behavior-project-create' => '065227cc',
'javelin-behavior-quicksand-blacklist' => '7927a7d3',
'javelin-behavior-refresh-csrf' => '7814b593',
'javelin-behavior-releeph-preview-branch' => 'b2b4fbaf',
'javelin-behavior-releeph-request-state-change' => 'a0b57eb8',
'javelin-behavior-releeph-request-typeahead' => 'de2e896f',
'javelin-behavior-remarkup-preview' => 'f7379f45',
'javelin-behavior-reorder-applications' => '76b9fc3e',
'javelin-behavior-reorder-columns' => 'e1d25dfb',
'javelin-behavior-repository-crossreference' => 'f9539603',
'javelin-behavior-scrollbar' => '834a1173',
'javelin-behavior-search-reorder-queries' => 'e9581f08',
'javelin-behavior-select-on-click' => '4e3e79a6',
'javelin-behavior-slowvote-embed' => '887ad43f',
'javelin-behavior-stripe-payment-form' => '3f5d6dbf',
'javelin-behavior-test-payment-form' => 'fc91ab6c',
'javelin-behavior-toggle-class' => 'e566f52c',
'javelin-behavior-view-placeholder' => '47830651',
'javelin-behavior-workflow' => '0a3f3021',
'javelin-color' => '7e41274a',
'javelin-cookie' => '62dfea03',
'javelin-diffusion-locate-file-source' => 'b42eddc7',
'javelin-dom' => '6f7962d5',
'javelin-dynval' => 'f6555212',
'javelin-event' => '85ea0626',
'javelin-fx' => '54b612ba',
'javelin-history' => 'd4505101',
'javelin-install' => '05270951',
'javelin-json' => '69adf288',
'javelin-leader' => '331b1611',
'javelin-magical-init' => '3010e992',
'javelin-mask' => '8a41885b',
'javelin-quicksand' => '2bb920b6',
'javelin-reactor' => '2b8de964',
'javelin-reactor-dom' => 'c90a04fc',
'javelin-reactor-node-calmer' => '76f4ebed',
'javelin-reactornode' => '1ad0a787',
'javelin-request' => '94b750d2',
'javelin-resource' => '44959b73',
'javelin-routable' => 'b3e7d692',
'javelin-router' => '29274e2b',
'javelin-scrollbar' => '798fdb63',
'javelin-sound' => '949c0fe5',
'javelin-stratcom' => '6c53634d',
'javelin-tokenizer' => '7644823e',
'javelin-typeahead' => '70baed2f',
'javelin-typeahead-composite-source' => '503e17fd',
'javelin-typeahead-normalizer' => '6f7a9da8',
'javelin-typeahead-ondemand-source' => '8b3fd187',
'javelin-typeahead-preloaded-source' => '54f314a0',
'javelin-typeahead-source' => '2818f5ce',
'javelin-typeahead-static-source' => '316b8fa1',
'javelin-uri' => '6eff08aa',
'javelin-util' => '93cc50d6',
'javelin-vector' => '2caa8fb8',
'javelin-view' => '0f764c35',
'javelin-view-html' => 'fe287620',
'javelin-view-interpreter' => 'f829edb3',
'javelin-view-renderer' => '6c2b09a2',
'javelin-view-visitor' => 'efe49472',
'javelin-websocket' => 'e292eaf4',
'javelin-workflow' => '5b2e3e2b',
'lightbox-attachment-css' => '7acac05d',
'maniphest-batch-editor' => '8f380ebc',
'maniphest-report-css' => 'f6931fdf',
'maniphest-task-edit-css' => '8e23031b',
'maniphest-task-summary-css' => 'ab2fc691',
'multirow-row-manager' => 'b5d57730',
'owners-path-editor' => 'aa1733d0',
'owners-path-editor-css' => '2f00933b',
'paste-css' => 'eb997ddd',
'path-typeahead' => 'f7fc67ec',
'people-profile-css' => '25970776',
'phabricator-action-list-view-css' => '9ee9910a',
'phabricator-application-launch-view-css' => '16ca323f',
'phabricator-busy' => 'f13d6d1d',
'phabricator-chatlog-css' => '852140ff',
'phabricator-content-source-view-css' => '4b8b05d4',
'phabricator-core-css' => 'd17c2099',
'phabricator-countdown-css' => '86b7b0a0',
'phabricator-dashboard-css' => '17937d22',
'phabricator-drag-and-drop-file-upload' => '7fa4b248',
'phabricator-draggable-list' => 'a16ec1c6',
'phabricator-fatal-config-template-css' => '8e6c6fcd',
'phabricator-feed-css' => 'b513b5f4',
'phabricator-file-upload' => '477359c8',
'phabricator-filetree-view-css' => 'fccf9f82',
'phabricator-flag-css' => '5337623f',
'phabricator-hovercard' => '7e8468ae',
'phabricator-hovercard-view-css' => '893f4783',
'phabricator-keyboard-shortcut' => '1ae869f2',
'phabricator-keyboard-shortcut-manager' => 'c1700f6f',
- 'phabricator-main-menu-view' => 'cfa2ec0f',
+ 'phabricator-main-menu-view' => '361a51f9',
'phabricator-nav-view-css' => '7aeaf435',
'phabricator-notification' => '0c6946e7',
'phabricator-notification-css' => '9c279160',
'phabricator-notification-menu-css' => '6aa0a74b',
'phabricator-object-selector-css' => '029a133d',
'phabricator-phtize' => 'd254d646',
'phabricator-prefab' => '72da38cc',
'phabricator-profile-css' => '1a20dcbf',
'phabricator-remarkup-css' => 'bc65f3cc',
'phabricator-search-results-css' => '15c71110',
'phabricator-shaped-request' => '7cbe244b',
'phabricator-side-menu-view-css' => '7e8c6341',
'phabricator-slowvote-css' => '266df6a1',
'phabricator-source-code-view-css' => '2ceee894',
'phabricator-standard-page-view' => 'd2a6518d',
'phabricator-textareautils' => '5c93c52c',
'phabricator-title' => '5c1c758c',
'phabricator-tooltip' => '1d298e3a',
'phabricator-transaction-view-css' => '042fc4bb',
'phabricator-ui-example-css' => '528b19de',
'phabricator-uiexample-javelin-view' => 'd4a14807',
'phabricator-uiexample-reactor-button' => 'd19198c8',
'phabricator-uiexample-reactor-checkbox' => '519705ea',
'phabricator-uiexample-reactor-focus' => '40a6a403',
'phabricator-uiexample-reactor-input' => '886fd850',
'phabricator-uiexample-reactor-mouseover' => '47c794d8',
'phabricator-uiexample-reactor-radio' => '988040b4',
'phabricator-uiexample-reactor-select' => 'a155550f',
'phabricator-uiexample-reactor-sendclass' => '1def2711',
'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
'phabricator-zindex-css' => '2db67397',
'phame-css' => '88bd4705',
'pholio-css' => '95174bdd',
'pholio-edit-css' => '3ad9d1ee',
'pholio-inline-comments-css' => '8e545e49',
'phortune-credit-card-form' => '2290aeef',
'phortune-credit-card-form-css' => '8391eb02',
'phortune-css' => '9149f103',
'phrequent-css' => 'ffc185ad',
'phriction-document-css' => '0d16bc9a',
'phui-action-header-view-css' => '89c497e7',
'phui-action-panel-css' => '3ee9afd5',
'phui-box-css' => '7b3a2eed',
'phui-button-css' => '53f55a72',
'phui-calendar-css' => '8675968e',
'phui-calendar-day-css' => 'de035c8a',
'phui-calendar-list-css' => 'c1d0ca59',
'phui-calendar-month-css' => 'a92e47d2',
'phui-crumbs-view-css' => '594d719e',
'phui-document-view-css' => '0f83a7df',
'phui-feed-story-css' => 'c9f3a0b5',
'phui-font-icon-base-css' => '3dad2ae3',
'phui-fontkit-css' => '1fa79503',
'phui-form-css' => 'f535f938',
'phui-form-view-css' => '78d729fe',
'phui-header-view-css' => '083669db',
'phui-icon-view-css' => 'd35aa857',
'phui-image-mask-css' => '5a8b09c8',
'phui-info-panel-css' => '27ea50a1',
'phui-info-view-css' => 'c6f0aef8',
'phui-inline-comment-view-css' => '7277be53',
'phui-list-view-css' => '2e25ebfb',
'phui-object-box-css' => 'd68ce5dc',
'phui-object-item-list-view-css' => '9db65899',
'phui-pinboard-view-css' => '3dd4a269',
'phui-property-list-view-css' => '51480060',
'phui-remarkup-preview-css' => '19ad512b',
'phui-spacing-css' => '042804d6',
'phui-status-list-view-css' => '888cedb8',
'phui-tag-view-css' => 'ea469f3a',
'phui-text-css' => 'cf019f54',
'phui-timeline-view-css' => 'b0fbc4d7',
'phui-workboard-view-css' => '8896938c',
'phui-workpanel-view-css' => 'e495a5cc',
'phuix-action-list-view' => 'b5c256b8',
'phuix-action-view' => '6e8cefa4',
'phuix-dropdown-menu' => 'bd4c8dca',
'policy-css' => '957ea14c',
'policy-edit-css' => '815c66f7',
'policy-transaction-detail-css' => '82100a43',
'ponder-comment-table-css' => '6cdccea7',
'ponder-feed-view-css' => 'e62615b6',
'ponder-post-css' => '9d415218',
'ponder-vote-css' => '8ed6ed8b',
'project-icon-css' => 'c2ecb7f1',
'raphael-core' => '51ee6b43',
'raphael-g' => '40dde778',
'raphael-g-line' => '40da039e',
'releeph-core' => '9b3c5733',
'releeph-preview-branch' => 'b7a6f4a5',
'releeph-request-differential-create-dialog' => '8d8b92cd',
'releeph-request-typeahead-css' => '667a48ae',
'setup-issue-css' => '22270af2',
'sprite-gradient-css' => '4bdb98a7',
'sprite-login-css' => 'a355d921',
'sprite-main-header-css' => '28d01b0b',
'sprite-menu-css' => '9ef76324',
'sprite-projects-css' => 'b0d9e24f',
'sprite-tokens-css' => '1706b943',
'syntax-highlighting-css' => '56c1ba38',
'tokens-css' => '3d0f239e',
'unhandled-exception-css' => '37d4f9a2',
),
'requires' => array(
'00def500' => array(
'javelin-behavior',
'javelin-request',
'javelin-stratcom',
'javelin-vector',
'javelin-dom',
'javelin-uri',
'javelin-behavior-device',
'phabricator-title',
),
'029a133d' => array(
'aphront-dialog-view-css',
),
'05270951' => array(
'javelin-util',
'javelin-magical-init',
),
'065227cc' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-workflow',
),
'08883e8b' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-util',
'javelin-dom',
'javelin-request',
'phabricator-keyboard-shortcut',
),
'0a3f3021' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'javelin-router',
),
'0c6946e7' => array(
'javelin-install',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
'phabricator-notification-css',
),
'0ec56e1d' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'phuix-dropdown-menu',
),
'0f764c35' => array(
'javelin-install',
'javelin-util',
),
'13c739ea' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'javelin-uri',
'phabricator-textareautils',
),
'1499a8cb' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-history',
),
'14a827de' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-json',
'phabricator-draggable-list',
),
'14d7a8b8' => array(
'javelin-behavior',
'javelin-behavior-device',
'javelin-stratcom',
'javelin-dom',
'javelin-magical-init',
'javelin-vector',
'javelin-request',
'javelin-util',
),
'1ad0a787' => array(
'javelin-install',
'javelin-reactor',
'javelin-util',
'javelin-reactor-node-calmer',
),
'1ae869f2' => array(
'javelin-install',
'javelin-util',
'phabricator-keyboard-shortcut-manager',
),
'1d298e3a' => array(
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-vector',
),
'1def2711' => array(
'javelin-install',
'javelin-dom',
'javelin-reactor-dom',
),
'1ec93bcf' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-workflow',
'javelin-util',
'phabricator-notification',
'javelin-behavior-device',
'phuix-dropdown-menu',
'phuix-action-list-view',
'phuix-action-view',
'conpherence-thread-manager',
),
'2035b9cb' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-workflow',
'phuix-dropdown-menu',
'phuix-action-list-view',
'phuix-action-view',
'phabricator-phtize',
'changeset-view-manager',
),
'21ba5861' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-workflow',
'javelin-stratcom',
'conpherence-thread-manager',
),
'2290aeef' => array(
'javelin-install',
'javelin-dom',
'javelin-json',
'javelin-workflow',
'javelin-util',
),
'2529c82d' => array(
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-install',
'javelin-request',
'javelin-workflow',
),
'2818f5ce' => array(
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-typeahead-normalizer',
),
'2926fff2' => array(
'javelin-behavior',
'javelin-dom',
),
'29274e2b' => array(
'javelin-install',
'javelin-util',
),
'2b228192' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-workflow',
'javelin-json',
),
'2b8de964' => array(
'javelin-install',
'javelin-util',
),
'2bb920b6' => array(
'javelin-install',
),
'2be71d56' => array(
'javelin-install',
'javelin-util',
'javelin-websocket',
'javelin-leader',
'javelin-json',
),
'2bfa2836' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'2c426492' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'phabricator-keyboard-shortcut',
),
'2caa8fb8' => array(
'javelin-install',
'javelin-event',
),
'316b8fa1' => array(
'javelin-install',
'javelin-typeahead-source',
),
'331b1611' => array(
'javelin-install',
),
'3ab51e2c' => array(
'javelin-behavior',
'javelin-behavior-device',
'javelin-stratcom',
'javelin-vector',
'javelin-dom',
'javelin-magical-init',
),
'3cb0b2fc' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-workflow',
'javelin-util',
'javelin-uri',
),
'3ee3408b' => array(
'javelin-behavior',
'javelin-behavior-device',
'javelin-stratcom',
'phabricator-tooltip',
),
'3f5d6dbf' => array(
'javelin-behavior',
'javelin-dom',
'phortune-credit-card-form',
),
'40a6a403' => array(
'javelin-install',
'javelin-dom',
'javelin-reactor-dom',
),
42126667 => array(
'javelin-behavior',
'javelin-dom',
'javelin-request',
),
'44168bad' => array(
'javelin-behavior',
'javelin-dom',
'phabricator-prefab',
),
'44959b73' => array(
'javelin-util',
'javelin-uri',
'javelin-install',
),
'453c5375' => array(
'javelin-behavior',
'javelin-dom',
),
'469c0d9e' => array(
'javelin-behavior',
'javelin-dom',
'javelin-workflow',
),
'477359c8' => array(
'javelin-install',
'javelin-dom',
'phabricator-notification',
),
47830651 => array(
'javelin-behavior',
'javelin-dom',
'javelin-view-renderer',
'javelin-install',
),
'47c794d8' => array(
'javelin-install',
'javelin-dom',
'javelin-reactor-dom',
),
48086888 => array(
'javelin-behavior',
'javelin-dom',
'javelin-workflow',
),
'49b73b36' => array(
'javelin-behavior',
'javelin-dom',
'javelin-request',
'javelin-util',
),
'4a2430d7' => array(
'phui-fontkit-css',
),
'4d94d9c3' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'phuix-dropdown-menu',
),
'4e3e79a6' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'4e9b766b' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-request',
),
'4fdb476d' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'503e17fd' => array(
'javelin-install',
'javelin-typeahead-source',
'javelin-util',
),
'519705ea' => array(
'javelin-install',
'javelin-dom',
'javelin-reactor-dom',
),
'54b612ba' => array(
'javelin-color',
'javelin-install',
'javelin-util',
),
'54f314a0' => array(
'javelin-install',
'javelin-util',
'javelin-request',
'javelin-typeahead-source',
),
'558829c2' => array(
'javelin-stratcom',
'javelin-behavior',
'javelin-vector',
'javelin-dom',
),
58562350 => array(
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-install',
'javelin-workflow',
'javelin-router',
'javelin-behavior-device',
'javelin-vector',
),
'59b251eb' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-vector',
'javelin-dom',
),
'5b2e3e2b' => array(
'javelin-stratcom',
'javelin-request',
'javelin-dom',
'javelin-vector',
'javelin-install',
'javelin-util',
'javelin-mask',
'javelin-uri',
'javelin-routable',
),
'5c1c758c' => array(
'javelin-install',
),
'5c54cbf3' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'5c93c52c' => array(
'javelin-install',
'javelin-dom',
'javelin-vector',
),
'5fefb143' => array(
'javelin-behavior',
'javelin-dom',
'javelin-workflow',
'javelin-stratcom',
),
60479091 => array(
'phabricator-busy',
'javelin-behavior',
),
'60821bc7' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'6153c708' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-workflow',
),
'61cbc29a' => array(
'javelin-magical-init',
'javelin-util',
),
'62dfea03' => array(
'javelin-install',
'javelin-util',
),
'6882e80a' => array(
'javelin-dom',
),
'69adf288' => array(
'javelin-install',
),
'6c2b09a2' => array(
'javelin-install',
'javelin-util',
),
'6c53634d' => array(
'javelin-install',
'javelin-event',
'javelin-util',
'javelin-magical-init',
),
'6d3e1947' => array(
'javelin-behavior',
'javelin-diffusion-locate-file-source',
'javelin-dom',
'javelin-typeahead',
'javelin-uri',
),
'6d49590e' => array(
'javelin-behavior',
'javelin-dom',
'phabricator-drag-and-drop-file-upload',
'phabricator-textareautils',
),
'6e2de6f2' => array(
'multirow-row-manager',
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-stratcom',
'javelin-json',
'phabricator-prefab',
),
'6e8cefa4' => array(
'javelin-install',
'javelin-dom',
'javelin-util',
),
'6eff08aa' => array(
'javelin-install',
'javelin-util',
'javelin-stratcom',
),
'6f7962d5' => array(
'javelin-magical-init',
'javelin-install',
'javelin-util',
'javelin-vector',
'javelin-stratcom',
),
'6f7a9da8' => array(
'javelin-install',
),
'70baed2f' => array(
'javelin-install',
'javelin-dom',
'javelin-vector',
'javelin-util',
),
'724b1247' => array(
'javelin-behavior',
'javelin-typeahead-ondemand-source',
'javelin-typeahead',
'javelin-dom',
'javelin-uri',
'javelin-util',
'javelin-stratcom',
'phabricator-prefab',
),
'72da38cc' => array(
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-typeahead',
'javelin-tokenizer',
'javelin-typeahead-preloaded-source',
'javelin-typeahead-ondemand-source',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
),
'7319e029' => array(
'javelin-behavior',
'javelin-dom',
),
'73d09eef' => array(
'javelin-behavior',
'javelin-vector',
'javelin-dom',
),
'7644823e' => array(
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-install',
),
'76b9fc3e' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'phabricator-draggable-list',
),
'76f4ebed' => array(
'javelin-install',
'javelin-reactor',
'javelin-util',
),
'7814b593' => array(
'javelin-request',
'javelin-behavior',
'javelin-dom',
'javelin-router',
'javelin-util',
'phabricator-busy',
),
'7927a7d3' => array(
'javelin-behavior',
'javelin-quicksand',
),
'798fdb63' => array(
'javelin-install',
'javelin-dom',
'javelin-stratcom',
'javelin-vector',
),
'7a68dda3' => array(
'owners-path-editor',
'javelin-behavior',
),
'7b98d7c5' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
),
'7cbe244b' => array(
'javelin-install',
'javelin-util',
'javelin-request',
'javelin-router',
),
'7e41274a' => array(
'javelin-install',
),
'7e8468ae' => array(
'javelin-install',
'javelin-dom',
'javelin-vector',
'javelin-request',
'javelin-uri',
),
'7ebaeed3' => array(
'herald-rule-editor',
'javelin-behavior',
),
'7ee2b591' => array(
'javelin-behavior',
'javelin-history',
),
'7fa4b248' => array(
'javelin-install',
'javelin-util',
'javelin-request',
'javelin-dom',
'javelin-uri',
'phabricator-file-upload',
),
82439934 => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-workflow',
'phabricator-draggable-list',
),
'82ce2142' => array(
'aphront-typeahead-control-css',
),
'834a1173' => array(
'javelin-behavior',
'javelin-scrollbar',
),
'84845b5b' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-workflow',
'phabricator-draggable-list',
),
'85ea0626' => array(
'javelin-install',
),
'8694b1df' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'phabricator-tooltip',
'changeset-view-manager',
),
'87cb6b51' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-vector',
'javelin-stratcom',
'javelin-workflow',
'phabricator-draggable-list',
),
'88236f00' => array(
'javelin-behavior',
'phabricator-keyboard-shortcut',
'javelin-stratcom',
),
'886fd850' => array(
'javelin-install',
'javelin-reactor-dom',
'javelin-view-html',
'javelin-view-interpreter',
'javelin-view-renderer',
),
'887ad43f' => array(
'javelin-behavior',
'javelin-request',
'javelin-stratcom',
'javelin-dom',
),
'88f0c5b3' => array(
'javelin-behavior',
'javelin-dom',
'javelin-vector',
),
'8a41885b' => array(
'javelin-install',
'javelin-dom',
),
'8b3fd187' => array(
'javelin-install',
'javelin-util',
'javelin-request',
'javelin-typeahead-source',
),
'8ce821c5' => array(
'phabricator-notification',
'javelin-stratcom',
'javelin-behavior',
),
'8e1389b5' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-request',
'javelin-util',
'phabricator-shaped-request',
),
'8ef9ab58' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
),
'8fc1c918' => array(
'javelin-behavior',
'javelin-uri',
'phabricator-notification',
),
'9414ff18' => array(
'javelin-behavior',
'javelin-resource',
'javelin-stratcom',
'javelin-workflow',
'javelin-util',
),
'949c0fe5' => array(
'javelin-install',
),
'94b750d2' => array(
'javelin-install',
'javelin-stratcom',
'javelin-util',
'javelin-behavior',
'javelin-json',
'javelin-dom',
'javelin-resource',
'javelin-routable',
),
'988040b4' => array(
'javelin-install',
'javelin-dom',
'javelin-reactor-dom',
),
'9c2623f4' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-workflow',
'phabricator-phtize',
'phabricator-drag-and-drop-file-upload',
'phabricator-draggable-list',
),
'9f36c42d' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-vector',
),
'9f7309fb' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-request',
'phabricator-shaped-request',
),
'a0216452' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-behavior-device',
'javelin-scrollbar',
'javelin-quicksand',
'phabricator-keyboard-shortcut',
'conpherence-thread-manager',
),
'a0b57eb8' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-workflow',
'javelin-util',
'phabricator-keyboard-shortcut',
),
'a155550f' => array(
'javelin-install',
'javelin-dom',
'javelin-reactor-dom',
),
'a16ec1c6' => array(
'javelin-install',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
'javelin-vector',
'javelin-magical-init',
),
'a205cf28' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-vector',
'javelin-install',
),
'a80d0378' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'a8d8459d' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
),
'a8da01f0' => array(
'javelin-behavior',
'javelin-uri',
'phabricator-keyboard-shortcut',
),
'a9f88de2' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-workflow',
'javelin-fx',
'javelin-util',
),
'aa1733d0' => array(
'multirow-row-manager',
'javelin-install',
'path-typeahead',
'javelin-dom',
'javelin-util',
'phabricator-prefab',
),
'b1f0ccee' => array(
'javelin-install',
'javelin-dom',
'javelin-reactor-dom',
),
'b2b4fbaf' => array(
'javelin-behavior',
'javelin-dom',
'javelin-uri',
'javelin-request',
),
'b3a4b884' => array(
'javelin-behavior',
'phabricator-prefab',
),
'b3e7d692' => array(
'javelin-install',
),
'b42eddc7' => array(
'javelin-install',
'javelin-dom',
'javelin-typeahead-preloaded-source',
'javelin-util',
),
'b5c256b8' => array(
'javelin-install',
'javelin-dom',
),
'b5d57730' => array(
'javelin-install',
'javelin-stratcom',
'javelin-dom',
'javelin-util',
),
'bb928342' => array(
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-install',
'javelin-workflow',
'javelin-router',
'javelin-behavior-device',
'javelin-vector',
),
'bba9eedf' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'bbdf75ca' => array(
'javelin-behavior',
'javelin-dom',
'javelin-uri',
'javelin-mask',
'phabricator-drag-and-drop-file-upload',
),
'bd4c8dca' => array(
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-vector',
'javelin-stratcom',
),
'bdaf4d04' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-request',
),
'bdf2226d' => array(
'javelin-behavior',
'javelin-aphlict',
'javelin-stratcom',
'javelin-request',
'javelin-uri',
'javelin-dom',
'javelin-json',
'javelin-router',
'javelin-util',
'javelin-leader',
'javelin-sound',
'phabricator-notification',
),
'be807912' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'phabricator-shaped-request',
),
'c1700f6f' => array(
'javelin-install',
'javelin-util',
'javelin-stratcom',
'javelin-dom',
'javelin-vector',
),
'c51ae228' => array(
'javelin-behavior',
'javelin-util',
'javelin-dom',
'javelin-stratcom',
'javelin-vector',
),
'c90a04fc' => array(
'javelin-dom',
'javelin-dynval',
'javelin-reactor',
'javelin-reactornode',
'javelin-install',
'javelin-util',
),
'ca3f91eb' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'phabricator-phtize',
),
+ 'cda6de80' => array(
+ 'javelin-behavior',
+ 'javelin-dom',
+ 'javelin-util',
+ 'javelin-stratcom',
+ 'javelin-workflow',
+ 'javelin-behavior-device',
+ 'javelin-history',
+ 'javelin-vector',
+ 'phabricator-shaped-request',
+ 'conpherence-thread-manager',
+ ),
'd19198c8' => array(
'javelin-install',
'javelin-dom',
'javelin-util',
'javelin-dynval',
'javelin-reactor-dom',
),
'd254d646' => array(
'javelin-util',
),
'd4505101' => array(
'javelin-stratcom',
'javelin-install',
'javelin-uri',
'javelin-util',
),
'd4a14807' => array(
'javelin-install',
'javelin-dom',
'javelin-view',
),
'd4eecc63' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
),
'd75709e6' => array(
'javelin-behavior',
'javelin-workflow',
'javelin-json',
'javelin-dom',
'phabricator-keyboard-shortcut',
),
'd835b03a' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'phabricator-shaped-request',
),
'dbbf48b6' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'phabricator-busy',
),
'de2e896f' => array(
'javelin-behavior',
'javelin-dom',
'javelin-typeahead',
'javelin-typeahead-ondemand-source',
'javelin-dom',
),
- 'de5579b4' => array(
- 'javelin-behavior',
- 'javelin-dom',
- 'javelin-util',
- 'javelin-stratcom',
- 'javelin-workflow',
- 'javelin-behavior-device',
- 'javelin-history',
- 'javelin-vector',
- 'phabricator-shaped-request',
- 'conpherence-thread-manager',
- ),
'e10f8e18' => array(
'javelin-behavior',
'javelin-dom',
'phabricator-prefab',
),
'e19cfd6e' => array(
'phui-inline-comment-view-css',
),
'e1d25dfb' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'phabricator-draggable-list',
),
'e1ff79b1' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'e292eaf4' => array(
'javelin-install',
),
'e32d14ab' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'phabricator-phtize',
'phabricator-textareautils',
'javelin-workflow',
'javelin-vector',
),
'e379b58e' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-vector',
'javelin-dom',
'javelin-uri',
),
'e4cc26b3' => array(
'javelin-behavior',
'javelin-dom',
),
'e566f52c' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'e5822781' => array(
'javelin-behavior',
'javelin-dom',
'javelin-json',
'javelin-workflow',
'javelin-magical-init',
),
'e58bf807' => array(
'javelin-behavior',
'javelin-util',
'javelin-stratcom',
'javelin-dom',
'javelin-vector',
'javelin-magical-init',
'javelin-request',
'javelin-history',
'javelin-workflow',
'javelin-mask',
'javelin-behavior-device',
'phabricator-keyboard-shortcut',
),
'e723c323' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-util',
'javelin-vector',
'differential-inline-comment-editor',
),
'e9581f08' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'phabricator-draggable-list',
),
'ea681761' => array(
'javelin-behavior',
'javelin-aphlict',
'phabricator-phtize',
'javelin-dom',
),
'efe49472' => array(
'javelin-install',
'javelin-util',
),
'f13d6d1d' => array(
'javelin-install',
'javelin-dom',
'javelin-fx',
),
'f24f3253' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'phabricator-prefab',
'multirow-row-manager',
'javelin-json',
),
'f36e01af' => array(
'javelin-behavior',
'javelin-behavior-device',
'javelin-stratcom',
'javelin-vector',
'phabricator-hovercard',
),
'f3fef818' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'phuix-dropdown-menu',
'phuix-action-list-view',
'phuix-action-view',
'javelin-workflow',
),
'f6555212' => array(
'javelin-install',
'javelin-reactornode',
'javelin-util',
'javelin-reactor',
),
'f7379f45' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'phabricator-shaped-request',
),
'f7f1289f' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
),
'f7fc67ec' => array(
'javelin-install',
'javelin-typeahead',
'javelin-dom',
'javelin-request',
'javelin-typeahead-ondemand-source',
'javelin-util',
),
'f8248bc5' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-json',
'javelin-stratcom',
'phabricator-shaped-request',
),
'f829edb3' => array(
'javelin-view',
'javelin-install',
'javelin-dom',
),
'f8ba29d7' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-mask',
'javelin-util',
'phabricator-busy',
),
'f9539603' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-uri',
),
'fa0f4fc2' => array(
'javelin-behavior',
'javelin-dom',
'javelin-vector',
'javelin-magical-init',
),
'fc91ab6c' => array(
'javelin-behavior',
'javelin-dom',
'phortune-credit-card-form',
),
'fe287620' => array(
'javelin-install',
'javelin-dom',
'javelin-view-visitor',
'javelin-util',
),
'fe9a552f' => array(
'javelin-behavior',
'multirow-row-manager',
'javelin-dom',
'javelin-util',
'phabricator-prefab',
'javelin-json',
),
),
'packages' => array(
'core.pkg.css' => array(
'phabricator-core-css',
'phabricator-zindex-css',
'phui-button-css',
'phabricator-standard-page-view',
'aphront-dialog-view-css',
'phui-form-view-css',
'aphront-panel-view-css',
'aphront-table-view-css',
'aphront-tokenizer-control-css',
'aphront-typeahead-control-css',
'aphront-list-filter-view-css',
'phabricator-remarkup-css',
'syntax-highlighting-css',
'aphront-pager-view-css',
'phabricator-transaction-view-css',
'aphront-tooltip-css',
'phabricator-flag-css',
'phui-info-view-css',
'sprite-gradient-css',
'sprite-menu-css',
'phabricator-main-menu-view',
'phabricator-notification-css',
'phabricator-notification-menu-css',
'lightbox-attachment-css',
'phui-header-view-css',
'phabricator-filetree-view-css',
'phabricator-nav-view-css',
'phabricator-side-menu-view-css',
'phui-crumbs-view-css',
'phui-object-item-list-view-css',
'global-drag-and-drop-css',
'phui-spacing-css',
'phui-form-css',
'phui-icon-view-css',
'phabricator-application-launch-view-css',
'phabricator-action-list-view-css',
'phui-property-list-view-css',
'phui-tag-view-css',
'phui-list-view-css',
'font-fontawesome',
'phui-font-icon-base-css',
'sprite-main-header-css',
'phui-box-css',
'phui-object-box-css',
'phui-timeline-view-css',
'sprite-tokens-css',
'tokens-css',
'phui-status-list-view-css',
'phui-feed-story-css',
'phabricator-feed-css',
'phabricator-dashboard-css',
'aphront-multi-column-view-css',
'phui-action-header-view-css',
),
'core.pkg.js' => array(
'javelin-util',
'javelin-install',
'javelin-event',
'javelin-stratcom',
'javelin-behavior',
'javelin-resource',
'javelin-request',
'javelin-vector',
'javelin-dom',
'javelin-json',
'javelin-uri',
'javelin-workflow',
'javelin-mask',
'javelin-typeahead',
'javelin-typeahead-normalizer',
'javelin-typeahead-source',
'javelin-typeahead-preloaded-source',
'javelin-typeahead-ondemand-source',
'javelin-tokenizer',
'javelin-history',
'javelin-router',
'javelin-routable',
'javelin-behavior-aphront-basic-tokenizer',
'javelin-behavior-workflow',
'javelin-behavior-aphront-form-disable-on-submit',
'phabricator-keyboard-shortcut-manager',
'phabricator-keyboard-shortcut',
'javelin-behavior-phabricator-keyboard-shortcuts',
'javelin-behavior-refresh-csrf',
'javelin-behavior-phabricator-watch-anchor',
'javelin-behavior-phabricator-autofocus',
'phuix-dropdown-menu',
'phuix-action-list-view',
'phuix-action-view',
'phabricator-phtize',
'javelin-behavior-phabricator-oncopy',
'phabricator-tooltip',
'javelin-behavior-phabricator-tooltips',
'phabricator-prefab',
'javelin-behavior-device',
'javelin-behavior-toggle-class',
'javelin-behavior-lightbox-attachments',
'phabricator-busy',
'javelin-aphlict',
'phabricator-notification',
'javelin-behavior-aphlict-listen',
'javelin-behavior-phabricator-search-typeahead',
'javelin-behavior-aphlict-dropdown',
'javelin-behavior-history-install',
'javelin-behavior-phabricator-gesture',
'javelin-behavior-phabricator-active-nav',
'javelin-behavior-phabricator-nav',
'javelin-behavior-phabricator-remarkup-assist',
'phabricator-textareautils',
'phabricator-file-upload',
'javelin-behavior-global-drag-and-drop',
'javelin-behavior-phabricator-reveal-content',
'phabricator-hovercard',
'javelin-behavior-phabricator-hovercards',
'javelin-color',
'javelin-fx',
'phabricator-draggable-list',
'javelin-behavior-phabricator-transaction-list',
'javelin-behavior-phabricator-show-older-transactions',
'javelin-behavior-phui-timeline-dropdown-menu',
'javelin-behavior-doorkeeper-tag',
'phabricator-title',
'javelin-leader',
'javelin-websocket',
'javelin-behavior-dashboard-async-panel',
'javelin-behavior-dashboard-tab-panel',
),
'darkconsole.pkg.js' => array(
'javelin-behavior-dark-console',
'javelin-behavior-error-log',
),
'differential.pkg.css' => array(
'differential-core-view-css',
'differential-changeset-view-css',
'differential-results-table-css',
'differential-revision-history-css',
'differential-revision-list-css',
'differential-table-of-contents-css',
'differential-revision-comment-css',
'differential-revision-add-comment-css',
'phabricator-object-selector-css',
'phabricator-content-source-view-css',
'inline-comment-summary-css',
),
'differential.pkg.js' => array(
'phabricator-drag-and-drop-file-upload',
'phabricator-shaped-request',
'javelin-behavior-differential-feedback-preview',
'javelin-behavior-differential-edit-inline-comments',
'javelin-behavior-differential-populate',
'javelin-behavior-differential-diff-radios',
'javelin-behavior-differential-comment-jump',
'javelin-behavior-differential-add-reviewers-and-ccs',
'javelin-behavior-differential-keyboard-navigation',
'javelin-behavior-aphront-drag-and-drop-textarea',
'javelin-behavior-phabricator-object-selector',
'javelin-behavior-repository-crossreference',
'javelin-behavior-load-blame',
'differential-inline-comment-editor',
'javelin-behavior-differential-dropdown-menus',
'javelin-behavior-differential-toggle-files',
'javelin-behavior-differential-user-select',
'javelin-behavior-aphront-more',
),
'diffusion.pkg.css' => array(
'diffusion-icons-css',
),
'diffusion.pkg.js' => array(
'javelin-behavior-diffusion-pull-lastmodified',
'javelin-behavior-diffusion-commit-graph',
'javelin-behavior-audit-preview',
),
'maniphest.pkg.css' => array(
'maniphest-task-summary-css',
),
'maniphest.pkg.js' => array(
'javelin-behavior-maniphest-batch-selector',
'javelin-behavior-maniphest-transaction-controls',
'javelin-behavior-maniphest-transaction-preview',
'javelin-behavior-maniphest-transaction-expand',
'javelin-behavior-maniphest-subpriority-editor',
'javelin-behavior-maniphest-list-editor',
),
),
);
diff --git a/src/applications/conpherence/controller/ConpherenceNewController.php b/src/applications/conpherence/controller/ConpherenceNewController.php
index a08b105008..a887404c7d 100644
--- a/src/applications/conpherence/controller/ConpherenceNewController.php
+++ b/src/applications/conpherence/controller/ConpherenceNewController.php
@@ -1,107 +1,91 @@
getUser();
$title = pht('New Message');
$participants = array();
$participant_prefill = null;
$message = '';
$e_participants = null;
$e_message = null;
$errors = array();
// this comes from ajax requests from all over. should be a single phid.
if ($request->isFormPost()) {
$participants = $request->getArr('participants');
$message = $request->getStr('message');
list($error_codes, $conpherence) = ConpherenceEditor::createThread(
$user,
$participants,
$conpherence_title = null,
$message,
PhabricatorContentSource::newFromRequest($request));
if ($error_codes) {
foreach ($error_codes as $error_code) {
switch ($error_code) {
case ConpherenceEditor::ERROR_EMPTY_MESSAGE:
$e_message = pht('Required');
$errors[] = pht(
'You can not send an empty message.');
break;
case ConpherenceEditor::ERROR_EMPTY_PARTICIPANTS:
$e_participants = pht('Required');
$errors[] = pht(
'You must choose at least one recipient for your '.
'message.');
break;
}
}
} else {
$uri = $this->getApplicationURI($conpherence->getID());
return id(new AphrontRedirectResponse())
->setURI($uri);
}
} else {
$participant_prefill = $request->getStr('participant');
if ($participant_prefill) {
$participants[] = $participant_prefill;
}
}
-
- $participant_handles = array();
- if ($participants) {
- $participant_handles = id(new PhabricatorHandleQuery())
- ->setViewer($user)
- ->withPHIDs($participants)
- ->execute();
- }
-
$submit_uri = $this->getApplicationURI('new/');
$cancel_uri = $this->getApplicationURI();
- // TODO - we can get a better cancel_uri once we get better at crazy
- // ajax jonx T2086
- if ($participant_prefill) {
- $handle = $participant_handles[$participant_prefill];
- $cancel_uri = $handle->getURI();
- }
-
$dialog = id(new AphrontDialogView())
->setWidth(AphrontDialogView::WIDTH_FORM)
->setErrors($errors)
->setUser($user)
->setTitle($title)
->addCancelButton($cancel_uri)
->addSubmitButton(pht('Send Message'));
- $form = id(new PHUIFormLayoutView())
+ $form = id(new AphrontFormView())
->setUser($user)
->setFullWidth(true)
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
- ->setName('participants')
- ->setValue($participant_handles)
- ->setUser($user)
- ->setDatasource(new PhabricatorPeopleDatasource())
- ->setLabel(pht('To'))
- ->setError($e_participants))
+ ->setName('participants')
+ ->setValue($participants)
+ ->setUser($user)
+ ->setDatasource(new PhabricatorPeopleDatasource())
+ ->setLabel(pht('To'))
+ ->setError($e_participants))
->appendChild(
id(new PhabricatorRemarkupControl())
->setUser($user)
->setName('message')
->setValue($message)
->setLabel(pht('Message'))
->setError($e_message));
- $dialog->appendChild($form);
+ $dialog->appendForm($form);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
}
diff --git a/src/applications/conpherence/controller/ConpherenceUpdateController.php b/src/applications/conpherence/controller/ConpherenceUpdateController.php
index 3b67b6c1df..e2c82e9318 100644
--- a/src/applications/conpherence/controller/ConpherenceUpdateController.php
+++ b/src/applications/conpherence/controller/ConpherenceUpdateController.php
@@ -1,428 +1,428 @@
getUser();
$conpherence_id = $request->getURIData('id');
if (!$conpherence_id) {
return new Aphront404Response();
}
$needed_capabilities = array(PhabricatorPolicyCapability::CAN_VIEW);
$action = $request->getStr('action', ConpherenceUpdateActions::METADATA);
switch ($action) {
case ConpherenceUpdateActions::REMOVE_PERSON:
$person_phid = $request->getStr('remove_person');
if ($person_phid != $user->getPHID()) {
$needed_capabilities[] = PhabricatorPolicyCapability::CAN_EDIT;
}
break;
case ConpherenceUpdateActions::ADD_PERSON:
case ConpherenceUpdateActions::METADATA:
$needed_capabilities[] = PhabricatorPolicyCapability::CAN_EDIT;
break;
case ConpherenceUpdateActions::JOIN_ROOM:
$needed_capabilities[] = PhabricatorPolicyCapability::CAN_JOIN;
break;
}
$conpherence = id(new ConpherenceThreadQuery())
->setViewer($user)
->withIDs(array($conpherence_id))
->needFilePHIDs(true)
->needParticipantCache(true)
->requireCapabilities($needed_capabilities)
->executeOne();
$latest_transaction_id = null;
$response_mode = $request->isAjax() ? 'ajax' : 'redirect';
$error_view = null;
$e_file = array();
$errors = array();
$delete_draft = false;
$xactions = array();
if ($request->isFormPost() || ($action == ConpherenceUpdateActions::LOAD)) {
$editor = id(new ConpherenceEditor())
->setContinueOnNoEffect($request->isContinueRequest())
->setContentSourceFromRequest($request)
->setActor($user);
switch ($action) {
case ConpherenceUpdateActions::DRAFT:
$draft = PhabricatorDraft::newFromUserAndKey(
$user,
$conpherence->getPHID());
$draft->setDraft($request->getStr('text'));
$draft->replaceOrDelete();
return new AphrontAjaxResponse();
case ConpherenceUpdateActions::JOIN_ROOM:
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(
ConpherenceTransactionType::TYPE_PARTICIPANTS)
->setNewValue(array('+' => array($user->getPHID())));
$delete_draft = true;
$message = $request->getStr('text');
if ($message) {
$message_xactions = $editor->generateTransactionsFromText(
$user,
$conpherence,
$message);
$xactions = array_merge($xactions, $message_xactions);
}
// for now, just redirect back to the conpherence so everything
// will work okay...!
$response_mode = 'redirect';
break;
case ConpherenceUpdateActions::MESSAGE:
$message = $request->getStr('text');
$xactions = $editor->generateTransactionsFromText(
$user,
$conpherence,
$message);
$delete_draft = true;
break;
case ConpherenceUpdateActions::ADD_PERSON:
$person_phids = $request->getArr('add_person');
if (!empty($person_phids)) {
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(
ConpherenceTransactionType::TYPE_PARTICIPANTS)
->setNewValue(array('+' => $person_phids));
}
break;
case ConpherenceUpdateActions::REMOVE_PERSON:
if (!$request->isContinueRequest()) {
// do nothing; we'll display a confirmation dialogue instead
break;
}
$person_phid = $request->getStr('remove_person');
if ($person_phid && $person_phid == $user->getPHID()) {
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(
ConpherenceTransactionType::TYPE_PARTICIPANTS)
->setNewValue(array('-' => array($person_phid)));
$response_mode = 'go-home';
}
break;
case ConpherenceUpdateActions::NOTIFICATIONS:
$notifications = $request->getStr('notifications');
$participant = $conpherence->getParticipantIfExists($user->getPHID());
if (!$participant) {
return id(new Aphront404Response());
}
$participant->setSettings(array('notifications' => $notifications));
$participant->save();
$result = pht(
'Updated notification settings to "%s".',
ConpherenceSettings::getHumanString($notifications));
return id(new AphrontAjaxResponse())
->setContent($result);
break;
case ConpherenceUpdateActions::METADATA:
$updated = false;
// all metadata updates are continue requests
if (!$request->isContinueRequest()) {
break;
}
$title = $request->getStr('title');
if ($title != $conpherence->getTitle()) {
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(ConpherenceTransactionType::TYPE_TITLE)
->setNewValue($title);
$updated = true;
if (!$request->getExists('force_ajax')) {
$response_mode = 'redirect';
}
}
if (!$updated) {
$errors[] = pht(
'That was a non-update. Try cancel.');
}
break;
case ConpherenceUpdateActions::LOAD:
$updated = false;
$response_mode = 'ajax';
break;
default:
throw new Exception('Unknown action: '.$action);
break;
}
if ($xactions) {
try {
$xactions = $editor->applyTransactions($conpherence, $xactions);
if ($delete_draft) {
$draft = PhabricatorDraft::newFromUserAndKey(
$user,
$conpherence->getPHID());
$draft->delete();
}
} catch (PhabricatorApplicationTransactionNoEffectException $ex) {
return id(new PhabricatorApplicationTransactionNoEffectResponse())
->setCancelURI($this->getApplicationURI($conpherence_id.'/'))
->setException($ex);
}
}
if ($xactions || ($action == ConpherenceUpdateActions::LOAD)) {
switch ($response_mode) {
case 'ajax':
$latest_transaction_id = $request->getInt('latest_transaction_id');
$content = $this->loadAndRenderUpdates(
$action,
$conpherence_id,
$latest_transaction_id);
return id(new AphrontAjaxResponse())
->setContent($content);
break;
case 'go-home':
return id(new AphrontRedirectResponse())
->setURI($this->getApplicationURI());
break;
case 'redirect':
default:
return id(new AphrontRedirectResponse())
->setURI($this->getApplicationURI($conpherence->getID().'/'));
break;
}
}
}
if ($errors) {
$error_view = id(new PHUIInfoView())
->setErrors($errors);
}
switch ($action) {
case ConpherenceUpdateActions::ADD_PERSON:
$dialogue = $this->renderAddPersonDialogue($conpherence);
break;
case ConpherenceUpdateActions::REMOVE_PERSON:
$dialogue = $this->renderRemovePersonDialogue($conpherence);
break;
case ConpherenceUpdateActions::METADATA:
default:
$dialogue = $this->renderMetadataDialogue($conpherence, $error_view);
break;
}
return id(new AphrontDialogResponse())
->setDialog($dialogue
->setUser($user)
->setWidth(AphrontDialogView::WIDTH_FORM)
->setSubmitURI($this->getApplicationURI('update/'.$conpherence_id.'/'))
->addSubmitButton()
->addCancelButton($this->getApplicationURI($conpherence->getID().'/')));
}
private function renderAddPersonDialogue(
ConpherenceThread $conpherence) {
$request = $this->getRequest();
$user = $request->getUser();
$add_person = $request->getStr('add_person');
- $form = id(new PHUIFormLayoutView())
+ $form = id(new AphrontFormView())
->setUser($user)
->setFullWidth(true)
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
- ->setName('add_person')
- ->setUser($user)
- ->setDatasource(new PhabricatorPeopleDatasource()));
+ ->setName('add_person')
+ ->setUser($user)
+ ->setDatasource(new PhabricatorPeopleDatasource()));
require_celerity_resource('conpherence-update-css');
$view = id(new AphrontDialogView())
->setTitle(pht('Add Participants'))
->addHiddenInput('action', 'add_person')
->addHiddenInput(
'latest_transaction_id',
$request->getInt('latest_transaction_id'))
- ->appendChild($form);
+ ->appendForm($form);
if ($request->getExists('minimal_display')) {
$view->addHiddenInput('minimal_display', true);
}
return $view;
}
private function renderRemovePersonDialogue(
ConpherenceThread $conpherence) {
$request = $this->getRequest();
$user = $request->getUser();
$remove_person = $request->getStr('remove_person');
$participants = $conpherence->getParticipants();
if ($conpherence->getIsRoom()) {
$message = pht(
'Are you sure you want to remove yourself from this room?');
} else {
$message = pht(
'Are you sure you want to remove yourself from this thread?');
if (count($participants) == 1) {
$message .= pht(
'The thread will be inaccessible forever and ever.');
} else {
$message .= pht(
'Someone else in the thread can add you back later.');
}
}
$body = phutil_tag(
'p',
array(
),
$message);
require_celerity_resource('conpherence-update-css');
return id(new AphrontDialogView())
->setTitle(pht('Remove Participants'))
->addHiddenInput('action', 'remove_person')
->addHiddenInput('remove_person', $remove_person)
->addHiddenInput(
'latest_transaction_id',
$request->getInt('latest_transaction_id'))
->addHiddenInput('__continue__', true)
->appendChild($body);
}
private function renderMetadataDialogue(
ConpherenceThread $conpherence,
$error_view) {
$request = $this->getRequest();
$form = id(new PHUIFormLayoutView())
->appendChild($error_view)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Title'))
->setName('title')
->setValue($conpherence->getTitle()));
require_celerity_resource('conpherence-update-css');
$view = id(new AphrontDialogView())
->setTitle(pht('Update Conpherence'))
->addHiddenInput('action', 'metadata')
->addHiddenInput(
'latest_transaction_id',
$request->getInt('latest_transaction_id'))
->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);
}
return $view;
}
private function loadAndRenderUpdates(
$action,
$conpherence_id,
$latest_transaction_id) {
$need_widget_data = false;
$need_transactions = false;
$need_participant_cache = false;
switch ($action) {
case ConpherenceUpdateActions::METADATA:
$need_participant_cache = true;
$need_transactions = true;
break;
case ConpherenceUpdateActions::LOAD:
$need_transactions = true;
break;
case ConpherenceUpdateActions::MESSAGE:
case ConpherenceUpdateActions::ADD_PERSON:
$need_transactions = true;
$need_widget_data = true;
break;
case ConpherenceUpdateActions::REMOVE_PERSON:
case ConpherenceUpdateActions::NOTIFICATIONS:
default:
break;
}
$user = $this->getRequest()->getUser();
$conpherence = id(new ConpherenceThreadQuery())
->setViewer($user)
->setAfterTransactionID($latest_transaction_id)
->needParticipantCache($need_participant_cache)
->needWidgetData($need_widget_data)
->needTransactions($need_transactions)
->withIDs(array($conpherence_id))
->executeOne();
if ($need_transactions) {
$data = ConpherenceTransactionView::renderTransactions(
$user,
$conpherence,
!$this->getRequest()->getExists('minimal_display'));
$participant_obj = $conpherence->getParticipant($user->getPHID());
$participant_obj->markUpToDate($conpherence, $data['latest_transaction']);
} else {
$data = array();
}
$rendered_transactions = idx($data, 'transactions');
$new_latest_transaction_id = idx($data, 'latest_transaction_id');
$widget_uri = $this->getApplicationURI('update/'.$conpherence->getID().'/');
$nav_item = null;
$header = null;
$people_widget = null;
$file_widget = null;
switch ($action) {
case ConpherenceUpdateActions::METADATA:
$policy_objects = id(new PhabricatorPolicyQuery())
->setViewer($user)
->setObject($conpherence)
->execute();
$header = $this->buildHeaderPaneContent($conpherence, $policy_objects);
$nav_item = id(new ConpherenceThreadListView())
->setUser($user)
->setBaseURI($this->getApplicationURI())
->renderSingleThread($conpherence);
break;
case ConpherenceUpdateActions::MESSAGE:
$file_widget = id(new ConpherenceFileWidgetView())
->setUser($this->getRequest()->getUser())
->setConpherence($conpherence)
->setUpdateURI($widget_uri);
break;
case ConpherenceUpdateActions::ADD_PERSON:
$people_widget = id(new ConpherencePeopleWidgetView())
->setUser($user)
->setConpherence($conpherence)
->setUpdateURI($widget_uri);
break;
case ConpherenceUpdateActions::REMOVE_PERSON:
case ConpherenceUpdateActions::NOTIFICATIONS:
default:
break;
}
$people_html = null;
if ($people_widget) {
$people_html = hsprintf('%s', $people_widget->render());
}
$data = $conpherence->getDisplayData($user);
$content = array(
'transactions' => hsprintf('%s', $rendered_transactions),
'conpherence_title' => (string) $data['title'],
'latest_transaction_id' => $new_latest_transaction_id,
'nav_item' => hsprintf('%s', $nav_item),
'conpherence_phid' => $conpherence->getPHID(),
'header' => hsprintf('%s', $header),
'file_widget' => $file_widget ? $file_widget->render() : null,
'people_widget' => $people_html,
);
return $content;
}
}
diff --git a/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php b/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php
index 4c15049845..822b41e440 100644
--- a/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php
+++ b/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php
@@ -1,158 +1,155 @@
isRooms = $bool;
return $this;
}
public function getResultTypeDescription() {
if ($this->isRooms) {
$type = pht('Rooms');
} else {
$type = pht('Threads');
}
return $type;
}
public function getApplicationClassName() {
return 'PhabricatorConpherenceApplication';
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter(
'participantPHIDs',
$this->readUsersFromRequest($request, 'participants'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new ConpherenceThreadQuery())
->withIsRoom($this->isRooms)
->needParticipantCache(true);
$participant_phids = $saved->getParameter('participantPHIDs', array());
if ($participant_phids && is_array($participant_phids)) {
$query->withParticipantPHIDs($participant_phids);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved) {
- $phids = $saved->getParameter('participantPHIDs', array());
- $participant_handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($phids)
- ->execute();
+ $participant_phids = $saved->getParameter('participantPHIDs', array());
+
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
- ->setDatasource(new PhabricatorPeopleDatasource())
- ->setName('participants')
- ->setLabel(pht('Participants'))
- ->setValue($participant_handles));
+ ->setDatasource(new PhabricatorPeopleDatasource())
+ ->setName('participants')
+ ->setLabel(pht('Participants'))
+ ->setValue($participant_phids));
}
protected function getURI($path) {
return '/conpherence/search/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array();
if ($this->isRooms) {
$names = array(
'all' => pht('All Rooms'),
);
if ($this->requireViewer()->isLoggedIn()) {
$names['participant'] = pht('Joined Rooms');
$names['messages'] = pht('All Messages');
}
}
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
case 'participant':
return $query->setParameter(
'participantPHIDs',
array($this->requireViewer()->getPHID()));
case 'messages':
$this->setIsRooms(false);
return $query->setParameter(
'participantPHIDs',
array($this->requireViewer()->getPHID()));
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function getRequiredHandlePHIDsForResultList(
array $conpherences,
PhabricatorSavedQuery $query) {
$recent = mpull($conpherences, 'getRecentParticipantPHIDs');
return array_unique(array_mergev($recent));
}
protected function renderResultList(
array $conpherences,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($conpherences, 'ConpherenceThread');
$viewer = $this->requireViewer();
$list = new PHUIObjectItemListView();
$list->setUser($viewer);
foreach ($conpherences as $conpherence) {
$created = phabricator_date($conpherence->getDateCreated(), $viewer);
$data = $conpherence->getDisplayData($viewer);
$title = $data['title'];
$item = id(new PHUIObjectItemView())
->setObjectName($conpherence->getMonogram())
->setHeader($title)
->setHref('/conpherence/'.$conpherence->getID().'/')
->setObject($conpherence)
->addIcon('none', $created)
->addIcon(
'none',
pht('Messages: %d', $conpherence->getMessageCount()))
->addAttribute(
array(
id(new PHUIIconView())->setIconFont('fa-envelope-o', 'green'),
' ',
pht(
'Last updated %s',
phabricator_datetime($conpherence->getDateModified(), $viewer)),
));
$list->addItem($item);
}
return $list;
}
}
diff --git a/src/applications/countdown/query/PhabricatorCountdownSearchEngine.php b/src/applications/countdown/query/PhabricatorCountdownSearchEngine.php
index f15c1a2012..c9af9791a2 100644
--- a/src/applications/countdown/query/PhabricatorCountdownSearchEngine.php
+++ b/src/applications/countdown/query/PhabricatorCountdownSearchEngine.php
@@ -1,150 +1,146 @@
setParameter(
'authorPHIDs',
$this->readUsersFromRequest($request, 'authors'));
$saved->setParameter('upcoming', $request->getBool('upcoming'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorCountdownQuery());
$author_phids = $saved->getParameter('authorPHIDs', array());
if ($author_phids) {
$query->withAuthorPHIDs($author_phids);
}
if ($saved->getParameter('upcoming')) {
$query->withUpcoming(true);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
- $phids = $saved_query->getParameter('authorPHIDs', array());
- $author_handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($phids)
- ->execute();
+ $author_phids = $saved_query->getParameter('authorPHIDs', array());
$upcoming = $saved_query->getParameter('upcoming');
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('authors')
->setLabel(pht('Authors'))
- ->setValue($author_handles))
+ ->setValue($author_phids))
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'upcoming',
1,
pht('Show only countdowns that are still counting down.'),
$upcoming));
}
protected function getURI($path) {
return '/countdown/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'upcoming' => pht('Upcoming'),
'all' => pht('All'),
);
if ($this->requireViewer()->getPHID()) {
$names['authored'] = pht('Authored');
}
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
case 'authored':
return $query->setParameter(
'authorPHIDs',
array($this->requireViewer()->getPHID()));
case 'upcoming':
return $query->setParameter('upcoming', true);
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function getRequiredHandlePHIDsForResultList(
array $countdowns,
PhabricatorSavedQuery $query) {
return mpull($countdowns, 'getAuthorPHID');
}
protected function renderResultList(
array $countdowns,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($countdowns, 'PhabricatorCountdown');
$viewer = $this->requireViewer();
$list = new PHUIObjectItemListView();
$list->setUser($viewer);
foreach ($countdowns as $countdown) {
$id = $countdown->getID();
$item = id(new PHUIObjectItemView())
->setObjectName("C{$id}")
->setHeader($countdown->getTitle())
->setHref($this->getApplicationURI("{$id}/"))
->addByline(
pht(
'Created by %s',
$handles[$countdown->getAuthorPHID()]->renderLink()));
$epoch = $countdown->getEpoch();
if ($epoch >= time()) {
$item->addIcon(
'none',
pht('Ends %s', phabricator_datetime($epoch, $viewer)));
} else {
$item->addIcon(
'delete',
pht('Ended %s', phabricator_datetime($epoch, $viewer)));
$item->setDisabled(true);
}
$list->addItem($item);
}
return $list;
}
}
diff --git a/src/applications/differential/controller/DifferentialDiffCreateController.php b/src/applications/differential/controller/DifferentialDiffCreateController.php
index e23f6cf718..c2d598cad9 100644
--- a/src/applications/differential/controller/DifferentialDiffCreateController.php
+++ b/src/applications/differential/controller/DifferentialDiffCreateController.php
@@ -1,211 +1,213 @@
getViewer();
// If we're on the "Update Diff" workflow, load the revision we're going
// to update.
$revision = null;
$revision_id = $request->getURIData('revisionID');
if ($revision_id) {
$revision = id(new DifferentialRevisionQuery())
->setViewer($viewer)
->withIDs(array($revision_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$revision) {
return new Aphront404Response();
}
}
$diff = null;
// This object is just for policy stuff
$diff_object = DifferentialDiff::initializeNewDiff($viewer);
$repository_phid = null;
- $repository_value = array();
$errors = array();
$e_diff = null;
$e_file = null;
$validation_exception = null;
if ($request->isFormPost()) {
$repository_tokenizer = $request->getArr(
id(new DifferentialRepositoryField())->getFieldKey());
if ($repository_tokenizer) {
$repository_phid = reset($repository_tokenizer);
}
if ($request->getFileExists('diff-file')) {
$diff = PhabricatorFile::readUploadedFileData($_FILES['diff-file']);
} else {
$diff = $request->getStr('diff');
}
if (!strlen($diff)) {
$errors[] = pht(
'You can not create an empty diff. Paste a diff or upload a '.
'file containing a diff.');
$e_diff = pht('Required');
$e_file = pht('Required');
}
if (!$errors) {
try {
$call = new ConduitCall(
'differential.createrawdiff',
array(
'diff' => $diff,
'repositoryPHID' => $repository_phid,
- 'viewPolicy' => $request->getStr('viewPolicy'),));
+ 'viewPolicy' => $request->getStr('viewPolicy'),
+ ));
$call->setUser($viewer);
$result = $call->execute();
$diff_id = $result['id'];
$uri = $this->getApplicationURI("diff/{$diff_id}/");
$uri = new PhutilURI($uri);
if ($revision) {
$uri->setQueryParam('revisionID', $revision->getID());
}
return id(new AphrontRedirectResponse())->setURI($uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
}
}
}
$form = new AphrontFormView();
$arcanist_href = PhabricatorEnv::getDoclink('Arcanist User Guide');
$arcanist_link = phutil_tag(
'a',
array(
'href' => $arcanist_href,
'target' => '_blank',
),
'Learn More');
$cancel_uri = $this->getApplicationURI();
- if ($repository_phid) {
- $repository_value = $this->loadViewerHandles(array($repository_phid));
- }
-
$policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->setObject($diff_object)
->execute();
$info_view = null;
if (!$request->isFormPost()) {
$info_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->setErrors(
array(
array(
pht(
'The best way to create a diff is to use the Arcanist '.
'command-line tool.'),
' ',
$arcanist_link,
),
pht(
'You can also paste a diff below, or upload a file '.
'containing a diff (for example, from %s, %s or %s).',
phutil_tag('tt', array(), 'svn diff'),
phutil_tag('tt', array(), 'git diff'),
phutil_tag('tt', array(), 'hg diff --git')),
));
}
if ($revision) {
$title = pht('Update Diff');
$header = pht('Update Diff');
$button = pht('Continue');
} else {
$title = pht('Create Diff');
$header = pht('Create New Diff');
$button = pht('Create Diff');
}
$form
->setEncType('multipart/form-data')
->setUser($viewer);
if ($revision) {
$revision_handles = $this->loadViewerHandles(array($revision->getPHID()));
$revision_handle = head($revision_handles);
$form->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Updating Revision'))
->setValue($revision_handle->renderLink()));
}
+ if ($repository_phid) {
+ $repository_value = array($repository_phid);
+ } else {
+ $repository_value = array();
+ }
+
$form
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel(pht('Raw Diff'))
->setName('diff')
->setValue($diff)
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
->setError($e_diff))
->appendChild(
id(new AphrontFormFileControl())
->setLabel(pht('Raw Diff From File'))
->setName('diff-file')
->setError($e_file))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
- ->setName(id(new DifferentialRepositoryField())->getFieldKey())
- ->setLabel(pht('Repository'))
- ->setDatasource(new DiffusionRepositoryDatasource())
- ->setValue($repository_value)
- ->setLimit(1))
+ ->setName(id(new DifferentialRepositoryField())->getFieldKey())
+ ->setLabel(pht('Repository'))
+ ->setDatasource(new DiffusionRepositoryDatasource())
+ ->setValue($repository_value)
+ ->setLimit(1))
->appendChild(
id(new AphrontFormPolicyControl())
- ->setUser($viewer)
- ->setName('viewPolicy')
- ->setPolicyObject($diff_object)
- ->setPolicies($policies)
- ->setCapability(PhabricatorPolicyCapability::CAN_VIEW))
+ ->setUser($viewer)
+ ->setName('viewPolicy')
+ ->setPolicyObject($diff_object)
+ ->setPolicies($policies)
+ ->setCapability(PhabricatorPolicyCapability::CAN_VIEW))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($cancel_uri)
->setValue($button));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($header)
->setValidationException($validation_exception)
->setForm($form)
->setFormErrors($errors);
if ($info_view) {
$form_box->setInfoView($info_view);
}
$crumbs = $this->buildApplicationCrumbs();
if ($revision) {
$crumbs->addTextCrumb(
$revision->getMonogram(),
'/'.$revision->getMonogram());
}
$crumbs->addTextCrumb($title);
return $this->buildApplicationPage(
array(
$crumbs,
$form_box,
),
array(
'title' => $title,
));
}
}
diff --git a/src/applications/differential/customfield/DifferentialProjectsField.php b/src/applications/differential/customfield/DifferentialProjectsField.php
index ee50d1bd5e..a784b98247 100644
--- a/src/applications/differential/customfield/DifferentialProjectsField.php
+++ b/src/applications/differential/customfield/DifferentialProjectsField.php
@@ -1,113 +1,110 @@
getPHID()) {
return array();
}
$projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
$revision->getPHID(),
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
$projects = array_reverse($projects);
return $projects;
}
public function getNewValueForApplicationTransactions() {
return array('=' => array_fuse($this->getValue()));
}
public function readValueFromRequest(AphrontRequest $request) {
$this->setValue($request->getArr($this->getFieldKey()));
}
- public function getRequiredHandlePHIDsForEdit() {
- return $this->getValue();
- }
-
public function renderEditControl(array $handles) {
return id(new AphrontFormTokenizerControl())
+ ->setUser($this->getViewer())
->setName($this->getFieldKey())
->setDatasource(new PhabricatorProjectDatasource())
- ->setValue($handles)
+ ->setValue($this->getValue())
->setLabel($this->getFieldName());
}
public function getApplicationTransactionType() {
return PhabricatorTransactions::TYPE_EDGE;
}
public function shouldAppearInCommitMessage() {
return true;
}
public function shouldAllowEditInCommitMessage() {
return true;
}
public function shouldOverwriteWhenCommitMessageIsEdited() {
return true;
}
public function getCommitMessageLabels() {
return array(
'Project',
'Projects',
);
}
public function getRequiredHandlePHIDsForCommitMessage() {
return $this->getValue();
}
public function renderCommitMessageValue(array $handles) {
return $this->renderObjectList($handles);
}
public function shouldAppearInConduitDictionary() {
return true;
}
public function getApplicationTransactionMetadata() {
return array(
'edge:type' => PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
);
}
public function parseValueFromCommitMessage($value) {
return $this->parseObjectList(
$value,
array(
PhabricatorProjectProjectPHIDType::TYPECONST,
));
}
}
diff --git a/src/applications/differential/customfield/DifferentialRepositoryField.php b/src/applications/differential/customfield/DifferentialRepositoryField.php
index ee520fd179..76f40d0957 100644
--- a/src/applications/differential/customfield/DifferentialRepositoryField.php
+++ b/src/applications/differential/customfield/DifferentialRepositoryField.php
@@ -1,169 +1,162 @@
getRepositoryPHID();
}
protected function writeValueToRevision(
DifferentialRevision $revision,
$value) {
$revision->setRepositoryPHID($value);
}
public function readValueFromRequest(AphrontRequest $request) {
$phids = $request->getArr($this->getFieldKey());
$first = head($phids);
$this->setValue(nonempty($first, null));
}
- public function getRequiredHandlePHIDsForEdit() {
- $phids = array();
- if ($this->getValue()) {
- $phids[] = $this->getValue();
- }
- return $phids;
- }
-
public function renderEditControl(array $handles) {
if ($this->getValue()) {
- $control_value = array_select_keys($handles, array($this->getValue()));
+ $value = array($this->getValue());
} else {
- $control_value = array();
+ $value = array();
}
return id(new AphrontFormTokenizerControl())
+ ->setUser($this->getViewer())
->setName($this->getFieldKey())
->setDatasource(new DiffusionRepositoryDatasource())
- ->setValue($control_value)
+ ->setValue($value)
->setError($this->getFieldError())
->setLabel($this->getFieldName())
->setLimit(1);
}
public function getApplicationTransactionRequiredHandlePHIDs(
PhabricatorApplicationTransaction $xaction) {
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
$phids = array();
if ($old) {
$phids[] = $old;
}
if ($new) {
$phids[] = $new;
}
return $phids;
}
public function getApplicationTransactionTitle(
PhabricatorApplicationTransaction $xaction) {
$author_phid = $xaction->getAuthorPHID();
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
if ($old && $new) {
return pht(
'%s changed the repository for this revision from %s to %s.',
$xaction->renderHandleLink($author_phid),
$xaction->renderHandleLink($old),
$xaction->renderHandleLink($new));
} else if ($new) {
return pht(
'%s set the repository for this revision to %s.',
$xaction->renderHandleLink($author_phid),
$xaction->renderHandleLink($new));
} else {
return pht(
'%s removed %s as the repository for this revision.',
$xaction->renderHandleLink($author_phid),
$xaction->renderHandleLink($old));
}
}
public function getApplicationTransactionTitleForFeed(
PhabricatorApplicationTransaction $xaction) {
$object_phid = $xaction->getObjectPHID();
$author_phid = $xaction->getAuthorPHID();
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
if ($old && $new) {
return pht(
'%s updated the repository for %s from %s to %s.',
$xaction->renderHandleLink($author_phid),
$xaction->renderHandleLink($object_phid),
$xaction->renderHandleLink($old),
$xaction->renderHandleLink($new));
} else if ($new) {
return pht(
'%s set the repository for %s to %s.',
$xaction->renderHandleLink($author_phid),
$xaction->renderHandleLink($object_phid),
$xaction->renderHandleLink($new));
} else {
return pht(
'%s removed the repository for %s. (Repository was %s.)',
$xaction->renderHandleLink($author_phid),
$xaction->renderHandleLink($object_phid),
$xaction->renderHandleLink($old));
}
}
public function shouldAppearInPropertyView() {
return true;
}
public function renderPropertyViewLabel() {
return $this->getFieldName();
}
public function getRequiredHandlePHIDsForPropertyView() {
$repository_phid = $this->getObject()->getRepositoryPHID();
if ($repository_phid) {
return array($repository_phid);
}
return array();
}
public function renderPropertyViewValue(array $handles) {
return $this->renderHandleList($handles);
}
public function shouldAppearInTransactionMail() {
return true;
}
public function updateTransactionMailBody(
PhabricatorMetaMTAMailBody $body,
PhabricatorApplicationTransactionEditor $editor,
array $xactions) {
$repository = $this->getObject()->getRepository();
if ($repository === null) {
return;
}
$body->addTextSection(pht('REPOSITORY'),
$repository->getMonogram().' '.$repository->getName());
}
}
diff --git a/src/applications/differential/customfield/DifferentialReviewersField.php b/src/applications/differential/customfield/DifferentialReviewersField.php
index d0794a37e2..9ab77c26ac 100644
--- a/src/applications/differential/customfield/DifferentialReviewersField.php
+++ b/src/applications/differential/customfield/DifferentialReviewersField.php
@@ -1,229 +1,227 @@
getReviewerStatus();
}
public function getNewValueForApplicationTransactions() {
$specs = array();
foreach ($this->getValue() as $reviewer) {
$specs[$reviewer->getReviewerPHID()] = array(
'data' => $reviewer->getEdgeData(),
);
}
return array('=' => $specs);
}
public function readValueFromRequest(AphrontRequest $request) {
// Compute a new set of reviewer objects. For reviewers who haven't been
// added or removed, retain their existing status. Also, respect the new
// order.
$old_status = $this->getValue();
$old_status = mpull($old_status, null, 'getReviewerPHID');
$new_phids = $request->getArr($this->getFieldKey());
$new_phids = array_fuse($new_phids);
$new_status = array();
foreach ($new_phids as $new_phid) {
if (empty($old_status[$new_phid])) {
$new_status[$new_phid] = new DifferentialReviewer(
$new_phid,
array(
'status' => DifferentialReviewerStatus::STATUS_ADDED,
));
} else {
$new_status[$new_phid] = $old_status[$new_phid];
}
}
$this->setValue($new_status);
}
- public function getRequiredHandlePHIDsForEdit() {
+ public function renderEditControl(array $handles) {
$phids = array();
if ($this->getValue()) {
$phids = mpull($this->getValue(), 'getReviewerPHID');
}
- return $phids;
- }
- public function renderEditControl(array $handles) {
return id(new AphrontFormTokenizerControl())
+ ->setUser($this->getViewer())
->setName($this->getFieldKey())
->setDatasource(new PhabricatorProjectOrUserDatasource())
- ->setValue($handles)
+ ->setValue($phids)
->setError($this->getFieldError())
->setLabel($this->getFieldName());
}
public function getApplicationTransactionType() {
return PhabricatorTransactions::TYPE_EDGE;
}
public function getApplicationTransactionMetadata() {
return array(
'edge:type' => DifferentialRevisionHasReviewerEdgeType::EDGECONST,
);
}
public function shouldAppearInPropertyView() {
return true;
}
public function renderPropertyViewLabel() {
return $this->getFieldName();
}
public function getRequiredHandlePHIDsForPropertyView() {
return mpull($this->getUserReviewers(), 'getReviewerPHID');
}
public function renderPropertyViewValue(array $handles) {
$reviewers = $this->getUserReviewers();
if (!$reviewers) {
return phutil_tag('em', array(), pht('None'));
}
$view = id(new DifferentialReviewersView())
->setUser($this->getViewer())
->setReviewers($reviewers)
->setHandles($handles);
// TODO: Active diff stuff.
return $view;
}
private function getUserReviewers() {
$reviewers = array();
foreach ($this->getObject()->getReviewerStatus() as $reviewer) {
if ($reviewer->isUser()) {
$reviewers[] = $reviewer;
}
}
return $reviewers;
}
public function shouldAppearInCommitMessage() {
return true;
}
public function shouldAppearInCommitMessageTemplate() {
return true;
}
public function getCommitMessageLabels() {
return array(
'Reviewer',
'Reviewers',
);
}
public function parseValueFromCommitMessage($value) {
return $this->parseObjectList(
$value,
array(
PhabricatorPeopleUserPHIDType::TYPECONST,
PhabricatorProjectProjectPHIDType::TYPECONST,
));
}
public function getRequiredHandlePHIDsForCommitMessage() {
return mpull($this->getValue(), 'getReviewerPHID');
}
public function readValueFromCommitMessage($value) {
$current_reviewers = $this->getObject()->getReviewerStatus();
$current_reviewers = mpull($current_reviewers, null, 'getReviewerPHID');
$reviewers = array();
foreach ($value as $phid) {
$reviewer = idx($current_reviewers, $phid);
if ($reviewer) {
$reviewers[] = $reviewer;
} else {
$data = array(
'status' => DifferentialReviewerStatus::STATUS_ADDED,
);
$reviewers[] = new DifferentialReviewer($phid, $data);
}
}
$this->setValue($reviewers);
return $this;
}
public function renderCommitMessageValue(array $handles) {
return $this->renderObjectList($handles);
}
public function validateCommitMessageValue($value) {
$author_phid = $this->getObject()->getAuthorPHID();
$config_self_accept_key = 'differential.allow-self-accept';
$allow_self_accept = PhabricatorEnv::getEnvConfig($config_self_accept_key);
foreach ($value as $phid) {
if (($phid == $author_phid) && !$allow_self_accept) {
throw new DifferentialFieldValidationException(
pht('The author of a revision can not be a reviewer.'));
}
}
}
public function getRequiredHandlePHIDsForRevisionHeaderWarnings() {
return mpull($this->getValue(), 'getReviewerPHID');
}
public function getWarningsForRevisionHeader(array $handles) {
$revision = $this->getObject();
$status_needs_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW;
if ($revision->getStatus() != $status_needs_review) {
return array();
}
foreach ($this->getValue() as $reviewer) {
if (!$handles[$reviewer->getReviewerPHID()]->isDisabled()) {
return array();
}
}
$warnings = array();
if ($this->getValue()) {
$warnings[] = pht(
'This revision needs review, but all specified reviewers are '.
'disabled or inactive.');
} else {
$warnings[] = pht(
'This revision needs review, but there are no reviewers specified.');
}
return $warnings;
}
}
diff --git a/src/applications/differential/customfield/DifferentialSubscribersField.php b/src/applications/differential/customfield/DifferentialSubscribersField.php
index cb558fd021..dac2040d77 100644
--- a/src/applications/differential/customfield/DifferentialSubscribersField.php
+++ b/src/applications/differential/customfield/DifferentialSubscribersField.php
@@ -1,96 +1,93 @@
getPHID()) {
return array();
}
return PhabricatorSubscribersQuery::loadSubscribersForPHID(
$revision->getPHID());
}
public function getNewValueForApplicationTransactions() {
return array('=' => $this->getValue());
}
public function readValueFromRequest(AphrontRequest $request) {
$this->setValue($request->getArr($this->getFieldKey()));
}
- public function getRequiredHandlePHIDsForEdit() {
- return $this->getValue();
- }
-
public function renderEditControl(array $handles) {
return id(new AphrontFormTokenizerControl())
+ ->setUser($this->getViewer())
->setName($this->getFieldKey())
->setDatasource(new PhabricatorMetaMTAMailableDatasource())
- ->setValue($handles)
+ ->setValue($this->getValue())
->setError($this->getFieldError())
->setLabel($this->getFieldName());
}
public function getApplicationTransactionType() {
return PhabricatorTransactions::TYPE_SUBSCRIBERS;
}
public function shouldAppearInCommitMessage() {
return true;
}
public function shouldAllowEditInCommitMessage() {
return true;
}
public function shouldAppearInCommitMessageTemplate() {
return true;
}
public function getCommitMessageLabels() {
return array(
'CC',
'CCs',
'Subscriber',
'Subscribers',
);
}
public function parseValueFromCommitMessage($value) {
return $this->parseObjectList(
$value,
array(
PhabricatorPeopleUserPHIDType::TYPECONST,
PhabricatorProjectProjectPHIDType::TYPECONST,
PhabricatorMailingListListPHIDType::TYPECONST,
));
}
public function getRequiredHandlePHIDsForCommitMessage() {
return $this->getValue();
}
public function renderCommitMessageValue(array $handles) {
return $this->renderObjectList($handles);
}
}
diff --git a/src/applications/differential/query/DifferentialRevisionSearchEngine.php b/src/applications/differential/query/DifferentialRevisionSearchEngine.php
index 65afcd55a6..22bfe35312 100644
--- a/src/applications/differential/query/DifferentialRevisionSearchEngine.php
+++ b/src/applications/differential/query/DifferentialRevisionSearchEngine.php
@@ -1,325 +1,311 @@
getQueryKey() == 'active') {
return 0xFFFF;
}
return parent::getPageSize($saved);
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter(
'responsiblePHIDs',
$this->readUsersFromRequest($request, 'responsibles'));
$saved->setParameter(
'authorPHIDs',
$this->readUsersFromRequest($request, 'authors'));
$saved->setParameter(
'reviewerPHIDs',
$this->readUsersFromRequest(
$request,
'reviewers',
array(
PhabricatorProjectProjectPHIDType::TYPECONST,
)));
$saved->setParameter(
'subscriberPHIDs',
$this->readUsersFromRequest($request, 'subscribers'));
$saved->setParameter(
'repositoryPHIDs',
$request->getArr('repositories'));
$saved->setParameter(
'draft',
$request->getBool('draft'));
$saved->setParameter(
'order',
$request->getStr('order'));
$saved->setParameter(
'status',
$request->getStr('status'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new DifferentialRevisionQuery())
->needFlags(true)
->needDrafts(true)
->needRelationships(true);
$responsible_phids = $saved->getParameter('responsiblePHIDs', array());
if ($responsible_phids) {
$query->withResponsibleUsers($responsible_phids);
}
$author_phids = $saved->getParameter('authorPHIDs', array());
if ($author_phids) {
$query->withAuthors($author_phids);
}
$reviewer_phids = $saved->getParameter('reviewerPHIDs', array());
if ($reviewer_phids) {
$query->withReviewers($reviewer_phids);
}
$subscriber_phids = $saved->getParameter('subscriberPHIDs', array());
if ($subscriber_phids) {
$query->withCCs($subscriber_phids);
}
$repository_phids = $saved->getParameter('repositoryPHIDs', array());
if ($repository_phids) {
$query->withRepositoryPHIDs($repository_phids);
}
$draft = $saved->getParameter('draft', false);
if ($draft && $this->requireViewer()->isLoggedIn()) {
$query->withDraftRepliesByAuthors(
array($this->requireViewer()->getPHID()));
}
$status = $saved->getParameter('status');
if (idx($this->getStatusOptions(), $status)) {
$query->withStatus($status);
}
$order = $saved->getParameter('order');
if (idx($this->getOrderOptions(), $order)) {
$query->setOrder($order);
} else {
$query->setOrder(DifferentialRevisionQuery::ORDER_CREATED);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved) {
$responsible_phids = $saved->getParameter('responsiblePHIDs', array());
$author_phids = $saved->getParameter('authorPHIDs', array());
$reviewer_phids = $saved->getParameter('reviewerPHIDs', array());
$subscriber_phids = $saved->getParameter('subscriberPHIDs', array());
$repository_phids = $saved->getParameter('repositoryPHIDs', array());
$only_draft = $saved->getParameter('draft', false);
- $all_phids = array_mergev(
- array(
- $responsible_phids,
- $author_phids,
- $reviewer_phids,
- $subscriber_phids,
- $repository_phids,
- ));
-
- $handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($all_phids)
- ->execute();
-
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Responsible Users'))
->setName('responsibles')
->setDatasource(new PhabricatorPeopleDatasource())
- ->setValue(array_select_keys($handles, $responsible_phids)))
- ->appendChild(
+ ->setValue($responsible_phids))
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Authors'))
->setName('authors')
->setDatasource(new PhabricatorPeopleDatasource())
- ->setValue(array_select_keys($handles, $author_phids)))
- ->appendChild(
+ ->setValue($author_phids))
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Reviewers'))
->setName('reviewers')
->setDatasource(new PhabricatorProjectOrUserDatasource())
- ->setValue(array_select_keys($handles, $reviewer_phids)))
- ->appendChild(
+ ->setValue($reviewer_phids))
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Subscribers'))
->setName('subscribers')
->setDatasource(new PhabricatorMetaMTAMailableDatasource())
- ->setValue(array_select_keys($handles, $subscriber_phids)))
- ->appendChild(
+ ->setValue($subscriber_phids))
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Repositories'))
->setName('repositories')
->setDatasource(new DiffusionRepositoryDatasource())
- ->setValue(array_select_keys($handles, $repository_phids)))
+ ->setValue($repository_phids))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Status'))
->setName('status')
->setOptions($this->getStatusOptions())
->setValue($saved->getParameter('status')));
if ($this->requireViewer()->isLoggedIn()) {
$form
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'draft',
1,
pht('Show only revisions with a draft comment.'),
$only_draft));
}
$form
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Order'))
->setName('order')
->setOptions($this->getOrderOptions())
->setValue($saved->getParameter('order')));
}
protected function getURI($path) {
return '/differential/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array();
if ($this->requireViewer()->isLoggedIn()) {
$names['active'] = pht('Active Revisions');
$names['authored'] = pht('Authored');
}
$names['all'] = pht('All Revisions');
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
$viewer = $this->requireViewer();
switch ($query_key) {
case 'active':
return $query
->setParameter('responsiblePHIDs', array($viewer->getPHID()))
->setParameter('status', DifferentialRevisionQuery::STATUS_OPEN);
case 'authored':
return $query
->setParameter('authorPHIDs', array($viewer->getPHID()));
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
private function getStatusOptions() {
return array(
DifferentialRevisionQuery::STATUS_ANY => pht('All'),
DifferentialRevisionQuery::STATUS_OPEN => pht('Open'),
DifferentialRevisionQuery::STATUS_ACCEPTED => pht('Accepted'),
DifferentialRevisionQuery::STATUS_NEEDS_REVIEW => pht('Needs Review'),
DifferentialRevisionQuery::STATUS_NEEDS_REVISION => pht('Needs Revision'),
DifferentialRevisionQuery::STATUS_CLOSED => pht('Closed'),
DifferentialRevisionQuery::STATUS_ABANDONED => pht('Abandoned'),
);
}
private function getOrderOptions() {
return array(
DifferentialRevisionQuery::ORDER_CREATED => pht('Created'),
DifferentialRevisionQuery::ORDER_MODIFIED => pht('Updated'),
);
}
protected function renderResultList(
array $revisions,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($revisions, 'DifferentialRevision');
$viewer = $this->requireViewer();
$template = id(new DifferentialRevisionListView())
->setUser($viewer)
->setNoBox($this->isPanelContext());
$views = array();
if ($query->getQueryKey() == 'active') {
$split = DifferentialRevisionQuery::splitResponsible(
$revisions,
$query->getParameter('responsiblePHIDs'));
list($blocking, $active, $waiting) = $split;
$views[] = id(clone $template)
->setHeader(pht('Blocking Others'))
->setNoDataString(
pht('No revisions are blocked on your action.'))
->setHighlightAge(true)
->setRevisions($blocking)
->setHandles(array());
$views[] = id(clone $template)
->setHeader(pht('Action Required'))
->setNoDataString(
pht('No revisions require your action.'))
->setHighlightAge(true)
->setRevisions($active)
->setHandles(array());
$views[] = id(clone $template)
->setHeader(pht('Waiting on Others'))
->setNoDataString(
pht('You have no revisions waiting on others.'))
->setRevisions($waiting)
->setHandles(array());
} else {
$views[] = id(clone $template)
->setRevisions($revisions)
->setHandles(array());
}
$phids = array_mergev(mpull($views, 'getRequiredHandlePHIDs'));
if ($phids) {
$handles = id(new PhabricatorHandleQuery())
->setViewer($viewer)
->withPHIDs($phids)
->execute();
} else {
$handles = array();
}
foreach ($views as $view) {
$view->setHandles($handles);
}
if (count($views) == 1) {
// Reduce this to a PHUIObjectItemListView so we can get the free
// support from ApplicationSearch.
return head($views)->render();
} else {
return $views;
}
}
}
diff --git a/src/applications/differential/view/DifferentialAddCommentView.php b/src/applications/differential/view/DifferentialAddCommentView.php
index cc84d6034a..e61065c4ec 100644
--- a/src/applications/differential/view/DifferentialAddCommentView.php
+++ b/src/applications/differential/view/DifferentialAddCommentView.php
@@ -1,196 +1,196 @@
errorView = $error_view;
return $this;
}
public function getErrorView() {
return $this->errorView;
}
public function setRevision($revision) {
$this->revision = $revision;
return $this;
}
public function setActions(array $actions) {
$this->actions = $actions;
return $this;
}
public function setActionURI($uri) {
$this->actionURI = $uri;
return $this;
}
public function setDraft(PhabricatorDraft $draft = null) {
$this->draft = $draft;
return $this;
}
public function setReviewers(array $names) {
$this->reviewers = $names;
return $this;
}
public function setCCs(array $names) {
$this->ccs = $names;
return $this;
}
public function render() {
$this->requireResource('differential-revision-add-comment-css');
$revision = $this->revision;
$action = null;
if ($this->draft) {
$action = idx($this->draft->getMetadata(), 'action');
}
$enable_reviewers = DifferentialAction::allowReviewers($action);
$enable_ccs = ($action == DifferentialAction::ACTION_ADDCCS);
$add_reviewers_labels = array(
'add_reviewers' => pht('Add Reviewers'),
'request_review' => pht('Add Reviewers'),
'resign' => pht('Suggest Reviewers'),
);
$form = new AphrontFormView();
$form
->setWorkflow(true)
->setUser($this->user)
->setAction($this->actionURI)
->addHiddenInput('revision_id', $revision->getID())
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Action'))
->setName('action')
->setValue($action)
->setID('comment-action')
->setOptions($this->actions))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel($enable_reviewers ? $add_reviewers_labels[$action] :
$add_reviewers_labels['add_reviewers'])
->setName('reviewers')
->setControlID('add-reviewers')
->setControlStyle($enable_reviewers ? null : 'display: none')
->setID('add-reviewers-tokenizer')
->setDisableBehavior(true))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Add Subscribers'))
->setName('ccs')
->setControlID('add-ccs')
->setControlStyle($enable_ccs ? null : 'display: none')
->setID('add-ccs-tokenizer')
->setDisableBehavior(true))
->appendChild(
id(new PhabricatorRemarkupControl())
->setName('comment')
->setID('comment-content')
->setLabel(pht('Comment'))
->setValue($this->draft ? $this->draft->getDraft() : null)
->setUser($this->user))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Submit')));
$mailable_source = new PhabricatorMetaMTAMailableDatasource();
$reviewer_source = new PhabricatorProjectOrUserDatasource();
Javelin::initBehavior(
'differential-add-reviewers-and-ccs',
array(
'dynamic' => array(
'add-reviewers-tokenizer' => array(
'actions' => array(
'request_review' => 1,
'add_reviewers' => 1,
'resign' => 1,
),
'src' => $reviewer_source->getDatasourceURI(),
'value' => $this->reviewers,
'row' => 'add-reviewers',
'labels' => $add_reviewers_labels,
'placeholder' => $reviewer_source->getPlaceholderText(),
),
'add-ccs-tokenizer' => array(
'actions' => array('add_ccs' => 1),
'src' => $mailable_source->getDatasourceURI(),
'value' => $this->ccs,
'row' => 'add-ccs',
'placeholder' => $mailable_source->getPlaceholderText(),
),
),
'select' => 'comment-action',
));
$diff = $revision->loadActiveDiff();
$rev_id = $revision->getID();
Javelin::initBehavior(
'differential-feedback-preview',
array(
'uri' => '/differential/comment/preview/'.$rev_id.'/',
'preview' => 'comment-preview',
'action' => 'comment-action',
'content' => 'comment-content',
'previewTokenizers' => array(
'reviewers' => 'add-reviewers-tokenizer',
'ccs' => 'add-ccs-tokenizer',
),
'inlineuri' => '/differential/comment/inline/preview/'.$rev_id.'/',
'inline' => 'inline-comment-preview',
));
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$header_text = $is_serious
? pht('Add Comment')
: pht('Leap Into Action');
$header = id(new PHUIHeaderView())
->setHeader($header_text);
$anchor = id(new PhabricatorAnchorView())
->setAnchorName('comment')
->setNavigationMarker(true);
$loading = phutil_tag(
'span',
array('class' => 'aphront-panel-preview-loading-text'),
pht('Loading comment preview...'));
$preview = phutil_tag_div(
'aphront-panel-preview aphront-panel-flush',
array(
phutil_tag('div', array('id' => 'comment-preview'), $loading),
phutil_tag('div', array('id' => 'inline-comment-preview')),
));
$comment_box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($anchor)
->appendChild($form);
if ($this->errorView) {
$comment_box->setInfoView($this->errorView);
}
return array($comment_box, $preview);
}
}
diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php
index 87505bd30e..96d10a77b8 100644
--- a/src/applications/diffusion/controller/DiffusionCommitController.php
+++ b/src/applications/diffusion/controller/DiffusionCommitController.php
@@ -1,1080 +1,1080 @@
getUser();
// This controller doesn't use blob/path stuff, just pass the dictionary
// in directly instead of using the AphrontRequest parsing mechanism.
$data = $request->getURIMap();
$data['user'] = $user;
$drequest = DiffusionRequest::newFromDictionary($data);
$this->diffusionRequest = $drequest;
if ($request->getStr('diff')) {
return $this->buildRawDiffResponse($drequest);
}
$repository = $drequest->getRepository();
$callsign = $repository->getCallsign();
$content = array();
$commit = id(new DiffusionCommitQuery())
->setViewer($request->getUser())
->withRepository($repository)
->withIdentifiers(array($drequest->getCommit()))
->needCommitData(true)
->needAuditRequests(true)
->executeOne();
$crumbs = $this->buildCrumbs(array(
'commit' => true,
));
if (!$commit) {
$exists = $this->callConduitWithDiffusionRequest(
'diffusion.existsquery',
array('commit' => $drequest->getCommit()));
if (!$exists) {
return new Aphront404Response();
}
$error = id(new PHUIInfoView())
->setTitle(pht('Commit Still Parsing'))
->appendChild(
pht(
'Failed to load the commit because the commit has not been '.
'parsed yet.'));
return $this->buildApplicationPage(
array(
$crumbs,
$error,
),
array(
'title' => pht('Commit Still Parsing'),
));
}
$audit_requests = $commit->getAudits();
$this->auditAuthorityPHIDs =
PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($user);
$commit_data = $commit->getCommitData();
$is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
$changesets = null;
if ($is_foreign) {
$subpath = $commit_data->getCommitDetail('svn-subpath');
$error_panel = new PHUIInfoView();
$error_panel->setTitle(pht('Commit Not Tracked'));
$error_panel->setSeverity(PHUIInfoView::SEVERITY_WARNING);
$error_panel->appendChild(
pht("This Diffusion repository is configured to track only one ".
"subdirectory of the entire Subversion repository, and this commit ".
"didn't affect the tracked subdirectory ('%s'), so no ".
"information is available.", $subpath));
$content[] = $error_panel;
} else {
$engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine();
$engine->setConfig('viewer', $user);
require_celerity_resource('phabricator-remarkup-css');
$parents = $this->callConduitWithDiffusionRequest(
'diffusion.commitparentsquery',
array('commit' => $drequest->getCommit()));
if ($parents) {
$parents = id(new DiffusionCommitQuery())
->setViewer($user)
->withRepository($repository)
->withIdentifiers($parents)
->execute();
}
$headsup_view = id(new PHUIHeaderView())
->setHeader(nonempty($commit->getSummary(), pht('Commit Detail')));
$headsup_actions = $this->renderHeadsupActionList($commit, $repository);
$commit_properties = $this->loadCommitProperties(
$commit,
$commit_data,
$parents,
$audit_requests);
$property_list = id(new PHUIPropertyListView())
->setHasKeyboardShortcuts(true)
->setUser($user)
->setObject($commit);
foreach ($commit_properties as $key => $value) {
$property_list->addProperty($key, $value);
}
$message = $commit_data->getCommitMessage();
$revision = $commit->getCommitIdentifier();
$message = $this->linkBugtraq($message);
$message = $engine->markupText($message);
$property_list->invokeWillRenderEvent();
$property_list->setActionList($headsup_actions);
$detail_list = new PHUIPropertyListView();
$detail_list->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);
$detail_list->addTextContent(
phutil_tag(
'div',
array(
'class' => 'diffusion-commit-message phabricator-remarkup',
),
$message));
$object_box = id(new PHUIObjectBoxView())
->setHeader($headsup_view)
->addPropertyList($property_list)
->addPropertyList($detail_list);
$content[] = $object_box;
}
$content[] = $this->buildComments($commit);
$hard_limit = 1000;
if ($commit->isImported()) {
$change_query = DiffusionPathChangeQuery::newFromDiffusionRequest(
$drequest);
$change_query->setLimit($hard_limit + 1);
$changes = $change_query->loadChanges();
} else {
$changes = array();
}
$was_limited = (count($changes) > $hard_limit);
if ($was_limited) {
$changes = array_slice($changes, 0, $hard_limit);
}
$content[] = $this->buildMergesTable($commit);
$highlighted_audits = $commit->getAuthorityAudits(
$user,
$this->auditAuthorityPHIDs);
$owners_paths = array();
if ($highlighted_audits) {
$packages = id(new PhabricatorOwnersPackage())->loadAllWhere(
'phid IN (%Ls)',
mpull($highlighted_audits, 'getAuditorPHID'));
if ($packages) {
$owners_paths = id(new PhabricatorOwnersPath())->loadAllWhere(
'repositoryPHID = %s AND packageID IN (%Ld)',
$repository->getPHID(),
mpull($packages, 'getID'));
}
}
$change_table = new DiffusionCommitChangeTableView();
$change_table->setDiffusionRequest($drequest);
$change_table->setPathChanges($changes);
$change_table->setOwnersPaths($owners_paths);
$count = count($changes);
$bad_commit = null;
if ($count == 0) {
$bad_commit = queryfx_one(
id(new PhabricatorRepository())->establishConnection('r'),
'SELECT * FROM %T WHERE fullCommitName = %s',
PhabricatorRepository::TABLE_BADCOMMIT,
'r'.$callsign.$commit->getCommitIdentifier());
}
if ($bad_commit) {
$content[] = $this->renderStatusMessage(
pht('Bad Commit'),
$bad_commit['description']);
} else if ($is_foreign) {
// Don't render anything else.
} else if (!$commit->isImported()) {
$content[] = $this->renderStatusMessage(
pht('Still Importing...'),
pht(
'This commit is still importing. Changes will be visible once '.
'the import finishes.'));
} else if (!count($changes)) {
$content[] = $this->renderStatusMessage(
pht('Empty Commit'),
pht(
'This commit is empty and does not affect any paths.'));
} else if ($was_limited) {
$content[] = $this->renderStatusMessage(
pht('Enormous Commit'),
pht(
'This commit is enormous, and affects more than %d files. '.
'Changes are not shown.',
$hard_limit));
} else {
// The user has clicked "Show All Changes", and we should show all the
// changes inline even if there are more than the soft limit.
$show_all_details = $request->getBool('show_all');
$change_panel = new PHUIObjectBoxView();
$header = new PHUIHeaderView();
$header->setHeader('Changes ('.number_format($count).')');
$change_panel->setID('toc');
if ($count > self::CHANGES_LIMIT && !$show_all_details) {
$icon = id(new PHUIIconView())
->setIconFont('fa-files-o');
$button = id(new PHUIButtonView())
->setText(pht('Show All Changes'))
->setHref('?show_all=true')
->setTag('a')
->setIcon($icon);
$warning_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setTitle('Very Large Commit')
->appendChild(
pht('This commit is very large. Load each file individually.'));
$change_panel->setInfoView($warning_view);
$header->addActionLink($button);
}
$change_panel->appendChild($change_table);
$change_panel->setHeader($header);
$content[] = $change_panel;
$changesets = DiffusionPathChange::convertToDifferentialChangesets(
$user,
$changes);
$vcs = $repository->getVersionControlSystem();
switch ($vcs) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$vcs_supports_directory_changes = true;
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$vcs_supports_directory_changes = false;
break;
default:
throw new Exception('Unknown VCS.');
}
$references = array();
foreach ($changesets as $key => $changeset) {
$file_type = $changeset->getFileType();
if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
if (!$vcs_supports_directory_changes) {
unset($changesets[$key]);
continue;
}
}
$references[$key] = $drequest->generateURI(
array(
'action' => 'rendering-ref',
'path' => $changeset->getFilename(),
));
}
// TODO: Some parts of the views still rely on properties of the
// DifferentialChangeset. Make the objects ephemeral to make sure we don't
// accidentally save them, and then set their ID to the appropriate ID for
// this application (the path IDs).
$path_ids = array_flip(mpull($changes, 'getPath'));
foreach ($changesets as $changeset) {
$changeset->makeEphemeral();
$changeset->setID($path_ids[$changeset->getFilename()]);
}
if ($count <= self::CHANGES_LIMIT || $show_all_details) {
$visible_changesets = $changesets;
} else {
$visible_changesets = array();
$inlines = PhabricatorAuditInlineComment::loadDraftAndPublishedComments(
$user,
$commit->getPHID());
$path_ids = mpull($inlines, null, 'getPathID');
foreach ($changesets as $key => $changeset) {
if (array_key_exists($changeset->getID(), $path_ids)) {
$visible_changesets[$key] = $changeset;
}
}
}
$change_list_title = DiffusionView::nameCommit(
$repository,
$commit->getCommitIdentifier());
$change_list = new DifferentialChangesetListView();
$change_list->setTitle($change_list_title);
$change_list->setChangesets($changesets);
$change_list->setVisibleChangesets($visible_changesets);
$change_list->setRenderingReferences($references);
$change_list->setRenderURI('/diffusion/'.$callsign.'/diff/');
$change_list->setRepository($repository);
$change_list->setUser($user);
// TODO: Try to setBranch() to something reasonable here?
$change_list->setStandaloneURI(
'/diffusion/'.$callsign.'/diff/');
$change_list->setRawFileURIs(
// TODO: Implement this, somewhat tricky if there's an octopus merge
// or whatever?
null,
'/diffusion/'.$callsign.'/diff/?view=r');
$change_list->setInlineCommentControllerURI(
'/diffusion/inline/edit/'.phutil_escape_uri($commit->getPHID()).'/');
$change_references = array();
foreach ($changesets as $key => $changeset) {
$change_references[$changeset->getID()] = $references[$key];
}
$change_table->setRenderingReferences($change_references);
$content[] = $change_list->render();
}
$content[] = $this->renderAddCommentPanel($commit, $audit_requests);
$commit_id = 'r'.$callsign.$commit->getCommitIdentifier();
$short_name = DiffusionView::nameCommit(
$repository,
$commit->getCommitIdentifier());
$prefs = $user->loadPreferences();
$pref_filetree = PhabricatorUserPreferences::PREFERENCE_DIFF_FILETREE;
$pref_collapse = PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED;
$show_filetree = $prefs->getPreference($pref_filetree);
$collapsed = $prefs->getPreference($pref_collapse);
if ($changesets && $show_filetree) {
$nav = id(new DifferentialChangesetFileTreeSideNavBuilder())
->setTitle($short_name)
->setBaseURI(new PhutilURI('/'.$commit_id))
->build($changesets)
->setCrumbs($crumbs)
->setCollapsed((bool)$collapsed)
->appendChild($content);
$content = $nav;
} else {
$content = array($crumbs, $content);
}
return $this->buildApplicationPage(
$content,
array(
'title' => $commit_id,
'pageObjects' => array($commit->getPHID()),
));
}
private function loadCommitProperties(
PhabricatorRepositoryCommit $commit,
PhabricatorRepositoryCommitData $data,
array $parents,
array $audit_requests) {
assert_instances_of($parents, 'PhabricatorRepositoryCommit');
$viewer = $this->getRequest()->getUser();
$commit_phid = $commit->getPHID();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$edge_query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(array($commit_phid))
->withEdgeTypes(array(
DiffusionCommitHasTaskEdgeType::EDGECONST,
DiffusionCommitHasRevisionEdgeType::EDGECONST,
DiffusionCommitRevertsCommitEdgeType::EDGECONST,
DiffusionCommitRevertedByCommitEdgeType::EDGECONST,
));
$edges = $edge_query->execute();
$task_phids = array_keys(
$edges[$commit_phid][DiffusionCommitHasTaskEdgeType::EDGECONST]);
$revision_phid = key(
$edges[$commit_phid][DiffusionCommitHasRevisionEdgeType::EDGECONST]);
$reverts_phids = array_keys(
$edges[$commit_phid][DiffusionCommitRevertsCommitEdgeType::EDGECONST]);
$reverted_by_phids = array_keys(
$edges[$commit_phid][DiffusionCommitRevertedByCommitEdgeType::EDGECONST]);
$phids = $edge_query->getDestinationPHIDs(array($commit_phid));
if ($data->getCommitDetail('authorPHID')) {
$phids[] = $data->getCommitDetail('authorPHID');
}
if ($data->getCommitDetail('reviewerPHID')) {
$phids[] = $data->getCommitDetail('reviewerPHID');
}
if ($data->getCommitDetail('committerPHID')) {
$phids[] = $data->getCommitDetail('committerPHID');
}
if ($parents) {
foreach ($parents as $parent) {
$phids[] = $parent->getPHID();
}
}
// NOTE: We should never normally have more than a single push log, but
// it can occur naturally if a commit is pushed, then the branch it was
// on is deleted, then the commit is pushed again (or through other similar
// chains of events). This should be rare, but does not indicate a bug
// or data issue.
// NOTE: We never query push logs in SVN because the commiter is always
// the pusher and the commit time is always the push time; the push log
// is redundant and we save a query by skipping it.
$push_logs = array();
if ($repository->isHosted() && !$repository->isSVN()) {
$push_logs = id(new PhabricatorRepositoryPushLogQuery())
->setViewer($viewer)
->withRepositoryPHIDs(array($repository->getPHID()))
->withNewRefs(array($commit->getCommitIdentifier()))
->withRefTypes(array(PhabricatorRepositoryPushLog::REFTYPE_COMMIT))
->execute();
foreach ($push_logs as $log) {
$phids[] = $log->getPusherPHID();
}
}
$handles = array();
if ($phids) {
$handles = $this->loadViewerHandles($phids);
}
$props = array();
if ($commit->getAuditStatus()) {
$status = PhabricatorAuditCommitStatusConstants::getStatusName(
$commit->getAuditStatus());
$tag = id(new PHUITagView())
->setType(PHUITagView::TYPE_STATE)
->setName($status);
switch ($commit->getAuditStatus()) {
case PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT:
$tag->setBackgroundColor(PHUITagView::COLOR_ORANGE);
break;
case PhabricatorAuditCommitStatusConstants::CONCERN_RAISED:
$tag->setBackgroundColor(PHUITagView::COLOR_RED);
break;
case PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED:
$tag->setBackgroundColor(PHUITagView::COLOR_BLUE);
break;
case PhabricatorAuditCommitStatusConstants::FULLY_AUDITED:
$tag->setBackgroundColor(PHUITagView::COLOR_GREEN);
break;
}
$props['Status'] = $tag;
}
if ($audit_requests) {
$user_requests = array();
$other_requests = array();
foreach ($audit_requests as $audit_request) {
if ($audit_request->isUser()) {
$user_requests[] = $audit_request;
} else {
$other_requests[] = $audit_request;
}
}
if ($user_requests) {
$props['Auditors'] = $this->renderAuditStatusView(
$user_requests);
}
if ($other_requests) {
$props['Project/Package Auditors'] = $this->renderAuditStatusView(
$other_requests);
}
}
$author_phid = $data->getCommitDetail('authorPHID');
$author_name = $data->getAuthorName();
if (!$repository->isSVN()) {
$authored_info = id(new PHUIStatusItemView());
// TODO: In Git, a distinct authorship date is available. When present,
// we should show it here.
if ($author_phid) {
$authored_info->setTarget($handles[$author_phid]->renderLink());
} else if (strlen($author_name)) {
$authored_info->setTarget($author_name);
}
$props['Authored'] = id(new PHUIStatusListView())
->addItem($authored_info);
}
$committed_info = id(new PHUIStatusItemView())
->setNote(phabricator_datetime($commit->getEpoch(), $viewer));
$committer_phid = $data->getCommitDetail('committerPHID');
$committer_name = $data->getCommitDetail('committer');
if ($committer_phid) {
$committed_info->setTarget($handles[$committer_phid]->renderLink());
} else if (strlen($committer_name)) {
$committed_info->setTarget($committer_name);
} else if ($author_phid) {
$committed_info->setTarget($handles[$author_phid]->renderLink());
} else if (strlen($author_name)) {
$committed_info->setTarget($author_name);
}
$props['Committed'] = id(new PHUIStatusListView())
->addItem($committed_info);
if ($push_logs) {
$pushed_list = new PHUIStatusListView();
foreach ($push_logs as $push_log) {
$pushed_item = id(new PHUIStatusItemView())
->setTarget($handles[$push_log->getPusherPHID()]->renderLink())
->setNote(phabricator_datetime($push_log->getEpoch(), $viewer));
$pushed_list->addItem($pushed_item);
}
$props['Pushed'] = $pushed_list;
}
$reviewer_phid = $data->getCommitDetail('reviewerPHID');
if ($reviewer_phid) {
$props['Reviewer'] = $handles[$reviewer_phid]->renderLink();
}
if ($revision_phid) {
$props['Differential Revision'] = $handles[$revision_phid]->renderLink();
}
if ($parents) {
$parent_links = array();
foreach ($parents as $parent) {
$parent_links[] = $handles[$parent->getPHID()]->renderLink();
}
$props['Parents'] = phutil_implode_html(" \xC2\xB7 ", $parent_links);
}
$props['Branches'] = phutil_tag(
'span',
array(
'id' => 'commit-branches',
),
pht('Unknown'));
$props['Tags'] = phutil_tag(
'span',
array(
'id' => 'commit-tags',
),
pht('Unknown'));
$callsign = $repository->getCallsign();
$root = '/diffusion/'.$callsign.'/commit/'.$commit->getCommitIdentifier();
Javelin::initBehavior(
'diffusion-commit-branches',
array(
$root.'/branches/' => 'commit-branches',
$root.'/tags/' => 'commit-tags',
));
$refs = $this->buildRefs($drequest);
if ($refs) {
$props['References'] = $refs;
}
if ($reverts_phids) {
$props[pht('Reverts')] = $viewer->renderHandleList($reverts_phids);
}
if ($reverted_by_phids) {
$props[pht('Reverted By')] = $viewer->renderHandleList(
$reverted_by_phids);
}
if ($task_phids) {
$task_list = array();
foreach ($task_phids as $phid) {
$task_list[] = $handles[$phid]->renderLink();
}
$task_list = phutil_implode_html(phutil_tag('br'), $task_list);
$props['Tasks'] = $task_list;
}
return $props;
}
private function buildComments(PhabricatorRepositoryCommit $commit) {
$timeline = $this->buildTransactionTimeline(
$commit,
new PhabricatorAuditTransactionQuery());
$commit->willRenderTimeline($timeline, $this->getRequest());
return $timeline;
}
private function renderAddCommentPanel(
PhabricatorRepositoryCommit $commit,
array $audit_requests) {
assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
$request = $this->getRequest();
$user = $request->getUser();
if (!$user->isLoggedIn()) {
return id(new PhabricatorApplicationTransactionCommentView())
->setUser($user)
->setRequestURI($request->getRequestURI());
}
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$pane_id = celerity_generate_unique_node_id();
Javelin::initBehavior(
'differential-keyboard-navigation',
array(
'haunt' => $pane_id,
));
$draft = id(new PhabricatorDraft())->loadOneWhere(
'authorPHID = %s AND draftKey = %s',
$user->getPHID(),
'diffusion-audit-'.$commit->getID());
if ($draft) {
$draft = $draft->getDraft();
} else {
$draft = null;
}
$actions = $this->getAuditActions($commit, $audit_requests);
$form = id(new AphrontFormView())
->setUser($user)
->setAction('/audit/addcomment/')
->addHiddenInput('commit', $commit->getPHID())
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Action'))
->setName('action')
->setID('audit-action')
->setOptions($actions))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Add Auditors'))
->setName('auditors')
->setControlID('add-auditors')
->setControlStyle('display: none')
->setID('add-auditors-tokenizer')
->setDisableBehavior(true))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Add CCs'))
->setName('ccs')
->setControlID('add-ccs')
->setControlStyle('display: none')
->setID('add-ccs-tokenizer')
->setDisableBehavior(true))
->appendChild(
id(new PhabricatorRemarkupControl())
->setLabel(pht('Comments'))
->setName('content')
->setValue($draft)
->setID('audit-content')
->setUser($user))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Submit')));
$header = new PHUIHeaderView();
$header->setHeader(
$is_serious ? pht('Audit Commit') : pht('Creative Accounting'));
$mailable_source = new PhabricatorMetaMTAMailableDatasource();
$auditor_source = new DiffusionAuditorDatasource();
Javelin::initBehavior(
'differential-add-reviewers-and-ccs',
array(
'dynamic' => array(
'add-auditors-tokenizer' => array(
'actions' => array('add_auditors' => 1),
'src' => $auditor_source->getDatasourceURI(),
'row' => 'add-auditors',
'placeholder' => $auditor_source->getPlaceholderText(),
),
'add-ccs-tokenizer' => array(
'actions' => array('add_ccs' => 1),
'src' => $mailable_source->getDatasourceURI(),
'row' => 'add-ccs',
'placeholder' => $mailable_source->getPlaceholderText(),
),
),
'select' => 'audit-action',
));
Javelin::initBehavior('differential-feedback-preview', array(
'uri' => '/audit/preview/'.$commit->getID().'/',
'preview' => 'audit-preview',
'content' => 'audit-content',
'action' => 'audit-action',
'previewTokenizers' => array(
'auditors' => 'add-auditors-tokenizer',
'ccs' => 'add-ccs-tokenizer',
),
'inline' => 'inline-comment-preview',
'inlineuri' => '/diffusion/inline/preview/'.$commit->getPHID().'/',
));
$loading = phutil_tag_div(
'aphront-panel-preview-loading-text',
pht('Loading preview...'));
$preview_panel = phutil_tag_div(
'aphront-panel-preview aphront-panel-flush',
array(
phutil_tag('div', array('id' => 'audit-preview'), $loading),
phutil_tag('div', array('id' => 'inline-comment-preview')),
));
// TODO: This is pretty awkward, unify the CSS between Diffusion and
// Differential better.
require_celerity_resource('differential-core-view-css');
$anchor = id(new PhabricatorAnchorView())
->setAnchorName('comment')
->setNavigationMarker(true)
->render();
$comment_box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($form);
return phutil_tag(
'div',
array(
'id' => $pane_id,
),
phutil_tag_div(
'differential-add-comment-panel',
array($anchor, $comment_box, $preview_panel)));
}
/**
* Return a map of available audit actions for rendering into a .
* This shows the user valid actions, and does not show nonsense/invalid
* actions (like closing an already-closed commit, or resigning from a commit
* you have no association with).
*/
private function getAuditActions(
PhabricatorRepositoryCommit $commit,
array $audit_requests) {
assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
$user = $this->getRequest()->getUser();
$user_is_author = ($commit->getAuthorPHID() == $user->getPHID());
$user_request = null;
foreach ($audit_requests as $audit_request) {
if ($audit_request->getAuditorPHID() == $user->getPHID()) {
$user_request = $audit_request;
break;
}
}
$actions = array();
$actions[PhabricatorAuditActionConstants::COMMENT] = true;
$actions[PhabricatorAuditActionConstants::ADD_CCS] = true;
$actions[PhabricatorAuditActionConstants::ADD_AUDITORS] = true;
// We allow you to accept your own commits. A use case here is that you
// notice an issue with your own commit and "Raise Concern" as an indicator
// to other auditors that you're on top of the issue, then later resolve it
// and "Accept". You can not accept on behalf of projects or packages,
// however.
$actions[PhabricatorAuditActionConstants::ACCEPT] = true;
$actions[PhabricatorAuditActionConstants::CONCERN] = true;
// To resign, a user must have authority on some request and not be the
// commit's author.
if (!$user_is_author) {
$may_resign = false;
$authority_map = array_fill_keys($this->auditAuthorityPHIDs, true);
foreach ($audit_requests as $request) {
if (empty($authority_map[$request->getAuditorPHID()])) {
continue;
}
$may_resign = true;
break;
}
// If the user has already resigned, don't show "Resign...".
$status_resigned = PhabricatorAuditStatusConstants::RESIGNED;
if ($user_request) {
if ($user_request->getAuditStatus() == $status_resigned) {
$may_resign = false;
}
}
if ($may_resign) {
$actions[PhabricatorAuditActionConstants::RESIGN] = true;
}
}
$status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED;
$concern_raised = ($commit->getAuditStatus() == $status_concern);
$can_close_option = PhabricatorEnv::getEnvConfig(
'audit.can-author-close-audit');
if ($can_close_option && $user_is_author && $concern_raised) {
$actions[PhabricatorAuditActionConstants::CLOSE] = true;
}
foreach ($actions as $constant => $ignored) {
$actions[$constant] =
PhabricatorAuditActionConstants::getActionName($constant);
}
return $actions;
}
private function buildMergesTable(PhabricatorRepositoryCommit $commit) {
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$vcs = $repository->getVersionControlSystem();
switch ($vcs) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
// These aren't supported under SVN.
return null;
}
$limit = 50;
$merges = $this->callConduitWithDiffusionRequest(
'diffusion.mergedcommitsquery',
array(
'commit' => $drequest->getCommit(),
'limit' => $limit + 1,
));
if (!$merges) {
return null;
}
$merges = DiffusionPathChange::newFromConduit($merges);
$caption = null;
if (count($merges) > $limit) {
$merges = array_slice($merges, 0, $limit);
$caption = new PHUIInfoView();
$caption->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
$caption->appendChild(
pht(
'This commit merges a very large number of changes. Only the first '.
'%s are shown.',
new PhutilNumber($limit)));
}
$history_table = new DiffusionHistoryTableView();
$history_table->setUser($this->getRequest()->getUser());
$history_table->setDiffusionRequest($drequest);
$history_table->setHistory($merges);
$history_table->loadRevisions();
$phids = $history_table->getRequiredHandlePHIDs();
$handles = $this->loadViewerHandles($phids);
$history_table->setHandles($handles);
$panel = new PHUIObjectBoxView();
$panel->setHeaderText(pht('Merged Changes'));
$panel->appendChild($history_table);
if ($caption) {
$panel->setInfoView($caption);
}
return $panel;
}
private function renderHeadsupActionList(
PhabricatorRepositoryCommit $commit,
PhabricatorRepository $repository) {
$request = $this->getRequest();
$user = $request->getUser();
$actions = id(new PhabricatorActionListView())
->setUser($user)
->setObject($commit)
->setObjectURI($request->getRequestURI());
$can_edit = PhabricatorPolicyFilter::hasCapability(
$user,
$commit,
PhabricatorPolicyCapability::CAN_EDIT);
$uri = '/diffusion/'.$repository->getCallsign().'/commit/'.
$commit->getCommitIdentifier().'/edit/';
$action = id(new PhabricatorActionView())
->setName(pht('Edit Commit'))
->setHref($uri)
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit);
$actions->addAction($action);
require_celerity_resource('phabricator-object-selector-css');
require_celerity_resource('javelin-behavior-phabricator-object-selector');
$maniphest = 'PhabricatorManiphestApplication';
if (PhabricatorApplication::isClassInstalled($maniphest)) {
$action = id(new PhabricatorActionView())
->setName(pht('Edit Maniphest Tasks'))
->setIcon('fa-anchor')
->setHref('/search/attach/'.$commit->getPHID().'/TASK/edge/')
->setWorkflow(true)
->setDisabled(!$can_edit);
$actions->addAction($action);
}
$action = id(new PhabricatorActionView())
->setName(pht('Download Raw Diff'))
->setHref($request->getRequestURI()->alter('diff', true))
->setIcon('fa-download');
$actions->addAction($action);
return $actions;
}
private function buildRefs(DiffusionRequest $request) {
// this is git-only, so save a conduit round trip and just get out of
// here if the repository isn't git
$type_git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
$repository = $request->getRepository();
if ($repository->getVersionControlSystem() != $type_git) {
return null;
}
$results = $this->callConduitWithDiffusionRequest(
'diffusion.refsquery',
array('commit' => $request->getCommit()));
$ref_links = array();
foreach ($results as $ref_data) {
$ref_links[] = phutil_tag('a',
array('href' => $ref_data['href']),
$ref_data['ref']);
}
return phutil_implode_html(', ', $ref_links);
}
private function buildRawDiffResponse(DiffusionRequest $drequest) {
$raw_diff = $this->callConduitWithDiffusionRequest(
'diffusion.rawdiffquery',
array(
'commit' => $drequest->getCommit(),
'path' => $drequest->getPath(),
));
$file = PhabricatorFile::buildFromFileDataOrHash(
$raw_diff,
array(
'name' => $drequest->getCommit().'.diff',
'ttl' => (60 * 60 * 24),
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
));
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$file->attachToObject($drequest->getRepository()->getPHID());
unset($unguarded);
return $file->getRedirectResponse();
}
private function renderAuditStatusView(array $audit_requests) {
assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
$viewer = $this->getViewer();
$authority_map = array_fill_keys($this->auditAuthorityPHIDs, true);
$view = new PHUIStatusListView();
foreach ($audit_requests as $request) {
$code = $request->getAuditStatus();
$item = new PHUIStatusItemView();
$item->setIcon(
PhabricatorAuditStatusConstants::getStatusIcon($code),
PhabricatorAuditStatusConstants::getStatusColor($code),
PhabricatorAuditStatusConstants::getStatusName($code));
$note = array();
foreach ($request->getAuditReasons() as $reason) {
$note[] = phutil_tag('div', array(), $reason);
}
$item->setNote($note);
$auditor_phid = $request->getAuditorPHID();
$target = $viewer->renderHandle($auditor_phid);
$item->setTarget($target);
if (isset($authority_map[$auditor_phid])) {
$item->setHighlighted(true);
}
$view->addItem($item);
}
return $view;
}
private function linkBugtraq($corpus) {
$url = PhabricatorEnv::getEnvConfig('bugtraq.url');
if (!strlen($url)) {
return $corpus;
}
$regexes = PhabricatorEnv::getEnvConfig('bugtraq.logregex');
if (!$regexes) {
return $corpus;
}
$parser = id(new PhutilBugtraqParser())
->setBugtraqPattern("[[ {$url} | %BUGID% ]]")
->setBugtraqCaptureExpression(array_shift($regexes));
$select = array_shift($regexes);
if ($select) {
$parser->setBugtraqSelectExpression($select);
}
return $parser->processCorpus($corpus);
}
}
diff --git a/src/applications/diffusion/controller/DiffusionCommitEditController.php b/src/applications/diffusion/controller/DiffusionCommitEditController.php
index 84b82fb15e..c9526883e0 100644
--- a/src/applications/diffusion/controller/DiffusionCommitEditController.php
+++ b/src/applications/diffusion/controller/DiffusionCommitEditController.php
@@ -1,131 +1,129 @@
getUser();
$drequest = $this->getDiffusionRequest();
$callsign = $drequest->getRepository()->getCallsign();
$repository = $drequest->getRepository();
$commit = $drequest->loadCommit();
$data = $commit->loadCommitData();
$page_title = pht('Edit Diffusion Commit');
if (!$commit) {
return new Aphront404Response();
}
- $commit_phid = $commit->getPHID();
- $edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
+ $commit_phid = $commit->getPHID();
+ $edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
$current_proj_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$commit_phid,
$edge_type);
- $handles = $this->loadViewerHandles($current_proj_phids);
- $proj_t_values = $handles;
if ($request->isFormPost()) {
$xactions = array();
$proj_phids = $request->getArr('projects');
$xactions[] = id(new PhabricatorAuditTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $edge_type)
->setNewValue(array('=' => array_fuse($proj_phids)));
$editor = id(new PhabricatorAuditEditor())
->setActor($user)
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request);
$xactions = $editor->applyTransactions($commit, $xactions);
return id(new AphrontRedirectResponse())
- ->setURI('/r'.$callsign.$commit->getCommitIdentifier());
+ ->setURI('/r'.$callsign.$commit->getCommitIdentifier());
}
$tokenizer_id = celerity_generate_unique_node_id();
$form = id(new AphrontFormView())
->setUser($user)
->setAction($request->getRequestURI()->getPath())
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Projects'))
->setName('projects')
- ->setValue($proj_t_values)
+ ->setValue($current_proj_phids)
->setID($tokenizer_id)
->setCaption(
javelin_tag(
'a',
array(
'href' => '/project/create/',
'mustcapture' => true,
'sigil' => 'project-create',
),
pht('Create New Project')))
->setDatasource(new PhabricatorProjectDatasource()));
$reason = $data->getCommitDetail('autocloseReason', false);
$reason = PhabricatorRepository::BECAUSE_AUTOCLOSE_FORCED;
if ($reason !== false) {
switch ($reason) {
case PhabricatorRepository::BECAUSE_REPOSITORY_IMPORTING:
$desc = pht('No, Repository Importing');
break;
case PhabricatorRepository::BECAUSE_AUTOCLOSE_DISABLED:
$desc = pht('No, Autoclose Disabled');
break;
case PhabricatorRepository::BECAUSE_NOT_ON_AUTOCLOSE_BRANCH:
$desc = pht('No, Not On Autoclose Branch');
break;
case PhabricatorRepository::BECAUSE_AUTOCLOSE_FORCED:
$desc = pht('Yes, Forced Via bin/repository CLI Tool.');
break;
case null:
$desc = pht('Yes');
break;
default:
$desc = pht('Unknown');
break;
}
$doc_href = PhabricatorEnv::getDoclink('Diffusion User Guide: Autoclose');
$doc_link = phutil_tag(
'a',
array(
'href' => $doc_href,
'target' => '_blank',
),
pht('Learn More'));
$form->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Autoclose?'))
->setValue(array($desc, " \xC2\xB7 ", $doc_link)));
}
Javelin::initBehavior('project-create', array(
'tokenizerID' => $tokenizer_id,
));
$submit = id(new AphrontFormSubmitControl())
->setValue(pht('Save'))
->addCancelButton('/r'.$callsign.$commit->getCommitIdentifier());
$form->appendChild($submit);
$crumbs = $this->buildCrumbs(array(
'commit' => true,
));
$crumbs->addTextCrumb(pht('Edit'));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($page_title)
->setForm($form);
return $this->buildApplicationPage(
array(
$crumbs,
$form_box,
),
array(
'title' => $page_title,
));
}
}
diff --git a/src/applications/diffusion/controller/DiffusionLintController.php b/src/applications/diffusion/controller/DiffusionLintController.php
index 5f51d31fe7..43dfdedaee 100644
--- a/src/applications/diffusion/controller/DiffusionLintController.php
+++ b/src/applications/diffusion/controller/DiffusionLintController.php
@@ -1,342 +1,341 @@
getUser();
$drequest = $this->diffusionRequest;
if ($request->getStr('lint') !== null) {
$controller = new DiffusionLintDetailsController();
$controller->setDiffusionRequest($drequest);
$controller->setCurrentApplication($this->getCurrentApplication());
return $this->delegateToController($controller);
}
$owners = array();
if (!$drequest) {
if (!$request->getArr('owner')) {
$owners = array($user->getPHID());
} else {
$owners = array(head($request->getArr('owner')));
}
- $owner_handles = $this->loadViewerHandles($owners);
}
$codes = $this->loadLintCodes($owners);
if ($codes && !$drequest) {
// TODO: Build some real Query classes for this stuff.
$branches = id(new PhabricatorRepositoryBranch())->loadAllWhere(
'id IN (%Ld)',
array_unique(ipull($codes, 'branchID')));
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($user)
->withIDs(mpull($branches, 'getRepositoryID'))
->execute();
$drequests = array();
foreach ($branches as $id => $branch) {
if (empty($repositories[$branch->getRepositoryID()])) {
continue;
}
$drequests[$id] = DiffusionRequest::newFromDictionary(array(
'user' => $user,
'repository' => $repositories[$branch->getRepositoryID()],
'branch' => $branch->getName(),
));
}
}
$rows = array();
$total = 0;
foreach ($codes as $code) {
if (!$this->diffusionRequest) {
$drequest = idx($drequests, $code['branchID']);
}
if (!$drequest) {
continue;
}
$total += $code['n'];
$href_lint = $drequest->generateURI(array(
'action' => 'lint',
'lint' => $code['code'],
));
$href_browse = $drequest->generateURI(array(
'action' => 'browse',
'lint' => $code['code'],
));
$href_repo = $drequest->generateURI(array('action' => 'lint'));
$rows[] = array(
phutil_tag('a', array('href' => $href_lint), $code['n']),
phutil_tag('a', array('href' => $href_browse), $code['files']),
phutil_tag('a', array('href' => $href_repo), $drequest->getCallsign()),
ArcanistLintSeverity::getStringForSeverity($code['maxSeverity']),
$code['code'],
$code['maxName'],
$code['maxDescription'],
);
}
$table = id(new AphrontTableView($rows))
->setHeaders(array(
pht('Problems'),
pht('Files'),
pht('Repository'),
pht('Severity'),
pht('Code'),
pht('Name'),
pht('Example'),
))
->setColumnVisibility(array(true, true, !$this->diffusionRequest))
->setColumnClasses(array('n', 'n', '', '', 'pri', '', ''));
$content = array();
if (!$this->diffusionRequest) {
$form = id(new AphrontFormView())
->setUser($user)
->setMethod('GET')
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setLimit(1)
->setName('owner')
->setLabel(pht('Owner'))
- ->setValue($owner_handles))
+ ->setValue($owners))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Filter'));
$content[] = id(new AphrontListFilterView())->appendChild($form);
}
$content[] = id(new PHUIObjectBoxView())
->setHeaderText(pht('Lint'))
->appendChild($table);
$title = array('Lint');
$crumbs = $this->buildCrumbs(
array(
'branch' => true,
'path' => true,
'view' => 'lint',
));
if ($this->diffusionRequest) {
$title[] = $drequest->getCallsign();
} else {
$crumbs->addTextCrumb(pht('All Lint'));
}
if ($this->diffusionRequest) {
$branch = $drequest->loadBranch();
$header = id(new PHUIHeaderView())
->setHeader($this->renderPathLinks($drequest, 'lint'))
->setUser($user)
->setPolicyObject($drequest->getRepository());
$actions = $this->buildActionView($drequest);
$properties = $this->buildPropertyView(
$drequest,
$branch,
$total,
$actions);
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
} else {
$object_box = null;
}
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
$content,
),
array(
'title' => $title,
));
}
private function loadLintCodes(array $owner_phids) {
$drequest = $this->diffusionRequest;
$conn = id(new PhabricatorRepository())->establishConnection('r');
$where = array('1 = 1');
if ($drequest) {
$branch = $drequest->loadBranch();
if (!$branch) {
return array();
}
$where[] = qsprintf($conn, 'branchID = %d', $branch->getID());
if ($drequest->getPath() != '') {
$path = '/'.$drequest->getPath();
$is_dir = (substr($path, -1) == '/');
$where[] = ($is_dir
? qsprintf($conn, 'path LIKE %>', $path)
: qsprintf($conn, 'path = %s', $path));
}
}
if ($owner_phids) {
$or = array();
$or[] = qsprintf($conn, 'authorPHID IN (%Ls)', $owner_phids);
$paths = array();
$packages = id(new PhabricatorOwnersOwner())
->loadAllWhere('userPHID IN (%Ls)', $owner_phids);
if ($packages) {
$paths = id(new PhabricatorOwnersPath())->loadAllWhere(
'packageID IN (%Ld)',
mpull($packages, 'getPackageID'));
}
if ($paths) {
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($this->getRequest()->getUser())
->withPHIDs(mpull($paths, 'getRepositoryPHID'))
->execute();
$repositories = mpull($repositories, 'getID', 'getPHID');
$branches = id(new PhabricatorRepositoryBranch())->loadAllWhere(
'repositoryID IN (%Ld)',
$repositories);
$branches = mgroup($branches, 'getRepositoryID');
}
foreach ($paths as $path) {
$branch = idx(
$branches,
idx(
$repositories,
$path->getRepositoryPHID()));
if ($branch) {
$condition = qsprintf(
$conn,
'(branchID IN (%Ld) AND path LIKE %>)',
array_keys($branch),
$path->getPath());
if ($path->getExcluded()) {
$where[] = 'NOT '.$condition;
} else {
$or[] = $condition;
}
}
}
$where[] = '('.implode(' OR ', $or).')';
}
return queryfx_all(
$conn,
'SELECT
branchID,
code,
MAX(severity) AS maxSeverity,
MAX(name) AS maxName,
MAX(description) AS maxDescription,
COUNT(DISTINCT path) AS files,
COUNT(*) AS n
FROM %T
WHERE %Q
GROUP BY branchID, code
ORDER BY n DESC',
PhabricatorRepository::TABLE_LINTMESSAGE,
implode(' AND ', $where));
}
protected function buildActionView(DiffusionRequest $drequest) {
$viewer = $this->getRequest()->getUser();
$view = id(new PhabricatorActionListView())
->setUser($viewer);
$list_uri = $drequest->generateURI(
array(
'action' => 'lint',
'lint' => '',
));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('View As List'))
->setHref($list_uri)
->setIcon('fa-list'));
$history_uri = $drequest->generateURI(
array(
'action' => 'history',
));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('View History'))
->setHref($history_uri)
->setIcon('fa-clock-o'));
$browse_uri = $drequest->generateURI(
array(
'action' => 'browse',
));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Browse Content'))
->setHref($browse_uri)
->setIcon('fa-files-o'));
return $view;
}
protected function buildPropertyView(
DiffusionRequest $drequest,
PhabricatorRepositoryBranch $branch,
$total,
PhabricatorActionListView $actions) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setActionList($actions);
$callsign = $drequest->getRepository()->getCallsign();
$lint_commit = $branch->getLintCommit();
$view->addProperty(
pht('Lint Commit'),
phutil_tag(
'a',
array(
'href' => $drequest->generateURI(
array(
'action' => 'commit',
'commit' => $lint_commit,
)),
),
$drequest->getRepository()->formatCommitName($lint_commit)));
$view->addProperty(
pht('Total Messages'),
pht('%s', new PhutilNumber($total)));
return $view;
}
}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php
index ecaef31297..986f46cec5 100644
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php
@@ -1,171 +1,170 @@
getUser();
$drequest = $this->diffusionRequest;
$repository = $drequest->getRepository();
$repository = id(new PhabricatorRepositoryQuery())
->setViewer($user)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->needProjectPHIDs(true)
->withIDs(array($repository->getID()))
->executeOne();
if (!$repository) {
return new Aphront404Response();
}
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
$v_name = $repository->getName();
$v_desc = $repository->getDetail('description');
$v_clone_name = $repository->getDetail('clone-name');
$e_name = true;
$errors = array();
if ($request->isFormPost()) {
$v_name = $request->getStr('name');
$v_desc = $request->getStr('description');
$v_projects = $request->getArr('projectPHIDs');
if ($repository->isHosted()) {
$v_clone_name = $request->getStr('cloneName');
}
if (!strlen($v_name)) {
$e_name = pht('Required');
$errors[] = pht('Repository name is required.');
} else {
$e_name = null;
}
if (!$errors) {
$xactions = array();
$template = id(new PhabricatorRepositoryTransaction());
$type_name = PhabricatorRepositoryTransaction::TYPE_NAME;
$type_desc = PhabricatorRepositoryTransaction::TYPE_DESCRIPTION;
$type_edge = PhabricatorTransactions::TYPE_EDGE;
$type_clone_name = PhabricatorRepositoryTransaction::TYPE_CLONE_NAME;
$xactions[] = id(clone $template)
->setTransactionType($type_name)
->setNewValue($v_name);
$xactions[] = id(clone $template)
->setTransactionType($type_desc)
->setNewValue($v_desc);
$xactions[] = id(clone $template)
->setTransactionType($type_clone_name)
->setNewValue($v_clone_name);
$xactions[] = id(clone $template)
->setTransactionType($type_edge)
->setMetadataValue(
'edge:type',
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST)
->setNewValue(
array(
'=' => array_fuse($v_projects),
));
id(new PhabricatorRepositoryEditor())
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request)
->setActor($user)
->applyTransactions($repository, $xactions);
return id(new AphrontRedirectResponse())->setURI($edit_uri);
}
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit Basics'));
$title = pht('Edit %s', $repository->getName());
- $project_handles = $this->loadViewerHandles($repository->getProjectPHIDs());
$form = id(new AphrontFormView())
->setUser($user)
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setLabel(pht('Name'))
->setValue($v_name)
->setError($e_name));
if ($repository->isHosted()) {
$form
->appendChild(
id(new AphrontFormTextControl())
->setName('cloneName')
->setLabel(pht('Clone/Checkout As'))
->setValue($v_clone_name)
->setCaption(
pht(
'Optional directory name to use when cloning or checking out '.
'this repository.')));
}
$form
->appendChild(
id(new PhabricatorRemarkupControl())
->setUser($user)
->setName('description')
->setLabel(pht('Description'))
->setValue($v_desc))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorProjectDatasource())
->setName('projectPHIDs')
->setLabel(pht('Projects'))
- ->setValue($project_handles))
+ ->setValue($repository->getProjectPHIDs()))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Save'))
->addCancelButton($edit_uri))
->appendChild(id(new PHUIFormDividerControl()))
->appendRemarkupInstructions($this->getReadmeInstructions());
$object_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setForm($form)
->setFormErrors($errors);
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
),
array(
'title' => $title,
));
}
private function getReadmeInstructions() {
return pht(<<setParameter(
'userPHIDs',
$this->readUsersFromRequest($request, 'users'));
$saved->setParameter(
'projectPHIDs',
array_values($request->getArr('projectPHIDs')));
$saved->setParameter(
'viewerProjects',
$request->getBool('viewerProjects'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorFeedQuery());
$phids = array();
$user_phids = $saved->getParameter('userPHIDs');
if ($user_phids) {
$phids[] = $user_phids;
}
$proj_phids = $saved->getParameter('projectPHIDs');
if ($proj_phids) {
$phids[] = $proj_phids;
}
$viewer_projects = $saved->getParameter('viewerProjects');
if ($viewer_projects) {
$viewer = $this->requireViewer();
$projects = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withMemberPHIDs(array($viewer->getPHID()))
->execute();
$phids[] = mpull($projects, 'getPHID');
}
$phids = array_mergev($phids);
if ($phids) {
$query->setFilterPHIDs($phids);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
$user_phids = $saved_query->getParameter('userPHIDs', array());
$proj_phids = $saved_query->getParameter('projectPHIDs', array());
-
- $phids = array_merge($user_phids, $proj_phids);
- $handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($phids)
- ->execute();
- $user_handles = array_select_keys($handles, $user_phids);
- $proj_handles = array_select_keys($handles, $proj_phids);
-
$viewer_projects = $saved_query->getParameter('viewerProjects');
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('users')
->setLabel(pht('Include Users'))
- ->setValue($user_handles))
- ->appendChild(
+ ->setValue($user_phids))
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorProjectDatasource())
->setName('projectPHIDs')
->setLabel(pht('Include Projects'))
- ->setValue($proj_handles))
+ ->setValue($proj_phids))
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'viewerProjects',
1,
pht('Include stories about projects I am a member of.'),
$viewer_projects));
}
protected function getURI($path) {
return '/feed/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'all' => pht('All Stories'),
);
if ($this->requireViewer()->isLoggedIn()) {
$names['projects'] = pht('Projects');
}
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
case 'projects':
return $query->setParameter('viewerProjects', true);
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $objects,
PhabricatorSavedQuery $query,
array $handles) {
$builder = new PhabricatorFeedBuilder($objects);
if ($this->isPanelContext()) {
$builder->setShowHovercards(false);
} else {
$builder->setShowHovercards(true);
}
$builder->setUser($this->requireViewer());
$view = $builder->buildView();
return phutil_tag_div('phabricator-feed-frame', $view);
}
}
diff --git a/src/applications/files/query/PhabricatorFileSearchEngine.php b/src/applications/files/query/PhabricatorFileSearchEngine.php
index 39e093cb86..86df8b7b6d 100644
--- a/src/applications/files/query/PhabricatorFileSearchEngine.php
+++ b/src/applications/files/query/PhabricatorFileSearchEngine.php
@@ -1,196 +1,191 @@
setParameter(
'authorPHIDs',
$this->readUsersFromRequest($request, 'authors'));
$saved->setParameter('explicit', $request->getBool('explicit'));
$saved->setParameter('createdStart', $request->getStr('createdStart'));
$saved->setParameter('createdEnd', $request->getStr('createdEnd'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorFileQuery());
$author_phids = $saved->getParameter('authorPHIDs', array());
if ($author_phids) {
$query->withAuthorPHIDs($author_phids);
}
if ($saved->getParameter('explicit')) {
$query->showOnlyExplicitUploads(true);
}
$start = $this->parseDateTime($saved->getParameter('createdStart'));
$end = $this->parseDateTime($saved->getParameter('createdEnd'));
if ($start) {
$query->withDateCreatedAfter($start);
}
if ($end) {
$query->withDateCreatedBefore($end);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
- $phids = $saved_query->getParameter('authorPHIDs', array());
- $author_handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($phids)
- ->execute();
-
+ $author_phids = $saved_query->getParameter('authorPHIDs', array());
$explicit = $saved_query->getParameter('explicit');
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('authors')
->setLabel(pht('Authors'))
- ->setValue($author_handles))
+ ->setValue($author_phids))
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'explicit',
1,
pht('Show only manually uploaded files.'),
$explicit));
$this->buildDateRange(
$form,
$saved_query,
'createdStart',
pht('Created After'),
'createdEnd',
pht('Created Before'));
}
protected function getURI($path) {
return '/file/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array();
if ($this->requireViewer()->isLoggedIn()) {
$names['authored'] = pht('Authored');
}
$names += array(
'all' => pht('All'),
);
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
case 'authored':
$author_phid = array($this->requireViewer()->getPHID());
return $query
->setParameter('authorPHIDs', $author_phid)
->setParameter('explicit', true);
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function getRequiredHandlePHIDsForResultList(
array $files,
PhabricatorSavedQuery $query) {
return mpull($files, 'getAuthorPHID');
}
protected function renderResultList(
array $files,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($files, 'PhabricatorFile');
$request = $this->getRequest();
if ($request) {
$highlighted_ids = $request->getStrList('h');
} else {
$highlighted_ids = array();
}
$viewer = $this->requireViewer();
$highlighted_ids = array_fill_keys($highlighted_ids, true);
$list_view = id(new PHUIObjectItemListView())
->setUser($viewer);
foreach ($files as $file) {
$id = $file->getID();
$phid = $file->getPHID();
$name = $file->getName();
$file_uri = $this->getApplicationURI("/info/{$phid}/");
$date_created = phabricator_date($file->getDateCreated(), $viewer);
$author_phid = $file->getAuthorPHID();
if ($author_phid) {
$author_link = $handles[$author_phid]->renderLink();
$uploaded = pht('Uploaded by %s on %s', $author_link, $date_created);
} else {
$uploaded = pht('Uploaded on %s', $date_created);
}
$item = id(new PHUIObjectItemView())
->setObject($file)
->setObjectName("F{$id}")
->setHeader($name)
->setHref($file_uri)
->addAttribute($uploaded)
->addIcon('none', phutil_format_bytes($file->getByteSize()));
$ttl = $file->getTTL();
if ($ttl !== null) {
$item->addIcon('blame', pht('Temporary'));
}
if ($file->getIsPartial()) {
$item->addIcon('fa-exclamation-triangle orange', pht('Partial'));
}
if (isset($highlighted_ids[$id])) {
$item->setEffect('highlighted');
}
$list_view->addItem($item);
}
$list_view->appendChild(id(new PhabricatorGlobalUploadTargetView())
->setUser($viewer));
return $list_view;
}
}
diff --git a/src/applications/fund/controller/FundInitiativeEditController.php b/src/applications/fund/controller/FundInitiativeEditController.php
index 5b3adf1679..b63b4d6d6b 100644
--- a/src/applications/fund/controller/FundInitiativeEditController.php
+++ b/src/applications/fund/controller/FundInitiativeEditController.php
@@ -1,261 +1,255 @@
id = idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
if ($this->id) {
$initiative = id(new FundInitiativeQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$initiative) {
return new Aphront404Response();
}
$is_new = false;
} else {
$initiative = FundInitiative::initializeNewInitiative($viewer);
$is_new = true;
}
if ($is_new) {
$title = pht('Create Initiative');
$button_text = pht('Create Initiative');
$cancel_uri = $this->getApplicationURI();
} else {
$title = pht(
'Edit %s %s',
$initiative->getMonogram(),
$initiative->getName());
$button_text = pht('Save Changes');
$cancel_uri = '/'.$initiative->getMonogram();
}
$e_name = true;
$v_name = $initiative->getName();
$e_merchant = null;
$v_merchant = $initiative->getMerchantPHID();
$v_desc = $initiative->getDescription();
$v_risk = $initiative->getRisks();
if ($is_new) {
$v_projects = array();
} else {
$v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
$initiative->getPHID(),
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
$v_projects = array_reverse($v_projects);
}
$validation_exception = null;
if ($request->isFormPost()) {
$v_name = $request->getStr('name');
$v_desc = $request->getStr('description');
$v_risk = $request->getStr('risks');
$v_view = $request->getStr('viewPolicy');
$v_edit = $request->getStr('editPolicy');
$v_merchant = $request->getStr('merchantPHID');
$v_projects = $request->getArr('projects');
$type_name = FundInitiativeTransaction::TYPE_NAME;
$type_desc = FundInitiativeTransaction::TYPE_DESCRIPTION;
$type_risk = FundInitiativeTransaction::TYPE_RISKS;
$type_merchant = FundInitiativeTransaction::TYPE_MERCHANT;
$type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
$type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
$xactions = array();
$xactions[] = id(new FundInitiativeTransaction())
->setTransactionType($type_name)
->setNewValue($v_name);
$xactions[] = id(new FundInitiativeTransaction())
->setTransactionType($type_desc)
->setNewValue($v_desc);
$xactions[] = id(new FundInitiativeTransaction())
->setTransactionType($type_risk)
->setNewValue($v_risk);
$xactions[] = id(new FundInitiativeTransaction())
->setTransactionType($type_merchant)
->setNewValue($v_merchant);
$xactions[] = id(new FundInitiativeTransaction())
->setTransactionType($type_view)
->setNewValue($v_view);
$xactions[] = id(new FundInitiativeTransaction())
->setTransactionType($type_edit)
->setNewValue($v_edit);
$proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
$xactions[] = id(new FundInitiativeTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $proj_edge_type)
->setNewValue(array('=' => array_fuse($v_projects)));
$editor = id(new FundInitiativeEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true);
try {
$editor->applyTransactions($initiative, $xactions);
return id(new AphrontRedirectResponse())
->setURI('/'.$initiative->getMonogram());
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
$e_name = $ex->getShortMessage($type_name);
$e_merchant = $ex->getShortMessage($type_merchant);
$initiative->setViewPolicy($v_view);
$initiative->setEditPolicy($v_edit);
}
}
$policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->setObject($initiative)
->execute();
- if ($v_projects) {
- $project_handles = $this->loadViewerHandles($v_projects);
- } else {
- $project_handles = array();
- }
-
$merchants = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->execute();
$merchant_options = array();
foreach ($merchants as $merchant) {
$merchant_options[$merchant->getPHID()] = pht(
'Merchant %d %s',
$merchant->getID(),
$merchant->getName());
}
if ($v_merchant && empty($merchant_options[$v_merchant])) {
$merchant_options = array(
$v_merchant => pht('(Restricted Merchant)'),
) + $merchant_options;
}
if (!$merchant_options) {
return $this->newDialog()
->setTitle(pht('No Valid Phortune Merchant Accounts'))
->appendParagraph(
pht(
'You do not control any merchant accounts which can receive '.
'payments from this initiative. When you create an initiative, '.
'you need to specify a merchant account where funds will be paid '.
'to.'))
->appendParagraph(
pht(
'Create a merchant account in the Phortune application before '.
'creating an initiative in Fund.'))
->addCancelButton($this->getApplicationURI());
}
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setLabel(pht('Name'))
->setValue($v_name)
->setError($e_name))
->appendChild(
id(new AphrontFormSelectControl())
->setName('merchantPHID')
->setLabel(pht('Pay To Merchant'))
->setValue($v_merchant)
->setError($e_merchant)
->setOptions($merchant_options))
->appendChild(
id(new PhabricatorRemarkupControl())
->setUser($viewer)
->setName('description')
->setLabel(pht('Description'))
->setValue($v_desc))
->appendChild(
id(new PhabricatorRemarkupControl())
->setUser($viewer)
->setName('risks')
->setLabel(pht('Risks/Challenges'))
->setValue($v_risk))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Projects'))
->setName('projects')
- ->setValue($project_handles)
+ ->setValue($v_projects)
->setDatasource(new PhabricatorProjectDatasource()))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('viewPolicy')
->setPolicyObject($initiative)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicies($policies))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('editPolicy')
->setPolicyObject($initiative)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicies($policies))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue($button_text)
->addCancelButton($cancel_uri));
$crumbs = $this->buildApplicationCrumbs();
if ($is_new) {
$crumbs->addTextCrumb(pht('Create Initiative'));
} else {
$crumbs->addTextCrumb(
$initiative->getMonogram(),
'/'.$initiative->getMonogram());
$crumbs->addTextCrumb(pht('Edit'));
}
$box = id(new PHUIObjectBoxView())
->setValidationException($validation_exception)
->setHeaderText($title)
->appendChild($form);
return $this->buildApplicationPage(
array(
$crumbs,
$box,
),
array(
'title' => $title,
));
}
}
diff --git a/src/applications/fund/query/FundBackerSearchEngine.php b/src/applications/fund/query/FundBackerSearchEngine.php
index 563838a954..d1b7e64a23 100644
--- a/src/applications/fund/query/FundBackerSearchEngine.php
+++ b/src/applications/fund/query/FundBackerSearchEngine.php
@@ -1,160 +1,149 @@
initiative = $initiative;
return $this;
}
public function getInitiative() {
return $this->initiative;
}
public function getResultTypeDescription() {
return pht('Fund Backers');
}
public function getApplicationClassName() {
return 'PhabricatorFundApplication';
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter(
'backerPHIDs',
$this->readUsersFromRequest($request, 'backers'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new FundBackerQuery());
$query->withStatuses(array(FundBacker::STATUS_PURCHASED));
if ($this->getInitiative()) {
$query->withInitiativePHIDs(
array(
$this->getInitiative()->getPHID(),
));
}
$backer_phids = $saved->getParameter('backerPHIDs');
if ($backer_phids) {
$query->withBackerPHIDs($backer_phids);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved) {
-
$backer_phids = $saved->getParameter('backerPHIDs', array());
- $all_phids = array_mergev(
- array(
- $backer_phids,
- ));
-
- $handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($all_phids)
- ->execute();
-
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Backers'))
->setName('backers')
->setDatasource(new PhabricatorPeopleDatasource())
- ->setValue(array_select_keys($handles, $backer_phids)));
+ ->setValue($backer_phids));
}
protected function getURI($path) {
if ($this->getInitiative()) {
return '/fund/backers/'.$this->getInitiative()->getID().'/'.$path;
} else {
return '/fund/backers/'.$path;
}
}
protected function getBuiltinQueryNames() {
$names = array();
$names['all'] = pht('All Backers');
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 getRequiredHandlePHIDsForResultList(
array $backers,
PhabricatorSavedQuery $query) {
$phids = array();
foreach ($backers as $backer) {
$phids[] = $backer->getBackerPHID();
$phids[] = $backer->getInitiativePHID();
}
return $phids;
}
protected function renderResultList(
array $backers,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($backers, 'FundBacker');
$viewer = $this->requireViewer();
$rows = array();
foreach ($backers as $backer) {
$rows[] = array(
$handles[$backer->getInitiativePHID()]->renderLink(),
$handles[$backer->getBackerPHID()]->renderLink(),
$backer->getAmountAsCurrency()->formatForDisplay(),
phabricator_datetime($backer->getDateCreated(), $viewer),
);
}
$table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Initiative'),
pht('Backer'),
pht('Amount'),
pht('Date'),
))
->setColumnClasses(
array(
null,
null,
'wide right',
'right',
));
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Backers'))
->appendChild($table);
}
}
diff --git a/src/applications/fund/query/FundInitiativeSearchEngine.php b/src/applications/fund/query/FundInitiativeSearchEngine.php
index 52c97e7a95..0f02f67ac8 100644
--- a/src/applications/fund/query/FundInitiativeSearchEngine.php
+++ b/src/applications/fund/query/FundInitiativeSearchEngine.php
@@ -1,180 +1,170 @@
setParameter(
'ownerPHIDs',
$this->readUsersFromRequest($request, 'owners'));
$saved->setParameter(
'statuses',
$this->readListFromRequest($request, 'statuses'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new FundInitiativeQuery())
->needProjectPHIDs(true);
$owner_phids = $saved->getParameter('ownerPHIDs');
if ($owner_phids) {
$query->withOwnerPHIDs($owner_phids);
}
$statuses = $saved->getParameter('statuses');
if ($statuses) {
$query->withStatuses($statuses);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved) {
$statuses = $saved->getParameter('statuses', array());
$statuses = array_fuse($statuses);
$owner_phids = $saved->getParameter('ownerPHIDs', array());
- $all_phids = array_mergev(
- array(
- $owner_phids,
- ));
-
- $handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($all_phids)
- ->execute();
-
$status_map = FundInitiative::getStatusNameMap();
$status_control = id(new AphrontFormCheckboxControl())
->setLabel(pht('Statuses'));
foreach ($status_map as $status => $name) {
$status_control->addCheckbox(
'statuses[]',
$status,
$name,
isset($statuses[$status]));
}
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Owners'))
->setName('owners')
->setDatasource(new PhabricatorPeopleDatasource())
- ->setValue(array_select_keys($handles, $owner_phids)))
+ ->setValue($owner_phids))
->appendChild($status_control);
}
protected function getURI($path) {
return '/fund/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array();
$names['open'] = pht('Open Initiatives');
if ($this->requireViewer()->isLoggedIn()) {
$names['owned'] = pht('Owned Initiatives');
}
$names['all'] = pht('All Initiatives');
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
case 'owned':
return $query->setParameter(
'ownerPHIDs',
array(
$this->requireViewer()->getPHID(),
));
case 'open':
return $query->setParameter(
'statuses',
array(
FundInitiative::STATUS_OPEN,
));
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function getRequiredHandlePHIDsForResultList(
array $initiatives,
PhabricatorSavedQuery $query) {
$phids = array();
foreach ($initiatives as $initiative) {
$phids[] = $initiative->getOwnerPHID();
foreach ($initiative->getProjectPHIDs() as $project_phid) {
$phids[] = $project_phid;
}
}
return $phids;
}
protected function renderResultList(
array $initiatives,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($initiatives, 'FundInitiative');
$viewer = $this->requireViewer();
$list = id(new PHUIObjectItemListView());
foreach ($initiatives as $initiative) {
$owner_handle = $handles[$initiative->getOwnerPHID()];
$item = id(new PHUIObjectItemView())
->setObjectName($initiative->getMonogram())
->setHeader($initiative->getName())
->setHref('/'.$initiative->getMonogram())
->addByline(pht('Owner: %s', $owner_handle->renderLink()));
if ($initiative->isClosed()) {
$item->setDisabled(true);
}
$project_handles = array_select_keys(
$handles,
$initiative->getProjectPHIDs());
if ($project_handles) {
$item->addAttribute(
id(new PHUIHandleTagListView())
->setLimit(4)
->setSlim(true)
->setHandles($project_handles));
}
$list->addItem($item);
}
return $list;
}
}
diff --git a/src/applications/harbormaster/controller/HarbormasterStepEditController.php b/src/applications/harbormaster/controller/HarbormasterStepEditController.php
index a7c4624dc9..ef9289a7c8 100644
--- a/src/applications/harbormaster/controller/HarbormasterStepEditController.php
+++ b/src/applications/harbormaster/controller/HarbormasterStepEditController.php
@@ -1,239 +1,234 @@
id = idx($data, 'id');
$this->planID = idx($data, 'plan');
$this->className = idx($data, 'class');
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$this->requireApplicationCapability(
HarbormasterManagePlansCapability::CAPABILITY);
if ($this->id) {
$step = id(new HarbormasterBuildStepQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->executeOne();
if (!$step) {
return new Aphront404Response();
}
$plan = $step->getBuildPlan();
$is_new = false;
} else {
$plan = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer)
->withIDs(array($this->planID))
->executeOne();
if (!$plan) {
return new Aphront404Response();
}
$impl = HarbormasterBuildStepImplementation::getImplementation(
$this->className);
if (!$impl) {
return new Aphront404Response();
}
$step = HarbormasterBuildStep::initializeNewStep($viewer)
->setBuildPlanPHID($plan->getPHID())
->setClassName($this->className);
$is_new = true;
}
$plan_uri = $this->getApplicationURI('plan/'.$plan->getID().'/');
$implementation = $step->getStepImplementation();
$field_list = PhabricatorCustomField::getObjectFields(
$step,
PhabricatorCustomField::ROLE_EDIT);
$field_list
->setViewer($viewer)
->readFieldsFromStorage($step);
$e_name = true;
$v_name = $step->getName();
$e_description = true;
$v_description = $step->getDescription();
$e_depends_on = true;
- $raw_depends_on = $step->getDetail('dependsOn', array());
-
- $v_depends_on = id(new PhabricatorHandleQuery())
- ->setViewer($viewer)
- ->withPHIDs($raw_depends_on)
- ->execute();
+ $v_depends_on = $step->getDetail('dependsOn', array());
$errors = array();
$validation_exception = null;
if ($request->isFormPost()) {
$e_name = null;
$v_name = $request->getStr('name');
$e_description = null;
$v_description = $request->getStr('description');
$e_depends_on = null;
$v_depends_on = $request->getArr('dependsOn');
$xactions = $field_list->buildFieldTransactionsFromRequest(
new HarbormasterBuildStepTransaction(),
$request);
$editor = id(new HarbormasterBuildStepEditor())
->setActor($viewer)
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request);
$name_xaction = id(new HarbormasterBuildStepTransaction())
->setTransactionType(HarbormasterBuildStepTransaction::TYPE_NAME)
->setNewValue($v_name);
array_unshift($xactions, $name_xaction);
$depends_on_xaction = id(new HarbormasterBuildStepTransaction())
->setTransactionType(
HarbormasterBuildStepTransaction::TYPE_DEPENDS_ON)
->setNewValue($v_depends_on);
array_unshift($xactions, $depends_on_xaction);
$description_xaction = id(new HarbormasterBuildStepTransaction())
->setTransactionType(
HarbormasterBuildStepTransaction::TYPE_DESCRIPTION)
->setNewValue($v_description);
array_unshift($xactions, $description_xaction);
if ($is_new) {
// When creating a new step, make sure we have a create transaction
// so we'll apply the transactions even if the step has no
// configurable options.
$create_xaction = id(new HarbormasterBuildStepTransaction())
->setTransactionType(HarbormasterBuildStepTransaction::TYPE_CREATE);
array_unshift($xactions, $create_xaction);
}
try {
$editor->applyTransactions($step, $xactions);
return id(new AphrontRedirectResponse())->setURI($plan_uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
}
}
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setLabel(pht('Name'))
->setError($e_name)
->setValue($v_name));
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(id(new HarbormasterBuildDependencyDatasource())
->setParameters(array(
'planPHID' => $plan->getPHID(),
'stepPHID' => $is_new ? null : $step->getPHID(),
)))
->setName('dependsOn')
->setLabel(pht('Depends On'))
->setError($e_depends_on)
->setValue($v_depends_on));
$field_list->appendFieldsToForm($form);
$form
->appendChild(
id(new PhabricatorRemarkupControl())
->setUser($viewer)
->setName('description')
->setLabel(pht('Description'))
->setError($e_description)
->setValue($v_description));
if ($is_new) {
$submit = pht('Create Build Step');
$header = pht('New Step: %s', $implementation->getName());
$crumb = pht('Add Step');
} else {
$submit = pht('Save Build Step');
$header = pht('Edit Step: %s', $implementation->getName());
$crumb = pht('Edit Step');
}
$form->appendChild(
id(new AphrontFormSubmitControl())
->setValue($submit)
->addCancelButton($plan_uri));
$box = id(new PHUIObjectBoxView())
->setHeaderText($header)
->setValidationException($validation_exception)
->setForm($form);
$crumbs = $this->buildApplicationCrumbs();
$id = $plan->getID();
$crumbs->addTextCrumb(pht('Plan %d', $id), $plan_uri);
$crumbs->addTextCrumb($crumb);
$variables = $this->renderBuildVariablesTable();
if ($is_new) {
$xaction_view = null;
$timeline = null;
} else {
$timeline = $this->buildTransactionTimeline(
$step,
new HarbormasterBuildStepTransactionQuery());
$timeline->setShouldTerminate(true);
}
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$variables,
$timeline,
),
array(
'title' => $implementation->getName(),
));
}
private function renderBuildVariablesTable() {
$viewer = $this->getRequest()->getUser();
$variables = HarbormasterBuild::getAvailableBuildVariables();
ksort($variables);
$rows = array();
$rows[] = pht(
'The following variables can be used in most fields. To reference '.
'a variable, use `${name}` in a field.');
$rows[] = pht('| Variable | Description |');
$rows[] = '|---|---|';
foreach ($variables as $name => $description) {
$rows[] = '| `'.$name.'` | '.$description.' |';
}
$rows = implode("\n", $rows);
$form = id(new AphrontFormView())
->setUser($viewer)
->appendRemarkupInstructions($rows);
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Build Variables'))
->appendChild($form);
}
}
diff --git a/src/applications/herald/query/HeraldRuleSearchEngine.php b/src/applications/herald/query/HeraldRuleSearchEngine.php
index dace06725d..4c7364588b 100644
--- a/src/applications/herald/query/HeraldRuleSearchEngine.php
+++ b/src/applications/herald/query/HeraldRuleSearchEngine.php
@@ -1,218 +1,213 @@
setParameter(
'authorPHIDs',
$this->readUsersFromRequest($request, 'authors'));
$saved->setParameter('contentType', $request->getStr('contentType'));
$saved->setParameter('ruleType', $request->getStr('ruleType'));
$saved->setParameter(
'disabled',
$this->readBoolFromRequest($request, 'disabled'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new HeraldRuleQuery());
$author_phids = $saved->getParameter('authorPHIDs');
if ($author_phids) {
$query->withAuthorPHIDs($author_phids);
}
$content_type = $saved->getParameter('contentType');
$content_type = idx($this->getContentTypeValues(), $content_type);
if ($content_type) {
$query->withContentTypes(array($content_type));
}
$rule_type = $saved->getParameter('ruleType');
$rule_type = idx($this->getRuleTypeValues(), $rule_type);
if ($rule_type) {
$query->withRuleTypes(array($rule_type));
}
$disabled = $saved->getParameter('disabled');
if ($disabled !== null) {
$query->withDisabled($disabled);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
- $phids = $saved_query->getParameter('authorPHIDs', array());
- $author_handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($phids)
- ->execute();
-
+ $author_phids = $saved_query->getParameter('authorPHIDs', array());
$content_type = $saved_query->getParameter('contentType');
$rule_type = $saved_query->getParameter('ruleType');
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('authors')
->setLabel(pht('Authors'))
- ->setValue($author_handles))
+ ->setValue($author_phids))
->appendChild(
id(new AphrontFormSelectControl())
->setName('contentType')
->setLabel(pht('Content Type'))
->setValue($content_type)
->setOptions($this->getContentTypeOptions()))
->appendChild(
id(new AphrontFormSelectControl())
->setName('ruleType')
->setLabel(pht('Rule Type'))
->setValue($rule_type)
->setOptions($this->getRuleTypeOptions()))
->appendChild(
id(new AphrontFormSelectControl())
->setName('disabled')
->setLabel(pht('Rule Status'))
->setValue($this->getBoolFromQuery($saved_query, 'disabled'))
->setOptions(
array(
'' => pht('Show Enabled and Disabled Rules'),
'false' => pht('Show Only Enabled Rules'),
'true' => pht('Show Only Disabled Rules'),
)));
}
protected function getURI($path) {
return '/herald/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array();
if ($this->requireViewer()->isLoggedIn()) {
$names['authored'] = pht('Authored');
}
$names['active'] = pht('Active');
$names['all'] = pht('All');
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
$viewer_phid = $this->requireViewer()->getPHID();
switch ($query_key) {
case 'all':
return $query;
case 'active':
return $query->setParameter('disabled', false);
case 'authored':
return $query
->setParameter('authorPHIDs', array($viewer_phid))
->setParameter('disabled', false);
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
private function getContentTypeOptions() {
return array(
'' => pht('(All Content Types)'),
) + HeraldAdapter::getEnabledAdapterMap($this->requireViewer());
}
private function getContentTypeValues() {
return array_fuse(
array_keys(
HeraldAdapter::getEnabledAdapterMap($this->requireViewer())));
}
private function getRuleTypeOptions() {
return array(
'' => pht('(All Rule Types)'),
) + HeraldRuleTypeConfig::getRuleTypeMap();
}
private function getRuleTypeValues() {
return array_fuse(array_keys(HeraldRuleTypeConfig::getRuleTypeMap()));
}
protected function getRequiredHandlePHIDsForResultList(
array $rules,
PhabricatorSavedQuery $query) {
return mpull($rules, 'getAuthorPHID');
}
protected function renderResultList(
array $rules,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($rules, 'HeraldRule');
$viewer = $this->requireViewer();
$content_type_map = HeraldAdapter::getEnabledAdapterMap($viewer);
$list = id(new PHUIObjectItemListView())
->setUser($viewer);
foreach ($rules as $rule) {
$id = $rule->getID();
$item = id(new PHUIObjectItemView())
->setObjectName("H{$id}")
->setHeader($rule->getName())
->setHref($this->getApplicationURI("rule/{$id}/"));
if ($rule->isPersonalRule()) {
$item->addIcon('fa-user', pht('Personal Rule'));
$item->addByline(
pht(
'Authored by %s',
$handles[$rule->getAuthorPHID()]->renderLink()));
} else {
$item->addIcon('fa-globe', pht('Global Rule'));
}
if ($rule->getIsDisabled()) {
$item->setDisabled(true);
$item->addIcon('fa-lock grey', pht('Disabled'));
}
$item->addAction(
id(new PHUIListItemView())
->setHref($this->getApplicationURI("history/{$id}/"))
->setIcon('fa-file-text-o')
->setName(pht('Edit Log')));
$content_type_name = idx($content_type_map, $rule->getContentType());
$item->addAttribute(pht('Affects: %s', $content_type_name));
$list->addItem($item);
}
return $list;
}
}
diff --git a/src/applications/legalpad/controller/LegalpadDocumentSignatureAddController.php b/src/applications/legalpad/controller/LegalpadDocumentSignatureAddController.php
index 662d608aea..73c9480600 100644
--- a/src/applications/legalpad/controller/LegalpadDocumentSignatureAddController.php
+++ b/src/applications/legalpad/controller/LegalpadDocumentSignatureAddController.php
@@ -1,162 +1,161 @@
getRequest();
$viewer = $request->getUser();
$document = id(new LegalpadDocumentQuery())
->setViewer($viewer)
->needDocumentBodies(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->withIDs(array($request->getURIData('id')))
->executeOne();
if (!$document) {
return new Aphront404Response();
}
$next_uri = $this->getApplicationURI('signatures/'.$document->getID().'/');
$e_name = true;
$e_user = true;
$v_users = array();
$v_notes = '';
$v_name = '';
$errors = array();
$type_individual = LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL;
$is_individual = ($document->getSignatureType() == $type_individual);
if ($request->isFormPost()) {
$v_notes = $request->getStr('notes');
$v_users = array_slice($request->getArr('users'), 0, 1);
$v_name = $request->getStr('name');
if ($is_individual) {
$user_phid = head($v_users);
if (!$user_phid) {
$e_user = pht('Required');
$errors[] = pht('You must choose a user to exempt.');
} else {
$user = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withPHIDs(array($user_phid))
->executeOne();
if (!$user) {
$e_user = pht('Invalid');
$errors[] = pht('That user does not exist.');
} else {
$signature = id(new LegalpadDocumentSignatureQuery())
->setViewer($viewer)
->withDocumentPHIDs(array($document->getPHID()))
->withSignerPHIDs(array($user->getPHID()))
->executeOne();
if ($signature) {
$e_user = pht('Signed');
$errors[] = pht('That user has already signed this document.');
} else {
$e_user = null;
}
}
}
} else {
$company_name = $v_name;
if (!strlen($company_name)) {
$e_name = pht('Required');
$errors[] = pht('You must choose a company to add an exemption for.');
}
}
if (!$errors) {
if ($is_individual) {
$name = $user->getRealName();
$email = $user->loadPrimaryEmailAddress();
$signer_phid = $user->getPHID();
$signature_data = array(
'name' => $name,
'email' => $email,
'notes' => $v_notes,
);
} else {
$name = $company_name;
$email = '';
$signer_phid = null;
$signature_data = array(
'name' => $name,
'email' => null,
'notes' => $v_notes,
'actorPHID' => $viewer->getPHID(),
);
}
$signature = id(new LegalpadDocumentSignature())
->setDocumentPHID($document->getPHID())
->setDocumentVersion($document->getVersions())
->setSignerPHID($signer_phid)
->setSignerName($name)
->setSignerEmail($email)
->setSignatureType($document->getSignatureType())
->setIsExemption(1)
->setExemptionPHID($viewer->getPHID())
->setVerified(LegalpadDocumentSignature::VERIFIED)
->setSignatureData($signature_data);
$signature->save();
return id(new AphrontRedirectResponse())->setURI($next_uri);
}
}
$form = id(new AphrontFormView())
->setUser($viewer);
if ($is_individual) {
- $user_handles = $this->loadViewerHandles($v_users);
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Exempt User'))
->setName('users')
->setLimit(1)
->setDatasource(new PhabricatorPeopleDatasource())
- ->setValue($user_handles)
+ ->setValue($v_users)
->setError($e_user));
} else {
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Company Name'))
->setName('name')
->setError($e_name)
->setValue($v_name));
}
$form
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel(pht('Notes'))
->setName('notes')
->setValue($v_notes));
return $this->newDialog()
->setTitle(pht('Add Signature Exemption'))
->setWidth(AphrontDialogView::WIDTH_FORM)
->setErrors($errors)
->appendParagraph(
pht(
'You can record a signature exemption if a user has signed an '.
'equivalent document. Other applications will behave as through the '.
'user has signed this document.'))
->appendParagraph(null)
->appendChild($form->buildLayoutView())
->addSubmitButton(pht('Add Exemption'))
->addCancelButton($next_uri);
}
}
diff --git a/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php b/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php
index 104da068a8..84abe3fdd1 100644
--- a/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php
+++ b/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php
@@ -1,221 +1,216 @@
setParameter(
'creatorPHIDs',
$this->readUsersFromRequest($request, 'creators'));
$saved->setParameter(
'contributorPHIDs',
$this->readUsersFromRequest($request, 'contributors'));
$saved->setParameter(
'withViewerSignature',
$request->getBool('withViewerSignature'));
$saved->setParameter('createdStart', $request->getStr('createdStart'));
$saved->setParameter('createdEnd', $request->getStr('createdEnd'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new LegalpadDocumentQuery())
->needViewerSignatures(true);
$creator_phids = $saved->getParameter('creatorPHIDs', array());
if ($creator_phids) {
$query->withCreatorPHIDs($creator_phids);
}
$contributor_phids = $saved->getParameter('contributorPHIDs', array());
if ($contributor_phids) {
$query->withContributorPHIDs($contributor_phids);
}
if ($saved->getParameter('withViewerSignature')) {
$viewer_phid = $this->requireViewer()->getPHID();
if ($viewer_phid) {
$query->withSignerPHIDs(array($viewer_phid));
}
}
$start = $this->parseDateTime($saved->getParameter('createdStart'));
$end = $this->parseDateTime($saved->getParameter('createdEnd'));
if ($start) {
$query->withDateCreatedAfter($start);
}
if ($end) {
$query->withDateCreatedBefore($end);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
$creator_phids = $saved_query->getParameter('creatorPHIDs', array());
$contributor_phids = $saved_query->getParameter(
'contributorPHIDs', array());
- $phids = array_merge($creator_phids, $contributor_phids);
- $handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($phids)
- ->execute();
$viewer_signature = $saved_query->getParameter('withViewerSignature');
if (!$this->requireViewer()->getPHID()) {
$viewer_signature = false;
}
$form
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'withViewerSignature',
1,
pht('Show only documents I have signed.'),
$viewer_signature)
->setDisabled(!$this->requireViewer()->getPHID()))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('creators')
->setLabel(pht('Creators'))
- ->setValue(array_select_keys($handles, $creator_phids)))
- ->appendChild(
+ ->setValue($creator_phids))
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('contributors')
->setLabel(pht('Contributors'))
- ->setValue(array_select_keys($handles, $contributor_phids)));
+ ->setValue($contributor_phids));
$this->buildDateRange(
$form,
$saved_query,
'createdStart',
pht('Created After'),
'createdEnd',
pht('Created Before'));
}
protected function getURI($path) {
return '/legalpad/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array();
if ($this->requireViewer()->isLoggedIn()) {
$names['signed'] = pht('Signed Documents');
}
$names['all'] = pht('All Documents');
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'signed':
return $query
->setParameter('withViewerSignature', true);
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function getRequiredHandlePHIDsForResultList(
array $documents,
PhabricatorSavedQuery $query) {
return array();
}
protected function renderResultList(
array $documents,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($documents, 'LegalpadDocument');
$viewer = $this->requireViewer();
$list = new PHUIObjectItemListView();
$list->setUser($viewer);
foreach ($documents as $document) {
$last_updated = phabricator_date($document->getDateModified(), $viewer);
$title = $document->getTitle();
$item = id(new PHUIObjectItemView())
->setObjectName($document->getMonogram())
->setHeader($title)
->setHref('/'.$document->getMonogram())
->setObject($document);
$no_signatures = LegalpadDocument::SIGNATURE_TYPE_NONE;
if ($document->getSignatureType() == $no_signatures) {
$item->addIcon('none', pht('Not Signable'));
} else {
$type_name = $document->getSignatureTypeName();
$type_icon = $document->getSignatureTypeIcon();
$item->addIcon($type_icon, $type_name);
if ($viewer->getPHID()) {
$signature = $document->getUserSignature($viewer->getPHID());
} else {
$signature = null;
}
if ($signature) {
$item->addAttribute(
array(
id(new PHUIIconView())->setIconFont('fa-check-square-o', 'green'),
' ',
pht(
'Signed on %s',
phabricator_date($signature->getDateCreated(), $viewer)),
));
} else {
$item->addAttribute(
array(
id(new PHUIIconView())->setIconFont('fa-square-o', 'grey'),
' ',
pht('Not Signed'),
));
}
}
$item->addIcon(
'fa-pencil grey',
pht('Version %d (%s)', $document->getVersions(), $last_updated));
$list->addItem($item);
}
return $list;
}
}
diff --git a/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php b/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php
index c878d92ce4..48cc1b94b6 100644
--- a/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php
+++ b/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php
@@ -1,327 +1,321 @@
document = $document;
return $this;
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter(
'signerPHIDs',
$this->readUsersFromRequest($request, 'signers'));
$saved->setParameter(
'documentPHIDs',
$this->readPHIDsFromRequest(
$request,
'documents',
array(
PhabricatorLegalpadDocumentPHIDType::TYPECONST,
)));
$saved->setParameter('nameContains', $request->getStr('nameContains'));
$saved->setParameter('emailContains', $request->getStr('emailContains'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new LegalpadDocumentSignatureQuery());
$signer_phids = $saved->getParameter('signerPHIDs', array());
if ($signer_phids) {
$query->withSignerPHIDs($signer_phids);
}
if ($this->document) {
$query->withDocumentPHIDs(array($this->document->getPHID()));
} else {
$document_phids = $saved->getParameter('documentPHIDs', array());
if ($document_phids) {
$query->withDocumentPHIDs($document_phids);
}
}
$name_contains = $saved->getParameter('nameContains');
if (strlen($name_contains)) {
$query->withNameContains($name_contains);
}
$email_contains = $saved->getParameter('emailContains');
if (strlen($email_contains)) {
$query->withEmailContains($email_contains);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
$document_phids = $saved_query->getParameter('documentPHIDs', array());
$signer_phids = $saved_query->getParameter('signerPHIDs', array());
- $phids = array_merge($document_phids, $signer_phids);
- $handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($phids)
- ->execute();
-
if (!$this->document) {
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new LegalpadDocumentDatasource())
->setName('documents')
->setLabel(pht('Documents'))
- ->setValue(array_select_keys($handles, $document_phids)));
+ ->setValue($document_phids));
}
$name_contains = $saved_query->getParameter('nameContains', '');
$email_contains = $saved_query->getParameter('emailContains', '');
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('signers')
->setLabel(pht('Signers'))
- ->setValue(array_select_keys($handles, $signer_phids)))
+ ->setValue($signer_phids))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Name Contains'))
->setName('nameContains')
->setValue($name_contains))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Email Contains'))
->setName('emailContains')
->setValue($email_contains));
}
protected function getURI($path) {
if ($this->document) {
return '/legalpad/signatures/'.$this->document->getID().'/'.$path;
} else {
return '/legalpad/signatures/'.$path;
}
}
protected function getBuiltinQueryNames() {
$names = array(
'all' => pht('All Signatures'),
);
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 getRequiredHandlePHIDsForResultList(
array $signatures,
PhabricatorSavedQuery $query) {
return array_merge(
mpull($signatures, 'getSignerPHID'),
mpull($signatures, 'getDocumentPHID'));
}
protected function renderResultList(
array $signatures,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($signatures, 'LegalpadDocumentSignature');
$viewer = $this->requireViewer();
Javelin::initBehavior('phabricator-tooltips');
$sig_good = $this->renderIcon(
'fa-check',
null,
pht('Verified, Current'));
$sig_corp = $this->renderIcon(
'fa-building-o',
null,
pht('Verified, Corporate'));
$sig_old = $this->renderIcon(
'fa-clock-o',
'orange',
pht('Signed Older Version'));
$sig_unverified = $this->renderIcon(
'fa-envelope',
'red',
pht('Unverified Email'));
$sig_exemption = $this->renderIcon(
'fa-asterisk',
'indigo',
pht('Exemption'));
id(new PHUIIconView())
->setIconFont('fa-envelope', 'red')
->addSigil('has-tooltip')
->setMetadata(array('tip' => pht('Unverified Email')));
$type_corporate = LegalpadDocument::SIGNATURE_TYPE_CORPORATION;
$rows = array();
foreach ($signatures as $signature) {
$name = $signature->getSignerName();
$email = $signature->getSignerEmail();
$document = $signature->getDocument();
if ($signature->getIsExemption()) {
$sig_icon = $sig_exemption;
} else if (!$signature->isVerified()) {
$sig_icon = $sig_unverified;
} else if ($signature->getDocumentVersion() != $document->getVersions()) {
$sig_icon = $sig_old;
} else if ($signature->getSignatureType() == $type_corporate) {
$sig_icon = $sig_corp;
} else {
$sig_icon = $sig_good;
}
$signature_href = $this->getApplicationURI(
'signature/'.$signature->getID().'/');
$sig_icon = javelin_tag(
'a',
array(
'href' => $signature_href,
'sigil' => 'workflow',
),
$sig_icon);
$signer_phid = $signature->getSignerPHID();
$rows[] = array(
$sig_icon,
$handles[$document->getPHID()]->renderLink(),
$signer_phid
? $handles[$signer_phid]->renderLink()
: null,
$name,
phutil_tag(
'a',
array(
'href' => 'mailto:'.$email,
),
$email),
phabricator_datetime($signature->getDateCreated(), $viewer),
);
}
$table = id(new AphrontTableView($rows))
->setNoDataString(pht('No signatures match the query.'))
->setHeaders(
array(
'',
pht('Document'),
pht('Account'),
pht('Name'),
pht('Email'),
pht('Signed'),
))
->setColumnVisibility(
array(
true,
// Only show the "Document" column if we aren't scoped to a
// particular document.
!$this->document,
))
->setColumnClasses(
array(
'',
'',
'',
'',
'wide',
'right',
));
$header = id(new PHUIHeaderView())
->setHeader(pht('Signatures'));
if ($this->document) {
$document_id = $this->document->getID();
$header->addActionLink(
id(new PHUIButtonView())
->setText(pht('Add Signature Exemption'))
->setTag('a')
->setHref($this->getApplicationURI('addsignature/'.$document_id.'/'))
->setWorkflow(true)
->setIcon(id(new PHUIIconView())->setIconFont('fa-pencil')));
}
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($table);
if (!$this->document) {
$policy_notice = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->setErrors(
array(
pht(
'NOTE: You can only see your own signatures and signatures on '.
'documents you have permission to edit.'),
));
$box->setInfoView($policy_notice);
}
return $box;
}
private function renderIcon($icon, $color, $title) {
Javelin::initBehavior('phabricator-tooltips');
return array(
id(new PHUIIconView())
->setIconFont($icon, $color)
->addSigil('has-tooltip')
->setMetadata(array('tip' => $title)),
javelin_tag(
'span',
array(
'aural' => true,
),
$title),
);
}
}
diff --git a/src/applications/macro/controller/PhabricatorMacroMemeDialogController.php b/src/applications/macro/controller/PhabricatorMacroMemeDialogController.php
index 65ffd39991..25a223f5ac 100644
--- a/src/applications/macro/controller/PhabricatorMacroMemeDialogController.php
+++ b/src/applications/macro/controller/PhabricatorMacroMemeDialogController.php
@@ -1,76 +1,77 @@
getRequest();
$user = $request->getUser();
$phid = head($request->getArr('macro'));
$above = $request->getStr('above');
$below = $request->getStr('below');
$e_macro = true;
$errors = array();
if ($request->isDialogFormPost()) {
if (!$phid) {
$e_macro = pht('Required');
$errors[] = pht('Macro name is required.');
} else {
$macro = id(new PhabricatorMacroQuery())
->setViewer($user)
->withPHIDs(array($phid))
->executeOne();
if (!$macro) {
$e_macro = pht('Invalid');
$errors[] = pht('No such macro.');
}
}
if (!$errors) {
$options = new PhutilSimpleOptions();
$data = array(
'src' => $macro->getName(),
'above' => $above,
'below' => $below,
);
$string = $options->unparse($data, $escape = '}');
$result = array(
'text' => "{meme, {$string}}",
);
return id(new AphrontAjaxResponse())->setContent($result);
}
}
- $view = id(new PHUIFormLayoutView())
- ->appendChild(
+ $view = id(new AphrontFormView())
+ ->setUser($user)
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Macro'))
->setName('macro')
->setLimit(1)
->setDatasource(new PhabricatorMacroDatasource())
->setError($e_macro))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Above'))
->setName('above')
->setValue($above))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Below'))
->setName('below')
->setValue($below));
$dialog = id(new AphrontDialogView())
->setUser($user)
->setTitle(pht('Create Meme'))
- ->appendChild($view)
+ ->appendForm($view)
->addCancelButton('/')
- ->addSubmitButton(pht('rofllolo!!~'));
+ ->addSubmitButton(pht('Llama Diorama'));
return id(new AphrontDialogResponse())->setDialog($dialog);
}
}
diff --git a/src/applications/macro/query/PhabricatorMacroSearchEngine.php b/src/applications/macro/query/PhabricatorMacroSearchEngine.php
index 8e00c45f17..9207685dc5 100644
--- a/src/applications/macro/query/PhabricatorMacroSearchEngine.php
+++ b/src/applications/macro/query/PhabricatorMacroSearchEngine.php
@@ -1,221 +1,216 @@
setParameter(
'authorPHIDs',
$this->readUsersFromRequest($request, 'authors'));
$saved->setParameter('status', $request->getStr('status'));
$saved->setParameter('names', $request->getStrList('names'));
$saved->setParameter('nameLike', $request->getStr('nameLike'));
$saved->setParameter('createdStart', $request->getStr('createdStart'));
$saved->setParameter('createdEnd', $request->getStr('createdEnd'));
$saved->setParameter('flagColor', $request->getStr('flagColor', '-1'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorMacroQuery())
->needFiles(true)
->withIDs($saved->getParameter('ids', array()))
->withPHIDs($saved->getParameter('phids', array()))
->withAuthorPHIDs($saved->getParameter('authorPHIDs', array()));
$status = $saved->getParameter('status');
$options = PhabricatorMacroQuery::getStatusOptions();
if (empty($options[$status])) {
$status = head_key($options);
}
$query->withStatus($status);
$names = $saved->getParameter('names', array());
if ($names) {
$query->withNames($names);
}
$like = $saved->getParameter('nameLike');
if (strlen($like)) {
$query->withNameLike($like);
}
$start = $this->parseDateTime($saved->getParameter('createdStart'));
$end = $this->parseDateTime($saved->getParameter('createdEnd'));
if ($start) {
$query->withDateCreatedAfter($start);
}
if ($end) {
$query->withDateCreatedBefore($end);
}
$color = $saved->getParameter('flagColor');
if (strlen($color)) {
$query->withFlagColor($color);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
- $phids = $saved_query->getParameter('authorPHIDs', array());
- $author_handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($phids)
- ->execute();
-
+ $author_phids = $saved_query->getParameter('authorPHIDs', array());
$status = $saved_query->getParameter('status');
$names = implode(', ', $saved_query->getParameter('names', array()));
$like = $saved_query->getParameter('nameLike');
$color = $saved_query->getParameter('flagColor', '-1');
$form
->appendChild(
id(new AphrontFormSelectControl())
->setName('status')
->setLabel(pht('Status'))
->setOptions(PhabricatorMacroQuery::getStatusOptions())
->setValue($status))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('authors')
->setLabel(pht('Authors'))
- ->setValue($author_handles))
+ ->setValue($author_phids))
->appendChild(
id(new AphrontFormTextControl())
->setName('nameLike')
->setLabel(pht('Name Contains'))
->setValue($like))
->appendChild(
id(new AphrontFormTextControl())
->setName('names')
->setLabel(pht('Exact Names'))
->setValue($names))
->appendChild(
id(new AphrontFormSelectControl())
->setName('flagColor')
->setLabel(pht('Marked with Flag'))
->setOptions(PhabricatorMacroQuery::getFlagColorsOptions())
->setValue($color));
$this->buildDateRange(
$form,
$saved_query,
'createdStart',
pht('Created After'),
'createdEnd',
pht('Created Before'));
}
protected function getURI($path) {
return '/macro/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'active' => pht('Active'),
'all' => pht('All'),
);
if ($this->requireViewer()->isLoggedIn()) {
$names['authored'] = pht('Authored');
}
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'active':
return $query;
case 'all':
return $query->setParameter(
'status',
PhabricatorMacroQuery::STATUS_ANY);
case 'authored':
return $query->setParameter(
'authorPHIDs',
array($this->requireViewer()->getPHID()));
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function getRequiredHandlePHIDsForResultList(
array $macros,
PhabricatorSavedQuery $query) {
return mpull($macros, 'getAuthorPHID');
}
protected function renderResultList(
array $macros,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($macros, 'PhabricatorFileImageMacro');
$viewer = $this->requireViewer();
$pinboard = new PHUIPinboardView();
foreach ($macros as $macro) {
$file = $macro->getFile();
$item = new PHUIPinboardItemView();
if ($file) {
$item->setImageURI($file->getThumb280x210URI());
$item->setImageSize(280, 210);
}
if ($macro->getDateCreated()) {
$datetime = phabricator_date($macro->getDateCreated(), $viewer);
$item->appendChild(
phutil_tag(
'div',
array(),
pht('Created on %s', $datetime)));
} else {
// Very old macros don't have a creation date. Rendering something
// keeps all the pins at the same height and avoids flow issues.
$item->appendChild(
phutil_tag(
'div',
array(),
pht('Created in ages long past')));
}
if ($macro->getAuthorPHID()) {
$author_handle = $handles[$macro->getAuthorPHID()];
$item->appendChild(
pht('Created by %s', $author_handle->renderLink()));
}
$item->setURI($this->getApplicationURI('/view/'.$macro->getID().'/'));
$item->setDisabled($macro->getisDisabled());
$item->setHeader($macro->getName());
$pinboard->addItem($item);
}
return $pinboard;
}
}
diff --git a/src/applications/maniphest/controller/ManiphestReportController.php b/src/applications/maniphest/controller/ManiphestReportController.php
index 2fd0d90873..f0b1c37110 100644
--- a/src/applications/maniphest/controller/ManiphestReportController.php
+++ b/src/applications/maniphest/controller/ManiphestReportController.php
@@ -1,791 +1,792 @@
view = idx($data, 'view');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
if ($request->isFormPost()) {
$uri = $request->getRequestURI();
$project = head($request->getArr('set_project'));
$project = nonempty($project, null);
$uri = $uri->alter('project', $project);
$window = $request->getStr('set_window');
$uri = $uri->alter('window', $window);
return id(new AphrontRedirectResponse())->setURI($uri);
}
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI('/maniphest/report/'));
$nav->addLabel(pht('Open Tasks'));
$nav->addFilter('user', pht('By User'));
$nav->addFilter('project', pht('By Project'));
$nav->addLabel(pht('Burnup'));
$nav->addFilter('burn', pht('Burnup Rate'));
$this->view = $nav->selectFilter($this->view, 'user');
require_celerity_resource('maniphest-report-css');
switch ($this->view) {
case 'burn':
$core = $this->renderBurn();
break;
case 'user':
case 'project':
$core = $this->renderOpenTasks();
break;
default:
return new Aphront404Response();
}
$nav->appendChild($core);
$nav->setCrumbs(
$this->buildApplicationCrumbs()
->setBorder(true)
->addTextCrumb(pht('Reports')));
return $this->buildApplicationPage(
$nav,
array(
'title' => pht('Maniphest Reports'),
'device' => false,
));
}
public function renderBurn() {
$request = $this->getRequest();
$user = $request->getUser();
$handle = null;
$project_phid = $request->getStr('project');
if ($project_phid) {
$phids = array($project_phid);
$handles = $this->loadViewerHandles($phids);
$handle = $handles[$project_phid];
}
$table = new ManiphestTransaction();
$conn = $table->establishConnection('r');
$joins = '';
if ($project_phid) {
$joins = qsprintf(
$conn,
'JOIN %T t ON x.objectPHID = t.phid
JOIN %T p ON p.src = t.phid AND p.type = %d AND p.dst = %s',
id(new ManiphestTask())->getTableName(),
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
$project_phid);
}
$data = queryfx_all(
$conn,
'SELECT x.oldValue, x.newValue, x.dateCreated FROM %T x %Q
WHERE transactionType = %s
ORDER BY x.dateCreated ASC',
$table->getTableName(),
$joins,
ManiphestTransaction::TYPE_STATUS);
$stats = array();
$day_buckets = array();
$open_tasks = array();
foreach ($data as $key => $row) {
// NOTE: Hack to avoid json_decode().
$oldv = trim($row['oldValue'], '"');
$newv = trim($row['newValue'], '"');
if ($oldv == 'null') {
$old_is_open = false;
} else {
$old_is_open = ManiphestTaskStatus::isOpenStatus($oldv);
}
$new_is_open = ManiphestTaskStatus::isOpenStatus($newv);
$is_open = ($new_is_open && !$old_is_open);
$is_close = ($old_is_open && !$new_is_open);
$data[$key]['_is_open'] = $is_open;
$data[$key]['_is_close'] = $is_close;
if (!$is_open && !$is_close) {
// This is either some kind of bogus event, or a resolution change
// (e.g., resolved -> invalid). Just skip it.
continue;
}
$day_bucket = phabricator_format_local_time(
$row['dateCreated'],
$user,
'Yz');
$day_buckets[$day_bucket] = $row['dateCreated'];
if (empty($stats[$day_bucket])) {
$stats[$day_bucket] = array(
'open' => 0,
'close' => 0,
);
}
$stats[$day_bucket][$is_close ? 'close' : 'open']++;
}
$template = array(
'open' => 0,
'close' => 0,
);
$rows = array();
$rowc = array();
$last_month = null;
$last_month_epoch = null;
$last_week = null;
$last_week_epoch = null;
$week = null;
$month = null;
$last = last_key($stats) - 1;
$period = $template;
foreach ($stats as $bucket => $info) {
$epoch = $day_buckets[$bucket];
$week_bucket = phabricator_format_local_time(
$epoch,
$user,
'YW');
if ($week_bucket != $last_week) {
if ($week) {
$rows[] = $this->formatBurnRow(
'Week of '.phabricator_date($last_week_epoch, $user),
$week);
$rowc[] = 'week';
}
$week = $template;
$last_week = $week_bucket;
$last_week_epoch = $epoch;
}
$month_bucket = phabricator_format_local_time(
$epoch,
$user,
'Ym');
if ($month_bucket != $last_month) {
if ($month) {
$rows[] = $this->formatBurnRow(
phabricator_format_local_time($last_month_epoch, $user, 'F, Y'),
$month);
$rowc[] = 'month';
}
$month = $template;
$last_month = $month_bucket;
$last_month_epoch = $epoch;
}
$rows[] = $this->formatBurnRow(phabricator_date($epoch, $user), $info);
$rowc[] = null;
$week['open'] += $info['open'];
$week['close'] += $info['close'];
$month['open'] += $info['open'];
$month['close'] += $info['close'];
$period['open'] += $info['open'];
$period['close'] += $info['close'];
}
if ($week) {
$rows[] = $this->formatBurnRow(
pht('Week To Date'),
$week);
$rowc[] = 'week';
}
if ($month) {
$rows[] = $this->formatBurnRow(
pht('Month To Date'),
$month);
$rowc[] = 'month';
}
$rows[] = $this->formatBurnRow(
pht('All Time'),
$period);
$rowc[] = 'aggregate';
$rows = array_reverse($rows);
$rowc = array_reverse($rowc);
$table = new AphrontTableView($rows);
$table->setRowClasses($rowc);
$table->setHeaders(
array(
pht('Period'),
pht('Opened'),
pht('Closed'),
pht('Change'),
));
$table->setColumnClasses(
array(
'right wide',
'n',
'n',
'n',
));
if ($handle) {
$inst = pht(
'NOTE: This table reflects tasks currently in '.
'the project. If a task was opened in the past but added to '.
'the project recently, it is counted on the day it was '.
'opened, not the day it was categorized. If a task was part '.
'of this project in the past but no longer is, it is not '.
'counted at all.');
$header = pht('Task Burn Rate for Project %s', $handle->renderLink());
$caption = phutil_tag('p', array(), $inst);
} else {
$header = pht('Task Burn Rate for All Tasks');
$caption = null;
}
if ($caption) {
$caption = id(new PHUIInfoView())
->appendChild($caption)
->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
}
$panel = new PHUIObjectBoxView();
$panel->setHeaderText($header);
if ($caption) {
$panel->setInfoView($caption);
}
$panel->appendChild($table);
$tokens = array();
if ($handle) {
$tokens = array($handle);
}
$filter = $this->renderReportFilters($tokens, $has_window = false);
$id = celerity_generate_unique_node_id();
$chart = phutil_tag(
'div',
array(
'id' => $id,
'style' => 'border: 1px solid #BFCFDA; '.
'background-color: #fff; '.
'margin: 8px 16px; '.
'height: 400px; ',
),
'');
list($burn_x, $burn_y) = $this->buildSeries($data);
require_celerity_resource('raphael-core');
require_celerity_resource('raphael-g');
require_celerity_resource('raphael-g-line');
Javelin::initBehavior('line-chart', array(
'hardpoint' => $id,
'x' => array(
$burn_x,
),
'y' => array(
$burn_y,
),
'xformat' => 'epoch',
'yformat' => 'int',
));
return array($filter, $chart, $panel);
}
private function renderReportFilters(array $tokens, $has_window) {
$request = $this->getRequest();
$user = $request->getUser();
$form = id(new AphrontFormView())
->setUser($user)
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorProjectDatasource())
->setLabel(pht('Project'))
->setLimit(1)
->setName('set_project')
- ->setValue($tokens));
+ // TODO: This is silly, but this is Maniphest reports.
+ ->setValue(mpull($tokens, 'getPHID')));
if ($has_window) {
list($window_str, $ignored, $window_error) = $this->getWindow();
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Recently Means'))
->setName('set_window')
->setCaption(
pht('Configure the cutoff for the "Recently Closed" column.'))
->setValue($window_str)
->setError($window_error));
}
$form
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Filter By Project')));
$filter = new AphrontListFilterView();
$filter->appendChild($form);
return $filter;
}
private function buildSeries(array $data) {
$out = array();
$counter = 0;
foreach ($data as $row) {
$t = (int)$row['dateCreated'];
if ($row['_is_close']) {
--$counter;
$out[$t] = $counter;
} else if ($row['_is_open']) {
++$counter;
$out[$t] = $counter;
}
}
return array(array_keys($out), array_values($out));
}
private function formatBurnRow($label, $info) {
$delta = $info['open'] - $info['close'];
$fmt = number_format($delta);
if ($delta > 0) {
$fmt = '+'.$fmt;
$fmt = phutil_tag('span', array('class' => 'red'), $fmt);
} else {
$fmt = phutil_tag('span', array('class' => 'green'), $fmt);
}
return array(
$label,
number_format($info['open']),
number_format($info['close']),
$fmt,
);
}
public function renderOpenTasks() {
$request = $this->getRequest();
$user = $request->getUser();
$query = id(new ManiphestTaskQuery())
->setViewer($user)
->withStatuses(ManiphestTaskStatus::getOpenStatusConstants());
switch ($this->view) {
case 'project':
$query->needProjectPHIDs(true);
break;
}
$project_phid = $request->getStr('project');
$project_handle = null;
if ($project_phid) {
$phids = array($project_phid);
$handles = $this->loadViewerHandles($phids);
$project_handle = $handles[$project_phid];
$query->withAnyProjects($phids);
}
$tasks = $query->execute();
$recently_closed = $this->loadRecentlyClosedTasks();
$date = phabricator_date(time(), $user);
switch ($this->view) {
case 'user':
$result = mgroup($tasks, 'getOwnerPHID');
$leftover = idx($result, '', array());
unset($result['']);
$result_closed = mgroup($recently_closed, 'getOwnerPHID');
$leftover_closed = idx($result_closed, '', array());
unset($result_closed['']);
$base_link = '/maniphest/?assigned=';
$leftover_name = phutil_tag('em', array(), pht('(Up For Grabs)'));
$col_header = pht('User');
$header = pht('Open Tasks by User and Priority (%s)', $date);
break;
case 'project':
$result = array();
$leftover = array();
foreach ($tasks as $task) {
$phids = $task->getProjectPHIDs();
if ($phids) {
foreach ($phids as $project_phid) {
$result[$project_phid][] = $task;
}
} else {
$leftover[] = $task;
}
}
$result_closed = array();
$leftover_closed = array();
foreach ($recently_closed as $task) {
$phids = $task->getProjectPHIDs();
if ($phids) {
foreach ($phids as $project_phid) {
$result_closed[$project_phid][] = $task;
}
} else {
$leftover_closed[] = $task;
}
}
$base_link = '/maniphest/?allProjects=';
$leftover_name = phutil_tag('em', array(), pht('(No Project)'));
$col_header = pht('Project');
$header = pht('Open Tasks by Project and Priority (%s)', $date);
break;
}
$phids = array_keys($result);
$handles = $this->loadViewerHandles($phids);
$handles = msort($handles, 'getName');
$order = $request->getStr('order', 'name');
list($order, $reverse) = AphrontTableView::parseSort($order);
require_celerity_resource('aphront-tooltip-css');
Javelin::initBehavior('phabricator-tooltips', array());
$rows = array();
$pri_total = array();
foreach (array_merge($handles, array(null)) as $handle) {
if ($handle) {
if (($project_handle) &&
($project_handle->getPHID() == $handle->getPHID())) {
// If filtering by, e.g., "bugs", don't show a "bugs" group.
continue;
}
$tasks = idx($result, $handle->getPHID(), array());
$name = phutil_tag(
'a',
array(
'href' => $base_link.$handle->getPHID(),
),
$handle->getName());
$closed = idx($result_closed, $handle->getPHID(), array());
} else {
$tasks = $leftover;
$name = $leftover_name;
$closed = $leftover_closed;
}
$taskv = $tasks;
$tasks = mgroup($tasks, 'getPriority');
$row = array();
$row[] = $name;
$total = 0;
foreach (ManiphestTaskPriority::getTaskPriorityMap() as $pri => $label) {
$n = count(idx($tasks, $pri, array()));
if ($n == 0) {
$row[] = '-';
} else {
$row[] = number_format($n);
}
$total += $n;
}
$row[] = number_format($total);
list($link, $oldest_all) = $this->renderOldest($taskv);
$row[] = $link;
$normal_or_better = array();
foreach ($taskv as $id => $task) {
// TODO: This is sort of a hard-code for the default "normal" status.
// When reports are more powerful, this should be made more general.
if ($task->getPriority() < 50) {
continue;
}
$normal_or_better[$id] = $task;
}
list($link, $oldest_pri) = $this->renderOldest($normal_or_better);
$row[] = $link;
if ($closed) {
$task_ids = implode(',', mpull($closed, 'getID'));
$row[] = phutil_tag(
'a',
array(
'href' => '/maniphest/?ids='.$task_ids,
'target' => '_blank',
),
number_format(count($closed)));
} else {
$row[] = '-';
}
switch ($order) {
case 'total':
$row['sort'] = $total;
break;
case 'oldest-all':
$row['sort'] = $oldest_all;
break;
case 'oldest-pri':
$row['sort'] = $oldest_pri;
break;
case 'closed':
$row['sort'] = count($closed);
break;
case 'name':
default:
$row['sort'] = $handle ? $handle->getName() : '~';
break;
}
$rows[] = $row;
}
$rows = isort($rows, 'sort');
foreach ($rows as $k => $row) {
unset($rows[$k]['sort']);
}
if ($reverse) {
$rows = array_reverse($rows);
}
$cname = array($col_header);
$cclass = array('pri right wide');
$pri_map = ManiphestTaskPriority::getShortNameMap();
foreach ($pri_map as $pri => $label) {
$cname[] = $label;
$cclass[] = 'n';
}
$cname[] = 'Total';
$cclass[] = 'n';
$cname[] = javelin_tag(
'span',
array(
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => pht('Oldest open task.'),
'size' => 200,
),
),
pht('Oldest (All)'));
$cclass[] = 'n';
$cname[] = javelin_tag(
'span',
array(
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => pht('Oldest open task, excluding those with Low or '.
'Wishlist priority.'),
'size' => 200,
),
),
pht('Oldest (Pri)'));
$cclass[] = 'n';
list($ignored, $window_epoch) = $this->getWindow();
$edate = phabricator_datetime($window_epoch, $user);
$cname[] = javelin_tag(
'span',
array(
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => pht('Closed after %s', $edate),
'size' => 260,
),
),
pht('Recently Closed'));
$cclass[] = 'n';
$table = new AphrontTableView($rows);
$table->setHeaders($cname);
$table->setColumnClasses($cclass);
$table->makeSortable(
$request->getRequestURI(),
'order',
$order,
$reverse,
array(
'name',
null,
null,
null,
null,
null,
null,
'total',
'oldest-all',
'oldest-pri',
'closed',
));
$panel = new PHUIObjectBoxView();
$panel->setHeaderText($header);
$panel->appendChild($table);
$tokens = array();
if ($project_handle) {
$tokens = array($project_handle);
}
$filter = $this->renderReportFilters($tokens, $has_window = true);
return array($filter, $panel);
}
/**
* Load all the tasks that have been recently closed.
*/
private function loadRecentlyClosedTasks() {
list($ignored, $window_epoch) = $this->getWindow();
$table = new ManiphestTask();
$xtable = new ManiphestTransaction();
$conn_r = $table->establishConnection('r');
// TODO: Gross. This table is not meant to be queried like this. Build
// real stats tables.
$open_status_list = array();
foreach (ManiphestTaskStatus::getOpenStatusConstants() as $constant) {
$open_status_list[] = json_encode((string)$constant);
}
$rows = queryfx_all(
$conn_r,
'SELECT t.id FROM %T t JOIN %T x ON x.objectPHID = t.phid
WHERE t.status NOT IN (%Ls)
AND x.oldValue IN (null, %Ls)
AND x.newValue NOT IN (%Ls)
AND t.dateModified >= %d
AND x.dateCreated >= %d',
$table->getTableName(),
$xtable->getTableName(),
ManiphestTaskStatus::getOpenStatusConstants(),
$open_status_list,
$open_status_list,
$window_epoch,
$window_epoch);
if (!$rows) {
return array();
}
$ids = ipull($rows, 'id');
$query = id(new ManiphestTaskQuery())
->setViewer($this->getRequest()->getUser())
->withIDs($ids);
switch ($this->view) {
case 'project':
$query->needProjectPHIDs(true);
break;
}
return $query->execute();
}
/**
* Parse the "Recently Means" filter into:
*
* - A string representation, like "12 AM 7 days ago" (default);
* - a locale-aware epoch representation; and
* - a possible error.
*/
private function getWindow() {
$request = $this->getRequest();
$user = $request->getUser();
$window_str = $this->getRequest()->getStr('window', '12 AM 7 days ago');
$error = null;
$window_epoch = null;
// Do locale-aware parsing so that the user's timezone is assumed for
// time windows like "3 PM", rather than assuming the server timezone.
$window_epoch = PhabricatorTime::parseLocalTime($window_str, $user);
if (!$window_epoch) {
$error = 'Invalid';
$window_epoch = time() - (60 * 60 * 24 * 7);
}
// If the time ends up in the future, convert it to the corresponding time
// and equal distance in the past. This is so users can type "6 days" (which
// means "6 days from now") and get the behavior of "6 days ago", rather
// than no results (because the window epoch is in the future). This might
// be a little confusing because it casues "tomorrow" to mean "yesterday"
// and "2022" (or whatever) to mean "ten years ago", but these inputs are
// nonsense anyway.
if ($window_epoch > time()) {
$window_epoch = time() - ($window_epoch - time());
}
return array($window_str, $window_epoch, $error);
}
private function renderOldest(array $tasks) {
assert_instances_of($tasks, 'ManiphestTask');
$oldest = null;
foreach ($tasks as $id => $task) {
if (($oldest === null) ||
($task->getDateCreated() < $tasks[$oldest]->getDateCreated())) {
$oldest = $id;
}
}
if ($oldest === null) {
return array('-', 0);
}
$oldest = $tasks[$oldest];
$raw_age = (time() - $oldest->getDateCreated());
$age = number_format($raw_age / (24 * 60 * 60)).' d';
$link = javelin_tag(
'a',
array(
'href' => '/T'.$oldest->getID(),
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => 'T'.$oldest->getID().': '.$oldest->getTitle(),
),
'target' => '_blank',
),
$age);
return array($link, $raw_age);
}
}
diff --git a/src/applications/maniphest/controller/ManiphestTaskDetailController.php b/src/applications/maniphest/controller/ManiphestTaskDetailController.php
index 147e9dfc6e..24c98ba24f 100644
--- a/src/applications/maniphest/controller/ManiphestTaskDetailController.php
+++ b/src/applications/maniphest/controller/ManiphestTaskDetailController.php
@@ -1,588 +1,588 @@
id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$e_title = null;
$priority_map = ManiphestTaskPriority::getTaskPriorityMap();
$task = id(new ManiphestTaskQuery())
->setViewer($user)
->withIDs(array($this->id))
->needSubscriberPHIDs(true)
->executeOne();
if (!$task) {
return new Aphront404Response();
}
$workflow = $request->getStr('workflow');
$parent_task = null;
if ($workflow && is_numeric($workflow)) {
$parent_task = id(new ManiphestTaskQuery())
->setViewer($user)
->withIDs(array($workflow))
->executeOne();
}
$field_list = PhabricatorCustomField::getObjectFields(
$task,
PhabricatorCustomField::ROLE_VIEW);
$field_list
->setViewer($user)
->readFieldsFromStorage($task);
$e_commit = ManiphestTaskHasCommitEdgeType::EDGECONST;
$e_dep_on = ManiphestTaskDependsOnTaskEdgeType::EDGECONST;
$e_dep_by = ManiphestTaskDependedOnByTaskEdgeType::EDGECONST;
$e_rev = ManiphestTaskHasRevisionEdgeType::EDGECONST;
$e_mock = ManiphestTaskHasMockEdgeType::EDGECONST;
$phid = $task->getPHID();
$query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(array($phid))
->withEdgeTypes(
array(
$e_commit,
$e_dep_on,
$e_dep_by,
$e_rev,
$e_mock,
));
$edges = idx($query->execute(), $phid);
$phids = array_fill_keys($query->getDestinationPHIDs(), true);
if ($task->getOwnerPHID()) {
$phids[$task->getOwnerPHID()] = true;
}
$phids[$task->getAuthorPHID()] = true;
$attached = $task->getAttached();
foreach ($attached as $type => $list) {
foreach ($list as $phid => $info) {
$phids[$phid] = true;
}
}
if ($parent_task) {
$phids[$parent_task->getPHID()] = true;
}
$phids = array_keys($phids);
$handles = $user->loadHandles($phids);
$info_view = null;
if ($parent_task) {
$info_view = new PHUIInfoView();
$info_view->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
$info_view->addButton(
id(new PHUIButtonView())
->setTag('a')
->setHref('/maniphest/task/create/?parent='.$parent_task->getID())
->setText(pht('Create Another Subtask')));
$info_view->appendChild(hsprintf(
'Created a subtask of %s.',
$handles->renderHandle($parent_task->getPHID())));
} else if ($workflow == 'create') {
$info_view = new PHUIInfoView();
$info_view->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
$info_view->addButton(
id(new PHUIButtonView())
->setTag('a')
->setHref('/maniphest/task/create/?template='.$task->getID())
->setText(pht('Similar Task')));
$info_view->addButton(
id(new PHUIButtonView())
->setTag('a')
->setHref('/maniphest/task/create/')
->setText(pht('Empty Task')));
$info_view->appendChild(pht('New task created. Create another?'));
}
$engine = new PhabricatorMarkupEngine();
$engine->setViewer($user);
$engine->setContextObject($task);
$engine->addObject($task, ManiphestTask::MARKUP_FIELD_DESCRIPTION);
$timeline = $this->buildTransactionTimeline(
$task,
new ManiphestTransactionQuery(),
$engine);
$resolution_types = ManiphestTaskStatus::getTaskStatusMap();
$transaction_types = array(
PhabricatorTransactions::TYPE_COMMENT => pht('Comment'),
ManiphestTransaction::TYPE_STATUS => pht('Change Status'),
ManiphestTransaction::TYPE_OWNER => pht('Reassign / Claim'),
PhabricatorTransactions::TYPE_SUBSCRIBERS => pht('Add CCs'),
ManiphestTransaction::TYPE_PRIORITY => pht('Change Priority'),
PhabricatorTransactions::TYPE_EDGE => pht('Associate Projects'),
);
// Remove actions the user doesn't have permission to take.
$requires = array(
ManiphestTransaction::TYPE_OWNER =>
ManiphestEditAssignCapability::CAPABILITY,
ManiphestTransaction::TYPE_PRIORITY =>
ManiphestEditPriorityCapability::CAPABILITY,
PhabricatorTransactions::TYPE_EDGE =>
ManiphestEditProjectsCapability::CAPABILITY,
ManiphestTransaction::TYPE_STATUS =>
ManiphestEditStatusCapability::CAPABILITY,
);
foreach ($transaction_types as $type => $name) {
if (isset($requires[$type])) {
if (!$this->hasApplicationCapability($requires[$type])) {
unset($transaction_types[$type]);
}
}
}
// Don't show an option to change to the current status, or to change to
// the duplicate status explicitly.
unset($resolution_types[$task->getStatus()]);
unset($resolution_types[ManiphestTaskStatus::getDuplicateStatus()]);
// Don't show owner/priority changes for closed tasks, as they don't make
// much sense.
if ($task->isClosed()) {
unset($transaction_types[ManiphestTransaction::TYPE_PRIORITY]);
unset($transaction_types[ManiphestTransaction::TYPE_OWNER]);
}
$default_claim = array(
$user->getPHID() => $user->getUsername().' ('.$user->getRealName().')',
);
$draft = id(new PhabricatorDraft())->loadOneWhere(
'authorPHID = %s AND draftKey = %s',
$user->getPHID(),
$task->getPHID());
if ($draft) {
$draft_text = $draft->getDraft();
} else {
$draft_text = null;
}
$comment_form = new AphrontFormView();
$comment_form
->setUser($user)
->setWorkflow(true)
->setAction('/maniphest/transaction/save/')
->setEncType('multipart/form-data')
->addHiddenInput('taskID', $task->getID())
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Action'))
->setName('action')
->setOptions($transaction_types)
->setID('transaction-action'))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Status'))
->setName('resolution')
->setControlID('resolution')
->setControlStyle('display: none')
->setOptions($resolution_types))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Assign To'))
->setName('assign_to')
->setControlID('assign_to')
->setControlStyle('display: none')
->setID('assign-tokenizer')
->setDisableBehavior(true))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('CCs'))
->setName('ccs')
->setControlID('ccs')
->setControlStyle('display: none')
->setID('cc-tokenizer')
->setDisableBehavior(true))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Priority'))
->setName('priority')
->setOptions($priority_map)
->setControlID('priority')
->setControlStyle('display: none')
->setValue($task->getPriority()))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Projects'))
->setName('projects')
->setControlID('projects')
->setControlStyle('display: none')
->setID('projects-tokenizer')
->setDisableBehavior(true))
->appendChild(
id(new AphrontFormFileControl())
->setLabel(pht('File'))
->setName('file')
->setControlID('file')
->setControlStyle('display: none'))
->appendChild(
id(new PhabricatorRemarkupControl())
->setUser($user)
->setLabel(pht('Comments'))
->setName('comments')
->setValue($draft_text)
->setID('transaction-comments')
->setUser($user))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Submit')));
$control_map = array(
ManiphestTransaction::TYPE_STATUS => 'resolution',
ManiphestTransaction::TYPE_OWNER => 'assign_to',
PhabricatorTransactions::TYPE_SUBSCRIBERS => 'ccs',
ManiphestTransaction::TYPE_PRIORITY => 'priority',
PhabricatorTransactions::TYPE_EDGE => 'projects',
);
$projects_source = new PhabricatorProjectDatasource();
$users_source = new PhabricatorPeopleDatasource();
$mailable_source = new PhabricatorMetaMTAMailableDatasource();
$tokenizer_map = array(
PhabricatorTransactions::TYPE_EDGE => array(
'id' => 'projects-tokenizer',
'src' => $projects_source->getDatasourceURI(),
'placeholder' => $projects_source->getPlaceholderText(),
),
ManiphestTransaction::TYPE_OWNER => array(
'id' => 'assign-tokenizer',
'src' => $users_source->getDatasourceURI(),
'value' => $default_claim,
'limit' => 1,
'placeholder' => $users_source->getPlaceholderText(),
),
PhabricatorTransactions::TYPE_SUBSCRIBERS => array(
'id' => 'cc-tokenizer',
'src' => $mailable_source->getDatasourceURI(),
'placeholder' => $mailable_source->getPlaceholderText(),
),
);
// TODO: Initializing these behaviors for logged out users fatals things.
if ($user->isLoggedIn()) {
Javelin::initBehavior('maniphest-transaction-controls', array(
'select' => 'transaction-action',
'controlMap' => $control_map,
'tokenizers' => $tokenizer_map,
));
Javelin::initBehavior('maniphest-transaction-preview', array(
'uri' => '/maniphest/transaction/preview/'.$task->getID().'/',
'preview' => 'transaction-preview',
'comments' => 'transaction-comments',
'action' => 'transaction-action',
'map' => $control_map,
'tokenizers' => $tokenizer_map,
));
}
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$comment_header = $is_serious
? pht('Add Comment')
: pht('Weigh In');
$preview_panel = phutil_tag_div(
'aphront-panel-preview',
phutil_tag(
'div',
array('id' => 'transaction-preview'),
phutil_tag_div(
'aphront-panel-preview-loading-text',
pht('Loading preview...'))));
$object_name = 'T'.$task->getID();
$actions = $this->buildActionView($task);
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb($object_name, '/'.$object_name);
$header = $this->buildHeaderView($task);
$properties = $this->buildPropertyView(
$task, $field_list, $edges, $actions, $handles);
$description = $this->buildDescriptionView($task, $engine);
if (!$user->isLoggedIn()) {
// TODO: Eventually, everything should run through this. For now, we're
// only using it to get a consistent "Login to Comment" button.
$comment_box = id(new PhabricatorApplicationTransactionCommentView())
->setUser($user)
->setRequestURI($request->getRequestURI());
$preview_panel = null;
} else {
$comment_box = id(new PHUIObjectBoxView())
->setFlush(true)
->setHeaderText($comment_header)
->appendChild($comment_form);
$timeline->setQuoteTargetID('transaction-comments');
$timeline->setQuoteRef($object_name);
}
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
if ($info_view) {
$object_box->setInfoView($info_view);
}
if ($description) {
$object_box->addPropertyList($description);
}
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
$timeline,
$comment_box,
$preview_panel,
),
array(
'title' => 'T'.$task->getID().' '.$task->getTitle(),
'pageObjects' => array($task->getPHID()),
));
}
private function buildHeaderView(ManiphestTask $task) {
$view = id(new PHUIHeaderView())
->setHeader($task->getTitle())
->setUser($this->getRequest()->getUser())
->setPolicyObject($task);
$status = $task->getStatus();
$status_name = ManiphestTaskStatus::renderFullDescription($status);
$view->addProperty(PHUIHeaderView::PROPERTY_STATUS, $status_name);
return $view;
}
private function buildActionView(ManiphestTask $task) {
$viewer = $this->getRequest()->getUser();
$viewer_phid = $viewer->getPHID();
$id = $task->getID();
$phid = $task->getPHID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$task,
PhabricatorPolicyCapability::CAN_EDIT);
$view = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($task)
->setObjectURI($this->getRequest()->getRequestURI());
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Task'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI("/task/edit/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Merge Duplicates In'))
->setHref("/search/attach/{$phid}/TASK/merge/")
->setWorkflow(true)
->setIcon('fa-compress')
->setDisabled(!$can_edit)
->setWorkflow(true));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Create Subtask'))
->setHref($this->getApplicationURI("/task/create/?parent={$id}"))
->setIcon('fa-level-down'));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Blocking Tasks'))
->setHref("/search/attach/{$phid}/TASK/blocks/")
->setWorkflow(true)
->setIcon('fa-link')
->setDisabled(!$can_edit)
->setWorkflow(true));
return $view;
}
private function buildPropertyView(
ManiphestTask $task,
PhabricatorCustomFieldList $field_list,
array $edges,
PhabricatorActionListView $actions,
$handles) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($task)
->setActionList($actions);
$view->addProperty(
pht('Assigned To'),
$task->getOwnerPHID()
? $handles->renderHandle($task->getOwnerPHID())
: phutil_tag('em', array(), pht('None')));
$view->addProperty(
pht('Priority'),
ManiphestTaskPriority::getTaskPriorityName($task->getPriority()));
$view->addProperty(
pht('Author'),
$handles->renderHandle($task->getAuthorPHID()));
$source = $task->getOriginalEmailSource();
if ($source) {
$subject = '[T'.$task->getID().'] '.$task->getTitle();
$view->addProperty(
pht('From Email'),
phutil_tag(
'a',
array(
'href' => 'mailto:'.$source.'?subject='.$subject,
),
$source));
}
$edge_types = array(
ManiphestTaskDependedOnByTaskEdgeType::EDGECONST
=> pht('Blocks'),
ManiphestTaskDependsOnTaskEdgeType::EDGECONST
=> pht('Blocked By'),
ManiphestTaskHasRevisionEdgeType::EDGECONST
=> pht('Differential Revisions'),
ManiphestTaskHasMockEdgeType::EDGECONST
=> pht('Pholio Mocks'),
);
$revisions_commits = array();
$commit_phids = array_keys(
$edges[ManiphestTaskHasCommitEdgeType::EDGECONST]);
if ($commit_phids) {
$commit_drev = DiffusionCommitHasRevisionEdgeType::EDGECONST;
$drev_edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($commit_phids)
->withEdgeTypes(array($commit_drev))
->execute();
foreach ($commit_phids as $phid) {
$revisions_commits[$phid] = $handles->renderHandle($phid);
$revision_phid = key($drev_edges[$phid][$commit_drev]);
$revision_handle = $handles->getHandleIfExists($revision_phid);
if ($revision_handle) {
$task_drev = ManiphestTaskHasRevisionEdgeType::EDGECONST;
unset($edges[$task_drev][$revision_phid]);
$revisions_commits[$phid] = hsprintf(
'%s / %s',
$revision_handle->renderLink($revision_handle->getName()),
$revisions_commits[$phid]);
}
}
}
foreach ($edge_types as $edge_type => $edge_name) {
if ($edges[$edge_type]) {
$edge_handles = $viewer->loadHandles(array_keys($edges[$edge_type]));
$view->addProperty(
$edge_name,
$edge_handles->renderList());
}
}
if ($revisions_commits) {
$view->addProperty(
pht('Commits'),
phutil_implode_html(phutil_tag('br'), $revisions_commits));
}
$attached = $task->getAttached();
if (!is_array($attached)) {
$attached = array();
}
$file_infos = idx($attached, PhabricatorFileFilePHIDType::TYPECONST);
if ($file_infos) {
$file_phids = array_keys($file_infos);
// TODO: These should probably be handles or something; clean this up
// as we sort out file attachments.
$files = id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs($file_phids)
->execute();
$file_view = new PhabricatorFileLinkListView();
$file_view->setFiles($files);
$view->addProperty(
pht('Files'),
$file_view->render());
}
$view->invokeWillRenderEvent();
$field_list->appendFieldsToPropertyList(
$task,
$viewer,
$view);
return $view;
}
private function buildDescriptionView(
ManiphestTask $task,
PhabricatorMarkupEngine $engine) {
$section = null;
if (strlen($task->getDescription())) {
$section = new PHUIPropertyListView();
$section->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);
$section->addTextContent(
phutil_tag(
'div',
array(
'class' => 'phabricator-remarkup',
),
$engine->getOutput($task, ManiphestTask::MARKUP_FIELD_DESCRIPTION)));
}
return $section;
}
}
diff --git a/src/applications/maniphest/controller/ManiphestTaskEditController.php b/src/applications/maniphest/controller/ManiphestTaskEditController.php
index 1912b94810..915ea6c33a 100644
--- a/src/applications/maniphest/controller/ManiphestTaskEditController.php
+++ b/src/applications/maniphest/controller/ManiphestTaskEditController.php
@@ -1,781 +1,767 @@
id = idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$response_type = $request->getStr('responseType', 'task');
$order = $request->getStr('order', PhabricatorProjectColumn::DEFAULT_ORDER);
$can_edit_assign = $this->hasApplicationCapability(
ManiphestEditAssignCapability::CAPABILITY);
$can_edit_policies = $this->hasApplicationCapability(
ManiphestEditPoliciesCapability::CAPABILITY);
$can_edit_priority = $this->hasApplicationCapability(
ManiphestEditPriorityCapability::CAPABILITY);
$can_edit_projects = $this->hasApplicationCapability(
ManiphestEditProjectsCapability::CAPABILITY);
$can_edit_status = $this->hasApplicationCapability(
ManiphestEditStatusCapability::CAPABILITY);
$can_create_projects = PhabricatorPolicyFilter::hasCapability(
$user,
PhabricatorApplication::getByClass('PhabricatorProjectApplication'),
ProjectCreateProjectsCapability::CAPABILITY);
$parent_task = null;
$template_id = null;
if ($this->id) {
$task = id(new ManiphestTaskQuery())
->setViewer($user)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->withIDs(array($this->id))
->needSubscriberPHIDs(true)
->needProjectPHIDs(true)
->executeOne();
if (!$task) {
return new Aphront404Response();
}
} else {
$task = ManiphestTask::initializeNewTask($user);
// We currently do not allow you to set the task status when creating
// a new task, although now that statuses are custom it might make
// sense.
$can_edit_status = false;
// These allow task creation with defaults.
if (!$request->isFormPost()) {
$task->setTitle($request->getStr('title'));
if ($can_edit_projects) {
$projects = $request->getStr('projects');
if ($projects) {
$tokens = $request->getStrList('projects');
$type_project = PhabricatorProjectProjectPHIDType::TYPECONST;
foreach ($tokens as $key => $token) {
if (phid_get_type($token) == $type_project) {
// If this is formatted like a PHID, leave it as-is.
continue;
}
if (preg_match('/^#/', $token)) {
// If this already has a "#", leave it as-is.
continue;
}
// Add a "#" prefix.
$tokens[$key] = '#'.$token;
}
$default_projects = id(new PhabricatorObjectQuery())
->setViewer($user)
->withNames($tokens)
->execute();
$default_projects = mpull($default_projects, 'getPHID');
if ($default_projects) {
$task->attachProjectPHIDs($default_projects);
}
}
}
if ($can_edit_priority) {
$priority = $request->getInt('priority');
if ($priority !== null) {
$priority_map = ManiphestTaskPriority::getTaskPriorityMap();
if (isset($priority_map[$priority])) {
$task->setPriority($priority);
}
}
}
$task->setDescription($request->getStr('description'));
if ($can_edit_assign) {
$assign = $request->getStr('assign');
if (strlen($assign)) {
$assign_user = id(new PhabricatorPeopleQuery())
->setViewer($user)
->withUsernames(array($assign))
->executeOne();
if (!$assign_user) {
$assign_user = id(new PhabricatorPeopleQuery())
->setViewer($user)
->withPHIDs(array($assign))
->executeOne();
}
if ($assign_user) {
$task->setOwnerPHID($assign_user->getPHID());
}
}
}
}
$template_id = $request->getInt('template');
// You can only have a parent task if you're creating a new task.
$parent_id = $request->getInt('parent');
if (strlen($parent_id)) {
$parent_task = id(new ManiphestTaskQuery())
->setViewer($user)
->withIDs(array($parent_id))
->executeOne();
if (!$parent_task) {
return new Aphront404Response();
}
if (!$template_id) {
$template_id = $parent_id;
}
}
}
$errors = array();
$e_title = true;
$field_list = PhabricatorCustomField::getObjectFields(
$task,
PhabricatorCustomField::ROLE_EDIT);
$field_list->setViewer($user);
$field_list->readFieldsFromStorage($task);
$aux_fields = $field_list->getFields();
if ($request->isFormPost()) {
$changes = array();
$new_title = $request->getStr('title');
$new_desc = $request->getStr('description');
$new_status = $request->getStr('status');
if (!$task->getID()) {
$workflow = 'create';
} else {
$workflow = '';
}
$changes[ManiphestTransaction::TYPE_TITLE] = $new_title;
$changes[ManiphestTransaction::TYPE_DESCRIPTION] = $new_desc;
if ($can_edit_status) {
$changes[ManiphestTransaction::TYPE_STATUS] = $new_status;
} else if (!$task->getID()) {
// Create an initial status transaction for the burndown chart.
// TODO: We can probably remove this once Facts comes online.
$changes[ManiphestTransaction::TYPE_STATUS] = $task->getStatus();
}
$owner_tokenizer = $request->getArr('assigned_to');
$owner_phid = reset($owner_tokenizer);
if (!strlen($new_title)) {
$e_title = pht('Required');
$errors[] = pht('Title is required.');
}
$old_values = array();
foreach ($aux_fields as $aux_arr_key => $aux_field) {
// TODO: This should be buildFieldTransactionsFromRequest() once we
// switch to ApplicationTransactions properly.
$aux_old_value = $aux_field->getOldValueForApplicationTransactions();
$aux_field->readValueFromRequest($request);
$aux_new_value = $aux_field->getNewValueForApplicationTransactions();
// TODO: We're faking a call to the ApplicaitonTransaction validation
// logic here. We need valid objects to pass, but they aren't used
// in a meaningful way. For now, build User objects. Once the Maniphest
// objects exist, this will switch over automatically. This is a big
// hack but shouldn't be long for this world.
$placeholder_editor = new PhabricatorUserProfileEditor();
$field_errors = $aux_field->validateApplicationTransactions(
$placeholder_editor,
PhabricatorTransactions::TYPE_CUSTOMFIELD,
array(
id(new ManiphestTransaction())
->setOldValue($aux_old_value)
->setNewValue($aux_new_value),
));
foreach ($field_errors as $error) {
$errors[] = $error->getMessage();
}
$old_values[$aux_field->getFieldKey()] = $aux_old_value;
}
if ($errors) {
$task->setTitle($new_title);
$task->setDescription($new_desc);
$task->setPriority($request->getInt('priority'));
$task->setOwnerPHID($owner_phid);
$task->attachSubscriberPHIDs($request->getArr('cc'));
$task->attachProjectPHIDs($request->getArr('projects'));
} else {
if ($can_edit_priority) {
$changes[ManiphestTransaction::TYPE_PRIORITY] =
$request->getInt('priority');
}
if ($can_edit_assign) {
$changes[ManiphestTransaction::TYPE_OWNER] = $owner_phid;
}
$changes[PhabricatorTransactions::TYPE_SUBSCRIBERS] =
array('=' => $request->getArr('cc'));
if ($can_edit_projects) {
$projects = $request->getArr('projects');
$changes[PhabricatorTransactions::TYPE_EDGE] =
$projects;
$column_phid = $request->getStr('columnPHID');
// allow for putting a task in a project column at creation -only-
if (!$task->getID() && $column_phid && $projects) {
$column = id(new PhabricatorProjectColumnQuery())
->setViewer($user)
->withProjectPHIDs($projects)
->withPHIDs(array($column_phid))
->executeOne();
if ($column) {
$changes[ManiphestTransaction::TYPE_PROJECT_COLUMN] =
array(
'new' => array(
'projectPHID' => $column->getProjectPHID(),
'columnPHIDs' => array($column_phid),
),
'old' => array(
'projectPHID' => $column->getProjectPHID(),
'columnPHIDs' => array(),
),
);
}
}
}
if ($can_edit_policies) {
$changes[PhabricatorTransactions::TYPE_VIEW_POLICY] =
$request->getStr('viewPolicy');
$changes[PhabricatorTransactions::TYPE_EDIT_POLICY] =
$request->getStr('editPolicy');
}
$template = new ManiphestTransaction();
$transactions = array();
foreach ($changes as $type => $value) {
$transaction = clone $template;
$transaction->setTransactionType($type);
if ($type == ManiphestTransaction::TYPE_PROJECT_COLUMN) {
$transaction->setNewValue($value['new']);
$transaction->setOldValue($value['old']);
} else if ($type == PhabricatorTransactions::TYPE_EDGE) {
$project_type =
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
$transaction
->setMetadataValue('edge:type', $project_type)
->setNewValue(
array(
'=' => array_fuse($value),
));
} else {
$transaction->setNewValue($value);
}
$transactions[] = $transaction;
}
if ($aux_fields) {
foreach ($aux_fields as $aux_field) {
$transaction = clone $template;
$transaction->setTransactionType(
PhabricatorTransactions::TYPE_CUSTOMFIELD);
$aux_key = $aux_field->getFieldKey();
$transaction->setMetadataValue('customfield:key', $aux_key);
$old = idx($old_values, $aux_key);
$new = $aux_field->getNewValueForApplicationTransactions();
$transaction->setOldValue($old);
$transaction->setNewValue($new);
$transactions[] = $transaction;
}
}
if ($transactions) {
$is_new = !$task->getID();
$event = new PhabricatorEvent(
PhabricatorEventType::TYPE_MANIPHEST_WILLEDITTASK,
array(
'task' => $task,
'new' => $is_new,
'transactions' => $transactions,
));
$event->setUser($user);
$event->setAphrontRequest($request);
PhutilEventEngine::dispatchEvent($event);
$task = $event->getValue('task');
$transactions = $event->getValue('transactions');
$editor = id(new ManiphestTransactionEditor())
->setActor($user)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->applyTransactions($task, $transactions);
$event = new PhabricatorEvent(
PhabricatorEventType::TYPE_MANIPHEST_DIDEDITTASK,
array(
'task' => $task,
'new' => $is_new,
'transactions' => $transactions,
));
$event->setUser($user);
$event->setAphrontRequest($request);
PhutilEventEngine::dispatchEvent($event);
}
if ($parent_task) {
// TODO: This should be transactional now.
id(new PhabricatorEdgeEditor())
->addEdge(
$parent_task->getPHID(),
ManiphestTaskDependsOnTaskEdgeType::EDGECONST,
$task->getPHID())
->save();
$workflow = $parent_task->getID();
}
if ($request->isAjax()) {
switch ($response_type) {
case 'card':
$owner = null;
if ($task->getOwnerPHID()) {
$owner = id(new PhabricatorHandleQuery())
->setViewer($user)
->withPHIDs(array($task->getOwnerPHID()))
->executeOne();
}
$tasks = id(new ProjectBoardTaskCard())
->setViewer($user)
->setTask($task)
->setOwner($owner)
->setCanEdit(true)
->getItem();
$column = id(new PhabricatorProjectColumnQuery())
->setViewer($user)
->withPHIDs(array($request->getStr('columnPHID')))
->executeOne();
if (!$column) {
return new Aphront404Response();
}
// re-load projects for accuracy as they are not re-loaded via
// the editor
$project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$task->getPHID(),
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
$task->attachProjectPHIDs($project_phids);
$remove_from_board = false;
if (!in_array($column->getProjectPHID(), $project_phids)) {
$remove_from_board = true;
}
$positions = id(new PhabricatorProjectColumnPositionQuery())
->setViewer($user)
->withColumns(array($column))
->execute();
$task_phids = mpull($positions, 'getObjectPHID');
$column_tasks = id(new ManiphestTaskQuery())
->setViewer($user)
->withPHIDs($task_phids)
->execute();
if ($order == PhabricatorProjectColumn::ORDER_NATURAL) {
// TODO: This is a little bit awkward, because PHP and JS use
// slightly different sort order parameters to achieve the same
// effect. It would be good to unify this a bit at some point.
$sort_map = array();
foreach ($positions as $position) {
$sort_map[$position->getObjectPHID()] = array(
-$position->getSequence(),
$position->getID(),
);
}
} else {
$sort_map = mpull(
$column_tasks,
'getPrioritySortVector',
'getPHID');
}
$data = array(
'sortMap' => $sort_map,
'removeFromBoard' => $remove_from_board,
);
break;
case 'task':
default:
$tasks = $this->renderSingleTask($task);
$data = array();
break;
}
return id(new AphrontAjaxResponse())->setContent(
array(
'tasks' => $tasks,
'data' => $data,
));
}
$redirect_uri = '/T'.$task->getID();
if ($workflow) {
$redirect_uri .= '?workflow='.$workflow;
}
return id(new AphrontRedirectResponse())
->setURI($redirect_uri);
}
} else {
if (!$task->getID()) {
$task->attachSubscriberPHIDs(array(
$user->getPHID(),
));
if ($template_id) {
$template_task = id(new ManiphestTaskQuery())
->setViewer($user)
->withIDs(array($template_id))
->needSubscriberPHIDs(true)
->needProjectPHIDs(true)
->executeOne();
if ($template_task) {
$cc_phids = array_unique(array_merge(
$template_task->getSubscriberPHIDs(),
array($user->getPHID())));
$task->attachSubscriberPHIDs($cc_phids);
$task->attachProjectPHIDs($template_task->getProjectPHIDs());
$task->setOwnerPHID($template_task->getOwnerPHID());
$task->setPriority($template_task->getPriority());
$task->setViewPolicy($template_task->getViewPolicy());
$task->setEditPolicy($template_task->getEditPolicy());
$template_fields = PhabricatorCustomField::getObjectFields(
$template_task,
PhabricatorCustomField::ROLE_EDIT);
$fields = $template_fields->getFields();
foreach ($fields as $key => $field) {
if (!$field->shouldCopyWhenCreatingSimilarTask()) {
unset($fields[$key]);
}
if (empty($aux_fields[$key])) {
unset($fields[$key]);
}
}
if ($fields) {
id(new PhabricatorCustomFieldList($fields))
->setViewer($user)
->readFieldsFromStorage($template_task);
foreach ($fields as $key => $field) {
$aux_fields[$key]->setValueFromStorage(
$field->getValueForStorage());
}
}
}
}
}
}
- $phids = array_merge(
- array($task->getOwnerPHID()),
- $task->getSubscriberPHIDs(),
- $task->getProjectPHIDs());
-
- if ($parent_task) {
- $phids[] = $parent_task->getPHID();
- }
-
- $phids = array_filter($phids);
- $phids = array_unique($phids);
-
- $handles = $this->loadViewerHandles($phids);
-
$error_view = null;
if ($errors) {
$error_view = new PHUIInfoView();
$error_view->setErrors($errors);
}
$priority_map = ManiphestTaskPriority::getTaskPriorityMap();
if ($task->getOwnerPHID()) {
- $assigned_value = array($handles[$task->getOwnerPHID()]);
+ $assigned_value = array($task->getOwnerPHID());
} else {
$assigned_value = array();
}
if ($task->getSubscriberPHIDs()) {
- $cc_value = array_select_keys($handles, $task->getSubscriberPHIDs());
+ $cc_value = $task->getSubscriberPHIDs();
} else {
$cc_value = array();
}
if ($task->getProjectPHIDs()) {
- $projects_value = array_select_keys($handles, $task->getProjectPHIDs());
+ $projects_value = $task->getProjectPHIDs();
} else {
$projects_value = array();
}
$cancel_id = nonempty($task->getID(), $template_id);
if ($cancel_id) {
$cancel_uri = '/T'.$cancel_id;
} else {
$cancel_uri = '/maniphest/';
}
if ($task->getID()) {
$button_name = pht('Save Task');
$header_name = pht('Edit Task');
} else if ($parent_task) {
$cancel_uri = '/T'.$parent_task->getID();
$button_name = pht('Create Task');
$header_name = pht('Create New Subtask');
} else {
$button_name = pht('Create Task');
$header_name = pht('Create New Task');
}
require_celerity_resource('maniphest-task-edit-css');
$project_tokenizer_id = celerity_generate_unique_node_id();
$form = new AphrontFormView();
$form
->setUser($user)
->addHiddenInput('template', $template_id)
->addHiddenInput('responseType', $response_type)
->addHiddenInput('order', $order)
->addHiddenInput('ungrippable', $request->getStr('ungrippable'))
->addHiddenInput('columnPHID', $request->getStr('columnPHID'));
if ($parent_task) {
$form
->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Parent Task'))
- ->setValue($handles[$parent_task->getPHID()]->getFullName()))
+ ->setValue($user->renderHandle($parent_task->getPHID())))
->addHiddenInput('parent', $parent_task->getID());
}
$form
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel(pht('Title'))
->setName('title')
->setError($e_title)
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)
->setValue($task->getTitle()));
if ($can_edit_status) {
// See T4819.
$status_map = ManiphestTaskStatus::getTaskStatusMap();
$dup_status = ManiphestTaskStatus::getDuplicateStatus();
if ($task->getStatus() != $dup_status) {
unset($status_map[$dup_status]);
}
$form
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Status'))
->setName('status')
->setValue($task->getStatus())
->setOptions($status_map));
}
$policies = id(new PhabricatorPolicyQuery())
->setViewer($user)
->setObject($task)
->execute();
if ($can_edit_assign) {
- $form->appendChild(
+ $form->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Assigned To'))
->setName('assigned_to')
->setValue($assigned_value)
->setUser($user)
->setDatasource(new PhabricatorPeopleDatasource())
->setLimit(1));
}
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('CC'))
->setName('cc')
->setValue($cc_value)
->setUser($user)
->setDatasource(new PhabricatorMetaMTAMailableDatasource()));
if ($can_edit_priority) {
$form
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Priority'))
->setName('priority')
->setOptions($priority_map)
->setValue($task->getPriority()));
}
if ($can_edit_policies) {
$form
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($user)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicyObject($task)
->setPolicies($policies)
->setName('viewPolicy'))
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($user)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicyObject($task)
->setPolicies($policies)
->setName('editPolicy'));
}
if ($can_edit_projects) {
$caption = null;
if ($can_create_projects) {
$caption = javelin_tag(
'a',
array(
'href' => '/project/create/',
'mustcapture' => true,
'sigil' => 'project-create',
),
pht('Create New Project'));
}
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Projects'))
->setName('projects')
->setValue($projects_value)
->setID($project_tokenizer_id)
->setCaption($caption)
->setDatasource(new PhabricatorProjectDatasource()));
}
$field_list->appendFieldsToForm($form);
require_celerity_resource('phui-info-view-css');
Javelin::initBehavior('project-create', array(
'tokenizerID' => $project_tokenizer_id,
));
$description_control = new PhabricatorRemarkupControl();
// "Upsell" creating tasks via email in create flows if the instance is
// configured for this awesomeness.
$email_create = PhabricatorEnv::getEnvConfig(
'metamta.maniphest.public-create-email');
if (!$task->getID() && $email_create) {
$email_hint = pht(
'You can also create tasks by sending an email to: %s',
phutil_tag('tt', array(), $email_create));
$description_control->setCaption($email_hint);
}
$description_control
->setLabel(pht('Description'))
->setName('description')
->setID('description-textarea')
->setValue($task->getDescription())
->setUser($user);
$form
->appendChild($description_control);
if ($request->isAjax()) {
$dialog = id(new AphrontDialogView())
->setUser($user)
->setWidth(AphrontDialogView::WIDTH_FULL)
->setTitle($header_name)
->appendChild(
array(
$error_view,
$form->buildLayoutView(),
))
->addCancelButton($cancel_uri)
->addSubmitButton($button_name);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
$form
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($cancel_uri)
->setValue($button_name));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($header_name)
->setFormErrors($errors)
->setForm($form);
$preview = id(new PHUIRemarkupPreviewPanel())
->setHeader(pht('Description Preview'))
->setControlID('description-textarea')
->setPreviewURI($this->getApplicationURI('task/descriptionpreview/'));
if ($task->getID()) {
$page_objects = array($task->getPHID());
} else {
$page_objects = array();
}
$crumbs = $this->buildApplicationCrumbs();
if ($task->getID()) {
$crumbs->addTextCrumb('T'.$task->getID(), '/T'.$task->getID());
}
$crumbs->addTextCrumb($header_name);
return $this->buildApplicationPage(
array(
$crumbs,
$form_box,
$preview,
),
array(
'title' => $header_name,
'pageObjects' => $page_objects,
));
}
}
diff --git a/src/applications/maniphest/query/ManiphestTaskSearchEngine.php b/src/applications/maniphest/query/ManiphestTaskSearchEngine.php
index 7ad2a09d5b..8230971a4b 100644
--- a/src/applications/maniphest/query/ManiphestTaskSearchEngine.php
+++ b/src/applications/maniphest/query/ManiphestTaskSearchEngine.php
@@ -1,591 +1,566 @@
isBoardView = $is_board_view;
return $this;
}
public function getIsBoardView() {
return $this->isBoardView;
}
public function setBaseURI($base_uri) {
$this->baseURI = $base_uri;
return $this;
}
public function getBaseURI() {
return $this->baseURI;
}
public function setShowBatchControls($show_batch_controls) {
$this->showBatchControls = $show_batch_controls;
return $this;
}
public function getResultTypeDescription() {
return pht('Tasks');
}
public function getApplicationClassName() {
return 'PhabricatorManiphestApplication';
}
public function getCustomFieldObject() {
return new ManiphestTask();
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter(
'assignedPHIDs',
$this->readUsersFromRequest($request, 'assigned'));
$saved->setParameter('withUnassigned', $request->getBool('withUnassigned'));
$saved->setParameter(
'authorPHIDs',
$this->readUsersFromRequest($request, 'authors'));
$saved->setParameter(
'subscriberPHIDs',
$this->readPHIDsFromRequest($request, 'subscribers'));
$saved->setParameter(
'statuses',
$this->readListFromRequest($request, 'statuses'));
$saved->setParameter(
'priorities',
$this->readListFromRequest($request, 'priorities'));
$saved->setParameter(
'blocking',
$this->readBoolFromRequest($request, 'blocking'));
$saved->setParameter(
'blocked',
$this->readBoolFromRequest($request, 'blocked'));
$saved->setParameter('group', $request->getStr('group'));
$saved->setParameter('order', $request->getStr('order'));
$ids = $request->getStrList('ids');
foreach ($ids as $key => $id) {
$id = trim($id, ' Tt');
if (!$id || !is_numeric($id)) {
unset($ids[$key]);
} else {
$ids[$key] = $id;
}
}
$saved->setParameter('ids', $ids);
$saved->setParameter('fulltext', $request->getStr('fulltext'));
$saved->setParameter(
'allProjectPHIDs',
$this->readPHIDsFromRequest($request, 'allProjects'));
$saved->setParameter(
'withNoProject',
$request->getBool('withNoProject'));
$saved->setParameter(
'anyProjectPHIDs',
$this->readPHIDsFromRequest($request, 'anyProjects'));
$saved->setParameter(
'excludeProjectPHIDs',
$this->readPHIDsFromRequest($request, 'excludeProjects'));
$saved->setParameter(
'userProjectPHIDs',
$this->readUsersFromRequest($request, 'userProjects'));
$saved->setParameter('createdStart', $request->getStr('createdStart'));
$saved->setParameter('createdEnd', $request->getStr('createdEnd'));
$saved->setParameter('modifiedStart', $request->getStr('modifiedStart'));
$saved->setParameter('modifiedEnd', $request->getStr('modifiedEnd'));
$limit = $request->getInt('limit');
if ($limit > 0) {
$saved->setParameter('limit', $limit);
}
$this->readCustomFieldsFromRequest($request, $saved);
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new ManiphestTaskQuery())
->needProjectPHIDs(true);
$author_phids = $saved->getParameter('authorPHIDs');
if ($author_phids) {
$query->withAuthors($author_phids);
}
$subscriber_phids = $saved->getParameter('subscriberPHIDs');
if ($subscriber_phids) {
$query->withSubscribers($subscriber_phids);
}
$with_unassigned = $saved->getParameter('withUnassigned');
if ($with_unassigned) {
$query->withOwners(array(null));
} else {
$assigned_phids = $saved->getParameter('assignedPHIDs', array());
if ($assigned_phids) {
$query->withOwners($assigned_phids);
}
}
$statuses = $saved->getParameter('statuses');
if ($statuses) {
$query->withStatuses($statuses);
}
$priorities = $saved->getParameter('priorities');
if ($priorities) {
$query->withPriorities($priorities);
}
$query->withBlockingTasks($saved->getParameter('blocking'));
$query->withBlockedTasks($saved->getParameter('blocked'));
$this->applyOrderByToQuery(
$query,
$this->getOrderValues(),
$saved->getParameter('order'));
$group = $saved->getParameter('group');
$group = idx($this->getGroupValues(), $group);
if ($group) {
$query->setGroupBy($group);
} else {
$query->setGroupBy(head($this->getGroupValues()));
}
$ids = $saved->getParameter('ids');
if ($ids) {
$query->withIDs($ids);
}
$fulltext = $saved->getParameter('fulltext');
if (strlen($fulltext)) {
$query->withFullTextSearch($fulltext);
}
$with_no_project = $saved->getParameter('withNoProject');
if ($with_no_project) {
$query->withAllProjects(array(ManiphestTaskOwner::PROJECT_NO_PROJECT));
} else {
$project_phids = $saved->getParameter('allProjectPHIDs');
if ($project_phids) {
$query->withAllProjects($project_phids);
}
}
$any_project_phids = $saved->getParameter('anyProjectPHIDs');
if ($any_project_phids) {
$query->withAnyProjects($any_project_phids);
}
$exclude_project_phids = $saved->getParameter('excludeProjectPHIDs');
if ($exclude_project_phids) {
$query->withoutProjects($exclude_project_phids);
}
$user_project_phids = $saved->getParameter('userProjectPHIDs');
if ($user_project_phids) {
$query->withAnyUserProjects($user_project_phids);
}
$start = $this->parseDateTime($saved->getParameter('createdStart'));
$end = $this->parseDateTime($saved->getParameter('createdEnd'));
if ($start) {
$query->withDateCreatedAfter($start);
}
if ($end) {
$query->withDateCreatedBefore($end);
}
$mod_start = $this->parseDateTime($saved->getParameter('modifiedStart'));
$mod_end = $this->parseDateTime($saved->getParameter('modifiedEnd'));
if ($mod_start) {
$query->withDateModifiedAfter($mod_start);
}
if ($mod_end) {
$query->withDateModifiedBefore($mod_end);
}
$this->applyCustomFieldsToQuery($query, $saved);
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved) {
$assigned_phids = $saved->getParameter('assignedPHIDs', array());
$author_phids = $saved->getParameter('authorPHIDs', array());
$all_project_phids = $saved->getParameter(
'allProjectPHIDs',
array());
$any_project_phids = $saved->getParameter(
'anyProjectPHIDs',
array());
$exclude_project_phids = $saved->getParameter(
'excludeProjectPHIDs',
array());
$user_project_phids = $saved->getParameter(
'userProjectPHIDs',
array());
$subscriber_phids = $saved->getParameter('subscriberPHIDs', array());
- $all_phids = array_merge(
- $assigned_phids,
- $author_phids,
- $all_project_phids,
- $any_project_phids,
- $exclude_project_phids,
- $user_project_phids,
- $subscriber_phids);
-
- if ($all_phids) {
- $handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($all_phids)
- ->execute();
- } else {
- $handles = array();
- }
-
- $assigned_handles = array_select_keys($handles, $assigned_phids);
- $author_handles = array_select_keys($handles, $author_phids);
- $all_project_handles = array_select_keys($handles, $all_project_phids);
- $any_project_handles = array_select_keys($handles, $any_project_phids);
- $exclude_project_handles = array_select_keys(
- $handles,
- $exclude_project_phids);
- $user_project_handles = array_select_keys($handles, $user_project_phids);
- $subscriber_handles = array_select_keys($handles, $subscriber_phids);
-
$with_unassigned = $saved->getParameter('withUnassigned');
$with_no_projects = $saved->getParameter('withNoProject');
$statuses = $saved->getParameter('statuses', array());
$statuses = array_fuse($statuses);
$status_control = id(new AphrontFormCheckboxControl())
->setLabel(pht('Status'));
foreach (ManiphestTaskStatus::getTaskStatusMap() as $status => $name) {
$status_control->addCheckbox(
'statuses[]',
$status,
$name,
isset($statuses[$status]));
}
$priorities = $saved->getParameter('priorities', array());
$priorities = array_fuse($priorities);
$priority_control = id(new AphrontFormCheckboxControl())
->setLabel(pht('Priority'));
foreach (ManiphestTaskPriority::getTaskPriorityMap() as $pri => $name) {
$priority_control->addCheckbox(
'priorities[]',
$pri,
$name,
isset($priorities[$pri]));
}
$blocking_control = id(new AphrontFormSelectControl())
->setLabel(pht('Blocking'))
->setName('blocking')
->setValue($this->getBoolFromQuery($saved, 'blocking'))
->setOptions(array(
'' => pht('Show All Tasks'),
'true' => pht('Show Tasks Blocking Other Tasks'),
- 'false' => pht('Show Tasks Not Blocking Other Tasks'),));
+ 'false' => pht('Show Tasks Not Blocking Other Tasks'),
+ ));
+
$blocked_control = id(new AphrontFormSelectControl())
->setLabel(pht('Blocked'))
->setName('blocked')
->setValue($this->getBoolFromQuery($saved, 'blocked'))
->setOptions(array(
'' => pht('Show All Tasks'),
'true' => pht('Show Tasks Blocked By Other Tasks'),
- 'false' => pht('Show Tasks Not Blocked By Other Tasks'),));
+ 'false' => pht('Show Tasks Not Blocked By Other Tasks'),
+ ));
$ids = $saved->getParameter('ids', array());
$builtin_orders = $this->getOrderOptions();
$custom_orders = $this->getCustomFieldOrderOptions();
$all_orders = $builtin_orders + $custom_orders;
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('assigned')
->setLabel(pht('Assigned To'))
- ->setValue($assigned_handles))
+ ->setValue($assigned_phids))
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'withUnassigned',
1,
pht('Show only unassigned tasks.'),
$with_unassigned))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorProjectDatasource())
->setName('allProjects')
->setLabel(pht('In All Projects'))
- ->setValue($all_project_handles));
+ ->setValue($all_project_phids));
if (!$this->getIsBoardView()) {
$form
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'withNoProject',
1,
pht('Show only tasks with no projects.'),
$with_no_projects));
}
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorProjectDatasource())
->setName('anyProjects')
->setLabel(pht('In Any Project'))
- ->setValue($any_project_handles))
- ->appendChild(
+ ->setValue($any_project_phids))
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorProjectDatasource())
->setName('excludeProjects')
->setLabel(pht('Not In Projects'))
- ->setValue($exclude_project_handles))
- ->appendChild(
+ ->setValue($exclude_project_phids))
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('userProjects')
->setLabel(pht('In Users\' Projects'))
- ->setValue($user_project_handles))
- ->appendChild(
+ ->setValue($user_project_phids))
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('authors')
->setLabel(pht('Authors'))
- ->setValue($author_handles))
- ->appendChild(
+ ->setValue($author_phids))
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorMetaMTAMailableDatasource())
->setName('subscribers')
->setLabel(pht('Subscribers'))
- ->setValue($subscriber_handles))
+ ->setValue($subscriber_phids))
->appendChild(
id(new AphrontFormTextControl())
->setName('fulltext')
->setLabel(pht('Contains Words'))
->setValue($saved->getParameter('fulltext')))
->appendChild($status_control)
->appendChild($priority_control)
->appendChild($blocking_control)
->appendChild($blocked_control);
if (!$this->getIsBoardView()) {
$form
->appendChild(
id(new AphrontFormSelectControl())
->setName('group')
->setLabel(pht('Group By'))
->setValue($saved->getParameter('group'))
->setOptions($this->getGroupOptions()))
->appendChild(
id(new AphrontFormSelectControl())
->setName('order')
->setLabel(pht('Order By'))
->setValue($saved->getParameter('order'))
->setOptions($all_orders));
}
$form
->appendChild(
id(new AphrontFormTextControl())
->setName('ids')
->setLabel(pht('Task IDs'))
->setValue(implode(', ', $ids)));
$this->appendCustomFieldsToForm($form, $saved);
$this->buildDateRange(
$form,
$saved,
'createdStart',
pht('Created After'),
'createdEnd',
pht('Created Before'));
$this->buildDateRange(
$form,
$saved,
'modifiedStart',
pht('Updated After'),
'modifiedEnd',
pht('Updated Before'));
if (!$this->getIsBoardView()) {
$form
->appendChild(
id(new AphrontFormTextControl())
->setName('limit')
->setLabel(pht('Page Size'))
->setValue($saved->getParameter('limit', 100)));
}
}
protected function getURI($path) {
if ($this->baseURI) {
return $this->baseURI.$path;
}
return '/maniphest/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array();
if ($this->requireViewer()->isLoggedIn()) {
$names['assigned'] = pht('Assigned');
$names['authored'] = pht('Authored');
$names['subscribed'] = pht('Subscribed');
}
$names['open'] = pht('Open Tasks');
$names['all'] = pht('All Tasks');
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
$viewer_phid = $this->requireViewer()->getPHID();
switch ($query_key) {
case 'all':
return $query;
case 'assigned':
return $query
->setParameter('assignedPHIDs', array($viewer_phid))
->setParameter(
'statuses',
ManiphestTaskStatus::getOpenStatusConstants());
case 'subscribed':
return $query
->setParameter('subscriberPHIDs', array($viewer_phid))
->setParameter(
'statuses',
ManiphestTaskStatus::getOpenStatusConstants());
case 'open':
return $query
->setParameter(
'statuses',
ManiphestTaskStatus::getOpenStatusConstants());
case 'authored':
return $query
->setParameter('authorPHIDs', array($viewer_phid))
->setParameter('order', 'created')
->setParameter('group', 'none');
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
private function getOrderOptions() {
return array(
'priority' => pht('Priority'),
'updated' => pht('Date Updated'),
'created' => pht('Date Created'),
'title' => pht('Title'),
);
}
private function getOrderValues() {
return array(
'priority' => ManiphestTaskQuery::ORDER_PRIORITY,
'updated' => ManiphestTaskQuery::ORDER_MODIFIED,
'created' => ManiphestTaskQuery::ORDER_CREATED,
'title' => ManiphestTaskQuery::ORDER_TITLE,
);
}
private function getGroupOptions() {
return array(
'priority' => pht('Priority'),
'assigned' => pht('Assigned'),
'status' => pht('Status'),
'project' => pht('Project'),
'none' => pht('None'),
);
}
private function getGroupValues() {
return array(
'priority' => ManiphestTaskQuery::GROUP_PRIORITY,
'assigned' => ManiphestTaskQuery::GROUP_OWNER,
'status' => ManiphestTaskQuery::GROUP_STATUS,
'project' => ManiphestTaskQuery::GROUP_PROJECT,
'none' => ManiphestTaskQuery::GROUP_NONE,
);
}
protected function renderResultList(
array $tasks,
PhabricatorSavedQuery $saved,
array $handles) {
$viewer = $this->requireViewer();
if ($this->isPanelContext()) {
$can_edit_priority = false;
$can_bulk_edit = false;
} else {
$can_edit_priority = PhabricatorPolicyFilter::hasCapability(
$viewer,
$this->getApplication(),
ManiphestEditPriorityCapability::CAPABILITY);
$can_bulk_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$this->getApplication(),
ManiphestBulkEditCapability::CAPABILITY);
}
return id(new ManiphestTaskResultListView())
->setUser($viewer)
->setTasks($tasks)
->setSavedQuery($saved)
->setCanEditPriority($can_edit_priority)
->setCanBatchEdit($can_bulk_edit)
->setShowBatchControls($this->showBatchControls);
}
}
diff --git a/src/applications/metamta/applicationpanel/PhabricatorMetaMTAApplicationEmailPanel.php b/src/applications/metamta/applicationpanel/PhabricatorMetaMTAApplicationEmailPanel.php
index 636b022c26..80cb3f0313 100644
--- a/src/applications/metamta/applicationpanel/PhabricatorMetaMTAApplicationEmailPanel.php
+++ b/src/applications/metamta/applicationpanel/PhabricatorMetaMTAApplicationEmailPanel.php
@@ -1,386 +1,384 @@
supportsEmailIntegration();
}
public function buildConfigurationPagePanel() {
$viewer = $this->getViewer();
$application = $this->getApplication();
$addresses = id(new PhabricatorMetaMTAApplicationEmailQuery())
->setViewer($viewer)
->withApplicationPHIDs(array($application->getPHID()))
->execute();
$rows = array();
foreach ($addresses as $address) {
$rows[] = array(
$address->getAddress(),
);
}
$table = id(new AphrontTableView($rows))
->setNoDataString(pht('No email addresses configured.'))
->setHeaders(
array(
pht('Address'),
));
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$application,
PhabricatorPolicyCapability::CAN_EDIT);
$header = id(new PHUIHeaderView())
->setHeader(pht('Application Emails'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setText(pht('Edit Application Emails'))
->setIcon(
id(new PHUIIconView())
->setIconFont('fa-pencil'))
->setHref($this->getPanelURI())
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($table);
return $box;
}
public function handlePanelRequest(
AphrontRequest $request,
PhabricatorController $controller) {
$viewer = $request->getViewer();
$application = $this->getApplication();
$path = $request->getURIData('path');
if (strlen($path)) {
return new Aphront404Response();
}
$uri = $request->getRequestURI();
$uri->setQueryParams(array());
$new = $request->getStr('new');
$edit = $request->getInt('edit');
$delete = $request->getInt('delete');
if ($new) {
return $this->returnNewAddressResponse($request, $uri, $application);
}
if ($edit) {
return $this->returnEditAddressResponse($request, $uri, $edit);
}
if ($delete) {
return $this->returnDeleteAddressResponse($request, $uri, $delete);
}
$emails = id(new PhabricatorMetaMTAApplicationEmailQuery())
->setViewer($viewer)
->withApplicationPHIDs(array($application->getPHID()))
->execute();
$highlight = $request->getInt('highlight');
$rowc = array();
$rows = array();
foreach ($emails as $email) {
$button_edit = javelin_tag(
'a',
array(
'class' => 'button small grey',
'href' => $uri->alter('edit', $email->getID()),
'sigil' => 'workflow',
),
pht('Edit'));
$button_remove = javelin_tag(
'a',
array(
'class' => 'button small grey',
'href' => $uri->alter('delete', $email->getID()),
'sigil' => 'workflow',
),
pht('Delete'));
if ($highlight == $email->getID()) {
$rowc[] = 'highlighted';
} else {
$rowc[] = null;
}
$rows[] = array(
$email->getAddress(),
$button_edit,
$button_remove,
);
}
$table = id(new AphrontTableView($rows))
->setNoDataString(pht('No application emails created yet.'));
$table->setHeaders(
array(
pht('Email'),
pht('Edit'),
pht('Delete'),
));
$table->setColumnClasses(
array(
'wide',
'action',
'action',
));
$table->setRowClasses($rowc);
$table->setColumnVisibility(
array(
true,
true,
true,
));
$form = id(new AphrontFormView())
->setUser($viewer);
$crumbs = $controller->buildPanelCrumbs($this);
$crumbs->addTextCrumb(pht('Edit Application Emails'));
$header = id(new PHUIHeaderView())
->setHeader(pht('Edit Application Emails: %s', $application->getName()))
->setSubheader($application->getAppEmailBlurb());
$icon = id(new PHUIIconView())
->setIconFont('fa-plus');
$button = new PHUIButtonView();
$button->setText(pht('Add New Address'));
$button->setTag('a');
$button->setHref($uri->alter('new', 'true'));
$button->setIcon($icon);
$button->addSigil('workflow');
$header->addActionLink($button);
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($table);
$title = $application->getName();
return $controller->buildPanelPage(
$this,
array(
$crumbs,
$object_box,
),
array(
'title' => $title,
));
}
private function validateApplicationEmail($email) {
$errors = array();
$e_email = true;
if (!strlen($email)) {
$e_email = pht('Required');
$errors[] = pht('Email is required.');
} else if (!PhabricatorUserEmail::isValidAddress($email)) {
$e_email = pht('Invalid');
$errors[] = PhabricatorUserEmail::describeValidAddresses();
} else if (!PhabricatorUserEmail::isAllowedAddress($email)) {
$e_email = pht('Disallowed');
$errors[] = PhabricatorUserEmail::describeAllowedAddresses();
}
$user_emails = id(new PhabricatorUserEmail())
->loadAllWhere('address = %s', $email);
if ($user_emails) {
$e_email = pht('Duplicate');
$errors[] = pht('A user already has this email.');
}
return array($e_email, $errors);
}
private function returnNewAddressResponse(
AphrontRequest $request,
PhutilURI $uri,
PhabricatorApplication $application) {
$viewer = $request->getUser();
$email_object =
PhabricatorMetaMTAApplicationEmail::initializeNewAppEmail($viewer)
->setApplicationPHID($application->getPHID());
return $this->returnSaveAddressResponse(
$request,
$uri,
$email_object,
$is_new = true);
}
private function returnEditAddressResponse(
AphrontRequest $request,
PhutilURI $uri,
$email_object_id) {
$viewer = $request->getUser();
$email_object = id(new PhabricatorMetaMTAApplicationEmailQuery())
->setViewer($viewer)
->withIDs(array($email_object_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$email_object) {
return new Aphront404Response();
}
return $this->returnSaveAddressResponse(
$request,
$uri,
$email_object,
$is_new = false);
}
private function returnSaveAddressResponse(
AphrontRequest $request,
PhutilURI $uri,
PhabricatorMetaMTAApplicationEmail $email_object,
$is_new) {
$viewer = $request->getUser();
$e_email = true;
$email = null;
$errors = array();
$default_user_key =
PhabricatorMetaMTAApplicationEmail::CONFIG_DEFAULT_AUTHOR;
if ($request->isDialogFormPost()) {
$email = trim($request->getStr('email'));
list($e_email, $errors) = $this->validateApplicationEmail($email);
$email_object->setAddress($email);
$default_user = $request->getArr($default_user_key);
$default_user = reset($default_user);
if ($default_user) {
$email_object->setConfigValue($default_user_key, $default_user);
}
if (!$errors) {
try {
$email_object->save();
return id(new AphrontRedirectResponse())->setURI(
$uri->alter('highlight', $email_object->getID()));
} catch (AphrontDuplicateKeyQueryException $ex) {
$e_email = pht('Duplicate');
$errors[] = pht(
'Another application is already configured to use this email '.
'address.');
}
}
}
if ($errors) {
$errors = id(new PHUIInfoView())
->setErrors($errors);
}
$default_user = $email_object->getConfigValue($default_user_key);
if ($default_user) {
- $default_user_handle = id(new PhabricatorHandleQuery())
- ->setViewer($viewer)
- ->withPHIDs(array($default_user))
- ->execute();
+ $default_user_value = array($default_user);
} else {
- $default_user_handle = array();
+ $default_user_value = array();
}
- $form = id(new PHUIFormLayoutView())
+ $form = id(new AphrontFormView())
+ ->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Email'))
->setName('email')
->setValue($email_object->getAddress())
->setCaption(PhabricatorUserEmail::describeAllowedAddresses())
->setError($e_email))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setLabel(pht('Default Author'))
->setName($default_user_key)
->setLimit(1)
- ->setValue($default_user_handle)
+ ->setValue($default_user_value)
->setCaption(pht(
'Used if the "From:" address does not map to a known account.')));
if ($is_new) {
$title = pht('New Address');
} else {
$title = pht('Edit Address');
}
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->setWidth(AphrontDialogView::WIDTH_FORM)
->setTitle($title)
->appendChild($errors)
- ->appendChild($form)
+ ->appendForm($form)
->addSubmitButton(pht('Save'))
->addCancelButton($uri);
if ($is_new) {
$dialog->addHiddenInput('new', 'true');
}
return id(new AphrontDialogResponse())->setDialog($dialog);
}
private function returnDeleteAddressResponse(
AphrontRequest $request,
PhutilURI $uri,
$email_object_id) {
$viewer = $request->getUser();
$email_object = id(new PhabricatorMetaMTAApplicationEmailQuery())
->setViewer($viewer)
->withIDs(array($email_object_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$email_object) {
return new Aphront404Response();
}
if ($request->isDialogFormPost()) {
$email_object->delete();
return id(new AphrontRedirectResponse())->setURI($uri);
}
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->addHiddenInput('delete', $email_object_id)
->setTitle(pht('Delete Address'))
->appendParagraph(pht(
'Are you sure you want to delete this email address?'))
->addSubmitButton(pht('Delete'))
->addCancelButton($uri);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
}
diff --git a/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php b/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php
index 634debc43e..bf4fdc5cf6 100644
--- a/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php
+++ b/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php
@@ -1,118 +1,114 @@
setParameter(
'creatorPHIDs',
$this->readUsersFromRequest($request, 'creators'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorOAuthServerClientQuery());
$creator_phids = $saved->getParameter('creatorPHIDs', array());
if ($creator_phids) {
$query->withCreatorPHIDs($saved->getParameter('creatorPHIDs', array()));
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
- $phids = $saved_query->getParameter('creatorPHIDs', array());
- $creator_handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($phids)
- ->execute();
+ $creator_phids = $saved_query->getParameter('creatorPHIDs', array());
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('creators')
->setLabel(pht('Creators'))
- ->setValue($creator_handles));
+ ->setValue($creator_phids));
}
protected function getURI($path) {
return '/oauthserver/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array();
if ($this->requireViewer()->isLoggedIn()) {
$names['created'] = pht('Created');
}
$names['all'] = pht('All Applications');
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
case 'created':
return $query->setParameter(
'creatorPHIDs',
array($this->requireViewer()->getPHID()));
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function getRequiredHandlePHIDsForResultList(
array $clients,
PhabricatorSavedQuery $query) {
return mpull($clients, 'getCreatorPHID');
}
protected function renderResultList(
array $clients,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($clients, 'PhabricatorOauthServerClient');
$viewer = $this->requireViewer();
$list = id(new PHUIObjectItemListView())
->setUser($viewer);
foreach ($clients as $client) {
$creator = $handles[$client->getCreatorPHID()];
$item = id(new PHUIObjectItemView())
->setObjectName(pht('Application %d', $client->getID()))
->setHeader($client->getName())
->setHref($client->getViewURI())
->setObject($client)
->addByline(pht('Creator: %s', $creator->renderLink()));
$list->addItem($item);
}
return $list;
}
}
diff --git a/src/applications/owners/controller/PhabricatorOwnersEditController.php b/src/applications/owners/controller/PhabricatorOwnersEditController.php
index 249b461fbb..34bf513737 100644
--- a/src/applications/owners/controller/PhabricatorOwnersEditController.php
+++ b/src/applications/owners/controller/PhabricatorOwnersEditController.php
@@ -1,281 +1,277 @@
id = idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
if ($this->id) {
$package = id(new PhabricatorOwnersPackage())->load($this->id);
if (!$package) {
return new Aphront404Response();
}
} else {
$package = new PhabricatorOwnersPackage();
$package->setPrimaryOwnerPHID($user->getPHID());
}
$e_name = true;
$e_primary = true;
$errors = array();
if ($request->isFormPost()) {
$package->setName($request->getStr('name'));
$package->setDescription($request->getStr('description'));
$old_auditing_enabled = $package->getAuditingEnabled();
$package->setAuditingEnabled(
($request->getStr('auditing') === 'enabled')
? 1
: 0);
$primary = $request->getArr('primary');
$primary = reset($primary);
$old_primary = $package->getPrimaryOwnerPHID();
$package->setPrimaryOwnerPHID($primary);
$owners = $request->getArr('owners');
if ($primary) {
array_unshift($owners, $primary);
}
$owners = array_unique($owners);
$paths = $request->getArr('path');
$repos = $request->getArr('repo');
$excludes = $request->getArr('exclude');
$path_refs = array();
for ($ii = 0; $ii < count($paths); $ii++) {
if (empty($paths[$ii]) || empty($repos[$ii])) {
continue;
}
$path_refs[] = array(
'repositoryPHID' => $repos[$ii],
'path' => $paths[$ii],
'excluded' => $excludes[$ii],
);
}
if (!strlen($package->getName())) {
$e_name = pht('Required');
$errors[] = pht('Package name is required.');
} else {
$e_name = null;
}
if (!$package->getPrimaryOwnerPHID()) {
$e_primary = pht('Required');
$errors[] = pht('Package must have a primary owner.');
} else {
$e_primary = null;
}
if (!$path_refs) {
$errors[] = pht('Package must include at least one path.');
}
if (!$errors) {
$package->attachUnsavedOwners($owners);
$package->attachUnsavedPaths($path_refs);
$package->attachOldAuditingEnabled($old_auditing_enabled);
$package->attachOldPrimaryOwnerPHID($old_primary);
try {
id(new PhabricatorOwnersPackageEditor())
->setActor($user)
->setPackage($package)
->save();
return id(new AphrontRedirectResponse())
->setURI('/owners/package/'.$package->getID().'/');
} catch (AphrontDuplicateKeyQueryException $ex) {
$e_name = pht('Duplicate');
$errors[] = pht('Package name must be unique.');
}
}
} else {
$owners = $package->loadOwners();
$owners = mpull($owners, 'getUserPHID');
$paths = $package->loadPaths();
$path_refs = array();
foreach ($paths as $path) {
$path_refs[] = array(
'repositoryPHID' => $path->getRepositoryPHID(),
'path' => $path->getPath(),
'excluded' => $path->getExcluded(),
);
}
}
- $handles = $this->loadViewerHandles($owners);
-
$primary = $package->getPrimaryOwnerPHID();
- if ($primary && isset($handles[$primary])) {
- $handle_primary_owner = array($handles[$primary]);
+ if ($primary) {
+ $value_primary_owner = array($primary);
} else {
- $handle_primary_owner = array();
+ $value_primary_owner = array();
}
- $handles_all_owners = array_select_keys($handles, $owners);
-
if ($package->getID()) {
$title = pht('Edit Package');
$side_nav_filter = 'edit/'.$this->id;
} else {
$title = pht('New Package');
$side_nav_filter = 'new';
}
$this->setSideNavFilter($side_nav_filter);
$repos = id(new PhabricatorRepositoryQuery())
->setViewer($user)
->execute();
$default_paths = array();
foreach ($repos as $repo) {
$default_path = $repo->getDetail('default-owners-path');
if ($default_path) {
$default_paths[$repo->getPHID()] = $default_path;
}
}
$repos = mpull($repos, 'getCallsign', 'getPHID');
asort($repos);
$template = new AphrontTypeaheadTemplateView();
$template = $template->render();
Javelin::initBehavior(
'owners-path-editor',
array(
'root' => 'path-editor',
'table' => 'paths',
'add_button' => 'addpath',
'repositories' => $repos,
'input_template' => $template,
'pathRefs' => $path_refs,
'completeURI' => '/diffusion/services/path/complete/',
'validateURI' => '/diffusion/services/path/validate/',
'repositoryDefaultPaths' => $default_paths,
));
require_celerity_resource('owners-path-editor-css');
$cancel_uri = $package->getID()
? '/owners/package/'.$package->getID().'/'
: '/owners/';
$form = id(new AphrontFormView())
->setUser($user)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Name'))
->setName('name')
->setValue($package->getName())
->setError($e_name))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorProjectOrUserDatasource())
->setLabel(pht('Primary Owner'))
->setName('primary')
->setLimit(1)
- ->setValue($handle_primary_owner)
+ ->setValue($value_primary_owner)
->setError($e_primary))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorProjectOrUserDatasource())
->setLabel(pht('Owners'))
->setName('owners')
- ->setValue($handles_all_owners))
+ ->setValue($owners))
->appendChild(
id(new AphrontFormSelectControl())
->setName('auditing')
->setLabel(pht('Auditing'))
->setCaption(
pht('With auditing enabled, all future commits that touch '.
'this package will be reviewed to make sure an owner '.
'of the package is involved and the commit message has '.
'a valid revision, reviewed by, and author.'))
->setOptions(array(
'disabled' => pht('Disabled'),
'enabled' => pht('Enabled'),
))
->setValue(
$package->getAuditingEnabled()
? 'enabled'
: 'disabled'))
->appendChild(
id(new PHUIFormInsetView())
->setTitle(pht('Paths'))
->addDivAttributes(array('id' => 'path-editor'))
->setRightButton(javelin_tag(
'a',
array(
'href' => '#',
'class' => 'button green',
'sigil' => 'addpath',
'mustcapture' => true,
),
pht('Add New Path')))
->setDescription(
pht('Specify the files and directories which comprise '.
'this package.'))
->setContent(javelin_tag(
'table',
array(
'class' => 'owners-path-editor-table',
'sigil' => 'paths',
),
'')))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel(pht('Description'))
->setName('description')
->setValue($package->getDescription()))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($cancel_uri)
->setValue(pht('Save Package')));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setFormErrors($errors)
->setForm($form);
$crumbs = $this->buildApplicationCrumbs();
if ($package->getID()) {
$crumbs->addTextCrumb(pht('Edit %s', $package->getName()));
} else {
$crumbs->addTextCrumb(pht('New Package'));
}
$nav = $this->buildSideNavView();
$nav->appendChild($crumbs);
$nav->appendChild($form_box);
return $this->buildApplicationPage(
array(
$nav,
),
array(
'title' => $title,
));
}
protected function getExtraPackageViews(AphrontSideNavFilterView $view) {
if ($this->id) {
$view->addFilter('edit/'.$this->id, pht('Edit'));
} else {
$view->addFilter('new', pht('New'));
}
}
}
diff --git a/src/applications/owners/controller/PhabricatorOwnersListController.php b/src/applications/owners/controller/PhabricatorOwnersListController.php
index 0d9041df80..8a3a1e8ec3 100644
--- a/src/applications/owners/controller/PhabricatorOwnersListController.php
+++ b/src/applications/owners/controller/PhabricatorOwnersListController.php
@@ -1,349 +1,343 @@
view = idx($data, 'view', 'owned');
$this->setSideNavFilter('view/'.$this->view);
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$package = new PhabricatorOwnersPackage();
$owner = new PhabricatorOwnersOwner();
$path = new PhabricatorOwnersPath();
$repository_phid = '';
if ($request->getStr('repository') != '') {
$repository_phid = id(new PhabricatorRepositoryQuery())
->setViewer($user)
->withCallsigns(array($request->getStr('repository')))
->executeOne()
->getPHID();
}
switch ($this->view) {
case 'search':
$packages = array();
$conn_r = $package->establishConnection('r');
$where = array('1 = 1');
$join = array();
$having = '';
if ($request->getStr('name')) {
$where[] = qsprintf(
$conn_r,
'p.name LIKE %~',
$request->getStr('name'));
}
if ($repository_phid || $request->getStr('path')) {
$join[] = qsprintf(
$conn_r,
'JOIN %T path ON path.packageID = p.id',
$path->getTableName());
if ($repository_phid) {
$where[] = qsprintf(
$conn_r,
'path.repositoryPHID = %s',
$repository_phid);
}
if ($request->getStr('path')) {
$where[] = qsprintf(
$conn_r,
'(path.path LIKE %~ AND NOT path.excluded) OR
%s LIKE CONCAT(REPLACE(path.path, %s, %s), %s)',
$request->getStr('path'),
$request->getStr('path'),
'_',
'\_',
'%');
$having = 'HAVING MAX(path.excluded) = 0';
}
}
if ($request->getArr('owner')) {
$join[] = qsprintf(
$conn_r,
'JOIN %T o ON o.packageID = p.id',
$owner->getTableName());
$where[] = qsprintf(
$conn_r,
'o.userPHID IN (%Ls)',
$request->getArr('owner'));
}
$data = queryfx_all(
$conn_r,
'SELECT p.* FROM %T p %Q WHERE %Q GROUP BY p.id %Q',
$package->getTableName(),
implode(' ', $join),
'('.implode(') AND (', $where).')',
$having);
$packages = $package->loadAllFromArray($data);
$header = pht('Search Results');
$nodata = pht('No packages match your query.');
break;
case 'owned':
$data = queryfx_all(
$package->establishConnection('r'),
'SELECT p.* FROM %T p JOIN %T o ON p.id = o.packageID
WHERE o.userPHID = %s GROUP BY p.id',
$package->getTableName(),
$owner->getTableName(),
$user->getPHID());
$packages = $package->loadAllFromArray($data);
$header = pht('Owned Packages');
$nodata = pht('No owned packages');
break;
case 'projects':
$projects = id(new PhabricatorProjectQuery())
->setViewer($user)
->withMemberPHIDs(array($user->getPHID()))
->withStatus(PhabricatorProjectQuery::STATUS_ANY)
->execute();
$owner_phids = mpull($projects, 'getPHID');
if ($owner_phids) {
$data = queryfx_all(
$package->establishConnection('r'),
'SELECT p.* FROM %T p JOIN %T o ON p.id = o.packageID
WHERE o.userPHID IN (%Ls) GROUP BY p.id',
$package->getTableName(),
$owner->getTableName(),
$owner_phids);
} else {
$data = array();
}
$packages = $package->loadAllFromArray($data);
$header = pht('Project Packages');
$nodata = pht('No owned packages');
break;
case 'all':
$packages = $package->loadAll();
$header = pht('All Packages');
$nodata = pht('There are no defined packages.');
break;
}
$content = $this->renderPackageTable(
$packages,
$header,
$nodata);
$filter = new AphrontListFilterView();
- $owners_search_value = array();
- if ($request->getArr('owner')) {
- $phids = $request->getArr('owner');
- $phid = reset($phids);
- $handles = $this->loadViewerHandles(array($phid));
- $owners_search_value = array($handles[$phid]);
- }
+ $owner_phids = $request->getArr('owner');
$callsigns = array('' => pht('(Any Repository)'));
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($user)
->setOrder(PhabricatorRepositoryQuery::ORDER_CALLSIGN)
->execute();
foreach ($repositories as $repository) {
$callsigns[$repository->getCallsign()] =
$repository->getCallsign().': '.$repository->getName();
}
$form = id(new AphrontFormView())
->setUser($user)
->setAction('/owners/view/search/')
->setMethod('GET')
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setLabel(pht('Name'))
->setValue($request->getStr('name')))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorProjectOrUserDatasource())
->setLimit(1)
->setName('owner')
->setLabel(pht('Owner'))
- ->setValue($owners_search_value))
+ ->setValue($owner_phids))
->appendChild(
id(new AphrontFormSelectControl())
->setName('repository')
->setLabel(pht('Repository'))
->setOptions($callsigns)
->setValue($request->getStr('repository')))
->appendChild(
id(new AphrontFormTextControl())
->setName('path')
->setLabel(pht('Path'))
->setValue($request->getStr('path')))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Search for Packages')));
$filter->appendChild($form);
$title = pht('Package Index');
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($header);
$crumbs->setBorder(true);
$nav = $this->buildSideNavView();
$nav->appendChild($crumbs);
$nav->appendChild($filter);
$nav->appendChild($content);
return $this->buildApplicationPage(
$nav,
array(
'title' => pht('Package Index'),
));
}
private function renderPackageTable(array $packages, $header, $nodata) {
assert_instances_of($packages, 'PhabricatorOwnersPackage');
if ($packages) {
$package_ids = mpull($packages, 'getID');
$owners = id(new PhabricatorOwnersOwner())->loadAllWhere(
'packageID IN (%Ld)',
$package_ids);
$paths = id(new PhabricatorOwnersPath())->loadAllWhere(
'packageID in (%Ld)',
$package_ids);
$phids = array();
foreach ($owners as $owner) {
$phids[$owner->getUserPHID()] = true;
}
$phids = array_keys($phids);
$handles = $this->loadViewerHandles($phids);
$repository_phids = array();
foreach ($paths as $path) {
$repository_phids[$path->getRepositoryPHID()] = true;
}
if ($repository_phids) {
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($this->getRequest()->getUser())
->withPHIDs(array_keys($repository_phids))
->execute();
} else {
$repositories = array();
}
$repositories = mpull($repositories, null, 'getPHID');
$owners = mgroup($owners, 'getPackageID');
$paths = mgroup($paths, 'getPackageID');
} else {
$handles = array();
$repositories = array();
$owners = array();
$paths = array();
}
$rows = array();
foreach ($packages as $package) {
$pkg_owners = idx($owners, $package->getID(), array());
foreach ($pkg_owners as $key => $owner) {
$pkg_owners[$key] = $handles[$owner->getUserPHID()]->renderLink();
if ($owner->getUserPHID() == $package->getPrimaryOwnerPHID()) {
$pkg_owners[$key] = phutil_tag('strong', array(), $pkg_owners[$key]);
}
}
$pkg_owners = phutil_implode_html(phutil_tag('br'), $pkg_owners);
$pkg_paths = idx($paths, $package->getID(), array());
foreach ($pkg_paths as $key => $path) {
$repo = idx($repositories, $path->getRepositoryPHID());
if ($repo) {
$href = DiffusionRequest::generateDiffusionURI(
array(
'callsign' => $repo->getCallsign(),
'branch' => $repo->getDefaultBranch(),
'path' => $path->getPath(),
'action' => 'browse',
));
$pkg_paths[$key] = hsprintf(
'%s %s%s',
($path->getExcluded() ? "\xE2\x80\x93" : '+'),
phutil_tag('strong', array(), $repo->getName()),
phutil_tag(
'a',
array(
'href' => (string) $href,
),
$path->getPath()));
} else {
$pkg_paths[$key] = $path->getPath();
}
}
$pkg_paths = phutil_implode_html(phutil_tag('br'), $pkg_paths);
$rows[] = array(
phutil_tag(
'a',
array(
'href' => '/owners/package/'.$package->getID().'/',
),
$package->getName()),
$pkg_owners,
$pkg_paths,
phutil_tag(
'a',
array(
'href' => '/audit/?auditorPHIDs='.$package->getPHID(),
),
pht('Related Commits')),
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
pht('Name'),
pht('Owners'),
pht('Paths'),
pht('Related Commits'),
));
$table->setColumnClasses(
array(
'pri',
'',
'wide wrap',
'narrow',
));
$panel = new PHUIObjectBoxView();
$panel->setHeaderText($header);
$panel->appendChild($table);
return $panel;
}
protected function getExtraPackageViews(AphrontSideNavFilterView $view) {
if ($this->view == 'search') {
$view->addFilter('view/search', pht('Search Results'));
}
}
}
diff --git a/src/applications/paste/controller/PhabricatorPasteEditController.php b/src/applications/paste/controller/PhabricatorPasteEditController.php
index 19652217b8..32cc465737 100644
--- a/src/applications/paste/controller/PhabricatorPasteEditController.php
+++ b/src/applications/paste/controller/PhabricatorPasteEditController.php
@@ -1,250 +1,244 @@
id = idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$parent = null;
$parent_id = null;
if (!$this->id) {
$is_create = true;
$paste = PhabricatorPaste::initializeNewPaste($user);
$parent_id = $request->getStr('parent');
if ($parent_id) {
// NOTE: If the Paste is forked from a paste which the user no longer
// has permission to see, we still let them edit it.
$parent = id(new PhabricatorPasteQuery())
->setViewer($user)
->withIDs(array($parent_id))
->needContent(true)
->needRawContent(true)
->execute();
$parent = head($parent);
if ($parent) {
$paste->setParentPHID($parent->getPHID());
$paste->setViewPolicy($parent->getViewPolicy());
}
}
$paste->setAuthorPHID($user->getPHID());
$paste->attachRawContent('');
} else {
$is_create = false;
$paste = id(new PhabricatorPasteQuery())
->setViewer($user)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->withIDs(array($this->id))
->needRawContent(true)
->executeOne();
if (!$paste) {
return new Aphront404Response();
}
}
$text = null;
$e_text = true;
$errors = array();
if ($is_create && $parent) {
$v_title = pht('Fork of %s', $parent->getFullName());
$v_language = $parent->getLanguage();
$v_text = $parent->getRawContent();
} else {
$v_title = $paste->getTitle();
$v_language = $paste->getLanguage();
$v_text = $paste->getRawContent();
}
$v_view_policy = $paste->getViewPolicy();
$v_edit_policy = $paste->getEditPolicy();
if ($is_create) {
$v_projects = array();
} else {
$v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
$paste->getPHID(),
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
$v_projects = array_reverse($v_projects);
}
if ($request->isFormPost()) {
$xactions = array();
$v_text = $request->getStr('text');
if (!strlen($v_text)) {
$e_text = pht('Required');
$errors[] = pht('The paste may not be blank.');
} else {
$e_text = null;
}
$v_title = $request->getStr('title');
$v_language = $request->getStr('language');
$v_view_policy = $request->getStr('can_view');
$v_edit_policy = $request->getStr('can_edit');
$v_projects = $request->getArr('projects');
// NOTE: The author is the only editor and can always view the paste,
// so it's impossible for them to choose an invalid policy.
if (!$errors) {
if ($is_create || ($v_text !== $paste->getRawContent())) {
$file = PhabricatorPasteEditor::initializeFileForPaste(
$user,
$v_title,
$v_text);
$xactions[] = id(new PhabricatorPasteTransaction())
->setTransactionType(PhabricatorPasteTransaction::TYPE_CONTENT)
->setNewValue($file->getPHID());
}
$xactions[] = id(new PhabricatorPasteTransaction())
->setTransactionType(PhabricatorPasteTransaction::TYPE_TITLE)
->setNewValue($v_title);
$xactions[] = id(new PhabricatorPasteTransaction())
->setTransactionType(PhabricatorPasteTransaction::TYPE_LANGUAGE)
->setNewValue($v_language);
$xactions[] = id(new PhabricatorPasteTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
->setNewValue($v_view_policy);
$xactions[] = id(new PhabricatorPasteTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
->setNewValue($v_edit_policy);
$proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
$xactions[] = id(new PhabricatorPasteTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $proj_edge_type)
->setNewValue(array('=' => array_fuse($v_projects)));
$editor = id(new PhabricatorPasteEditor())
->setActor($user)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true);
$xactions = $editor->applyTransactions($paste, $xactions);
return id(new AphrontRedirectResponse())->setURI($paste->getURI());
} else {
// make sure we update policy so its correctly populated to what
// the user chose
$paste->setViewPolicy($v_view_policy);
$paste->setEditPolicy($v_edit_policy);
}
}
$form = new AphrontFormView();
$langs = array(
'' => pht('(Detect From Filename in Title)'),
) + PhabricatorEnv::getEnvConfig('pygments.dropdown-choices');
$form
->setUser($user)
->addHiddenInput('parent', $parent_id)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Title'))
->setValue($v_title)
->setName('title'))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Language'))
->setName('language')
->setValue($v_language)
->setOptions($langs));
$policies = id(new PhabricatorPolicyQuery())
->setViewer($user)
->setObject($paste)
->execute();
$form->appendChild(
id(new AphrontFormPolicyControl())
->setUser($user)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicyObject($paste)
->setPolicies($policies)
->setName('can_view'));
$form->appendChild(
id(new AphrontFormPolicyControl())
->setUser($user)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicyObject($paste)
->setPolicies($policies)
->setName('can_edit'));
- if ($v_projects) {
- $project_handles = $this->loadViewerHandles($v_projects);
- } else {
- $project_handles = array();
- }
-
- $form->appendChild(
+ $form->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Projects'))
->setName('projects')
- ->setValue($project_handles)
+ ->setValue($v_projects)
->setDatasource(new PhabricatorProjectDatasource()));
$form
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel(pht('Text'))
->setError($e_text)
->setValue($v_text)
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
->setCustomClass('PhabricatorMonospaced')
->setName('text'));
$submit = new AphrontFormSubmitControl();
if (!$is_create) {
$submit->addCancelButton($paste->getURI());
$submit->setValue(pht('Save Paste'));
$title = pht('Edit %s', $paste->getFullName());
$short = pht('Edit');
} else {
$submit->setValue(pht('Create Paste'));
$title = pht('Create New Paste');
$short = pht('Create');
}
$form->appendChild($submit);
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setFormErrors($errors)
->setForm($form);
$crumbs = $this->buildApplicationCrumbs($this->buildSideNavView());
if (!$is_create) {
$crumbs->addTextCrumb('P'.$paste->getID(), '/P'.$paste->getID());
}
$crumbs->addTextCrumb($short);
return $this->buildApplicationPage(
array(
$crumbs,
$form_box,
),
array(
'title' => $title,
));
}
}
diff --git a/src/applications/paste/query/PhabricatorPasteSearchEngine.php b/src/applications/paste/query/PhabricatorPasteSearchEngine.php
index d2d677ea5a..45dad0799b 100644
--- a/src/applications/paste/query/PhabricatorPasteSearchEngine.php
+++ b/src/applications/paste/query/PhabricatorPasteSearchEngine.php
@@ -1,199 +1,195 @@
setParameter(
'authorPHIDs',
$this->readUsersFromRequest($request, 'authors'));
$languages = $request->getStrList('languages');
if ($request->getBool('noLanguage')) {
$languages[] = null;
}
$saved->setParameter('languages', $languages);
$saved->setParameter('createdStart', $request->getStr('createdStart'));
$saved->setParameter('createdEnd', $request->getStr('createdEnd'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorPasteQuery())
->needContent(true)
->withAuthorPHIDs($saved->getParameter('authorPHIDs', array()))
->withLanguages($saved->getParameter('languages', array()));
$start = $this->parseDateTime($saved->getParameter('createdStart'));
$end = $this->parseDateTime($saved->getParameter('createdEnd'));
if ($start) {
$query->withDateCreatedAfter($start);
}
if ($end) {
$query->withDateCreatedBefore($end);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
- $phids = $saved_query->getParameter('authorPHIDs', array());
- $author_handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($phids)
- ->execute();
+ $author_phids = $saved_query->getParameter('authorPHIDs', array());
$languages = $saved_query->getParameter('languages', array());
$no_language = false;
foreach ($languages as $key => $language) {
if ($language === null) {
$no_language = true;
unset($languages[$key]);
continue;
}
}
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('authors')
->setLabel(pht('Authors'))
- ->setValue($author_handles))
+ ->setValue($author_phids))
->appendChild(
id(new AphrontFormTextControl())
->setName('languages')
->setLabel(pht('Languages'))
->setValue(implode(', ', $languages)))
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'noLanguage',
1,
pht('Find Pastes with no specified language.'),
$no_language));
$this->buildDateRange(
$form,
$saved_query,
'createdStart',
pht('Created After'),
'createdEnd',
pht('Created Before'));
}
protected function getURI($path) {
return '/paste/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'all' => pht('All Pastes'),
);
if ($this->requireViewer()->isLoggedIn()) {
$names['authored'] = pht('Authored');
}
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
case 'authored':
return $query->setParameter(
'authorPHIDs',
array($this->requireViewer()->getPHID()));
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function getRequiredHandlePHIDsForResultList(
array $pastes,
PhabricatorSavedQuery $query) {
return mpull($pastes, 'getAuthorPHID');
}
protected function renderResultList(
array $pastes,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($pastes, 'PhabricatorPaste');
$viewer = $this->requireViewer();
$lang_map = PhabricatorEnv::getEnvConfig('pygments.dropdown-choices');
$list = new PHUIObjectItemListView();
$list->setUser($viewer);
foreach ($pastes as $paste) {
$created = phabricator_date($paste->getDateCreated(), $viewer);
$author = $handles[$paste->getAuthorPHID()]->renderLink();
$lines = phutil_split_lines($paste->getContent());
$preview = id(new PhabricatorSourceCodeView())
->setLimit(5)
->setLines($lines)
->setURI(new PhutilURI($paste->getURI()));
$source_code = phutil_tag(
'div',
array(
'class' => 'phabricator-source-code-summary',
),
$preview);
$created = phabricator_datetime($paste->getDateCreated(), $viewer);
$line_count = count($lines);
$line_count = pht(
'%s Line(s)',
new PhutilNumber($line_count));
$title = nonempty($paste->getTitle(), pht('(An Untitled Masterwork)'));
$item = id(new PHUIObjectItemView())
->setObjectName('P'.$paste->getID())
->setHeader($title)
->setHref('/P'.$paste->getID())
->setObject($paste)
->addByline(pht('Author: %s', $author))
->addIcon('none', $created)
->addIcon('none', $line_count)
->appendChild($source_code);
$lang_name = $paste->getLanguage();
if ($lang_name) {
$lang_name = idx($lang_map, $lang_name, $lang_name);
$item->addIcon('none', $lang_name);
}
$list->addItem($item);
}
return $list;
}
}
diff --git a/src/applications/people/query/PhabricatorPeopleLogSearchEngine.php b/src/applications/people/query/PhabricatorPeopleLogSearchEngine.php
index 8e75efed6c..b98e29840b 100644
--- a/src/applications/people/query/PhabricatorPeopleLogSearchEngine.php
+++ b/src/applications/people/query/PhabricatorPeopleLogSearchEngine.php
@@ -1,209 +1,193 @@
setParameter(
'userPHIDs',
$this->readUsersFromRequest($request, 'users'));
$saved->setParameter(
'actorPHIDs',
$this->readUsersFromRequest($request, 'actors'));
$saved->setParameter(
'actions',
$this->readListFromRequest($request, 'actions'));
$saved->setParameter(
'ip',
$request->getStr('ip'));
$saved->setParameter(
'sessions',
$this->readListFromRequest($request, 'sessions'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorPeopleLogQuery());
// NOTE: If the viewer isn't an administrator, always restrict the query to
// related records. This echoes the policy logic of these logs. This is
// mostly a performance optimization, to prevent us from having to pull
// large numbers of logs that the user will not be able to see and filter
// them in-process.
$viewer = $this->requireViewer();
if (!$viewer->getIsAdmin()) {
$query->withRelatedPHIDs(array($viewer->getPHID()));
}
$actor_phids = $saved->getParameter('actorPHIDs', array());
if ($actor_phids) {
$query->withActorPHIDs($actor_phids);
}
$user_phids = $saved->getParameter('userPHIDs', array());
if ($user_phids) {
$query->withUserPHIDs($user_phids);
}
$actions = $saved->getParameter('actions', array());
if ($actions) {
$query->withActions($actions);
}
$remote_prefix = $saved->getParameter('ip');
if (strlen($remote_prefix)) {
$query->withRemoteAddressprefix($remote_prefix);
}
$sessions = $saved->getParameter('sessions', array());
if ($sessions) {
$query->withSessionKeys($sessions);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved) {
$actor_phids = $saved->getParameter('actorPHIDs', array());
$user_phids = $saved->getParameter('userPHIDs', array());
- $all_phids = array_merge(
- $actor_phids,
- $user_phids);
-
- if ($all_phids) {
- $handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($all_phids)
- ->execute();
- } else {
- $handles = array();
- }
-
- $actor_handles = array_select_keys($handles, $actor_phids);
- $user_handles = array_select_keys($handles, $user_phids);
-
$actions = $saved->getParameter('actions', array());
$remote_prefix = $saved->getParameter('ip');
$sessions = $saved->getParameter('sessions', array());
$actions = array_fuse($actions);
$action_control = id(new AphrontFormCheckboxControl())
->setLabel(pht('Actions'));
$action_types = PhabricatorUserLog::getActionTypeMap();
foreach ($action_types as $type => $label) {
$action_control->addCheckbox(
'actions[]',
$type,
$label,
isset($actions[$label]));
}
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('actors')
->setLabel(pht('Actors'))
- ->setValue($actor_handles))
- ->appendChild(
+ ->setValue($actor_phids))
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('users')
->setLabel(pht('Users'))
- ->setValue($user_handles))
+ ->setValue($user_phids))
->appendChild($action_control)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Filter IP'))
->setName('ip')
->setValue($remote_prefix))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Sessions'))
->setName('sessions')
->setValue(implode(', ', $sessions)));
}
protected function getURI($path) {
return '/people/logs/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'all' => pht('All'),
);
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 getRequiredHandlePHIDsForResultList(
array $logs,
PhabricatorSavedQuery $query) {
$phids = array();
foreach ($logs as $log) {
$phids[$log->getActorPHID()] = true;
$phids[$log->getUserPHID()] = true;
}
return array_keys($phids);
}
protected function renderResultList(
array $logs,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($logs, 'PhabricatorUserLog');
$viewer = $this->requireViewer();
$table = id(new PhabricatorUserLogView())
->setUser($viewer)
->setLogs($logs)
->setHandles($handles);
if ($viewer->getIsAdmin()) {
$table->setSearchBaseURI($this->getApplicationURI('logs/'));
}
return id(new PHUIObjectBoxView())
->setHeaderText(pht('User Activity Logs'))
->appendChild($table);
}
}
diff --git a/src/applications/pholio/controller/PholioMockEditController.php b/src/applications/pholio/controller/PholioMockEditController.php
index 7fed740bfc..357f4e49da 100644
--- a/src/applications/pholio/controller/PholioMockEditController.php
+++ b/src/applications/pholio/controller/PholioMockEditController.php
@@ -1,404 +1,393 @@
id = idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
if ($this->id) {
$mock = id(new PholioMockQuery())
->setViewer($user)
->needImages(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->withIDs(array($this->id))
->executeOne();
if (!$mock) {
return new Aphront404Response();
}
$title = pht('Edit Mock');
$is_new = false;
$mock_images = $mock->getImages();
$files = mpull($mock_images, 'getFile');
$mock_images = mpull($mock_images, null, 'getFilePHID');
} else {
$mock = PholioMock::initializeNewMock($user);
$title = pht('Create Mock');
$is_new = true;
$files = array();
$mock_images = array();
}
if ($is_new) {
$v_projects = array();
} else {
$v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
$mock->getPHID(),
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
$v_projects = array_reverse($v_projects);
}
$e_name = true;
$e_images = count($mock_images) ? null : true;
$errors = array();
$posted_mock_images = array();
$v_name = $mock->getName();
$v_desc = $mock->getDescription();
$v_status = $mock->getStatus();
$v_view = $mock->getViewPolicy();
$v_edit = $mock->getEditPolicy();
$v_cc = PhabricatorSubscribersQuery::loadSubscribersForPHID(
$mock->getPHID());
if ($request->isFormPost()) {
$xactions = array();
$type_name = PholioTransactionType::TYPE_NAME;
$type_desc = PholioTransactionType::TYPE_DESCRIPTION;
$type_status = PholioTransactionType::TYPE_STATUS;
$type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
$type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
$type_cc = PhabricatorTransactions::TYPE_SUBSCRIBERS;
$v_name = $request->getStr('name');
$v_desc = $request->getStr('description');
$v_status = $request->getStr('status');
$v_view = $request->getStr('can_view');
$v_edit = $request->getStr('can_edit');
$v_cc = $request->getArr('cc');
$v_projects = $request->getArr('projects');
$mock_xactions = array();
$mock_xactions[$type_name] = $v_name;
$mock_xactions[$type_desc] = $v_desc;
$mock_xactions[$type_status] = $v_status;
$mock_xactions[$type_view] = $v_view;
$mock_xactions[$type_edit] = $v_edit;
$mock_xactions[$type_cc] = array('=' => $v_cc);
if (!strlen($request->getStr('name'))) {
$e_name = 'Required';
$errors[] = pht('You must give the mock a name.');
}
$file_phids = $request->getArr('file_phids');
if ($file_phids) {
$files = id(new PhabricatorFileQuery())
->setViewer($user)
->withPHIDs($file_phids)
->execute();
$files = mpull($files, null, 'getPHID');
$files = array_select_keys($files, $file_phids);
} else {
$files = array();
}
if (!$files) {
$e_images = pht('Required');
$errors[] = pht('You must add at least one image to the mock.');
} else {
$mock->setCoverPHID(head($files)->getPHID());
}
foreach ($mock_xactions as $type => $value) {
$xactions[$type] = id(new PholioTransaction())
->setTransactionType($type)
->setNewValue($value);
}
$order = $request->getStrList('imageOrder');
$sequence_map = array_flip($order);
$replaces = $request->getArr('replaces');
$replaces_map = array_flip($replaces);
/**
* Foreach file posted, check to see whether we are replacing an image,
* adding an image, or simply updating image metadata. Create
* transactions for these cases as appropos.
*/
foreach ($files as $file_phid => $file) {
$replaces_image_phid = null;
if (isset($replaces_map[$file_phid])) {
$old_file_phid = $replaces_map[$file_phid];
if ($old_file_phid != $file_phid) {
$old_image = idx($mock_images, $old_file_phid);
if ($old_image) {
$replaces_image_phid = $old_image->getPHID();
}
}
}
$existing_image = idx($mock_images, $file_phid);
$title = (string)$request->getStr('title_'.$file_phid);
$description = (string)$request->getStr('description_'.$file_phid);
$sequence = $sequence_map[$file_phid];
if ($replaces_image_phid) {
$replace_image = id(new PholioImage())
->setReplacesImagePHID($replaces_image_phid)
->setFilePhid($file_phid)
->attachFile($file)
->setName(strlen($title) ? $title : $file->getName())
->setDescription($description)
->setSequence($sequence);
$xactions[] = id(new PholioTransaction())
->setTransactionType(
PholioTransactionType::TYPE_IMAGE_REPLACE)
->setNewValue($replace_image);
$posted_mock_images[] = $replace_image;
} else if (!$existing_image) { // this is an add
$add_image = id(new PholioImage())
->setFilePhid($file_phid)
->attachFile($file)
->setName(strlen($title) ? $title : $file->getName())
->setDescription($description)
->setSequence($sequence);
$xactions[] = id(new PholioTransaction())
->setTransactionType(PholioTransactionType::TYPE_IMAGE_FILE)
->setNewValue(
array('+' => array($add_image)));
$posted_mock_images[] = $add_image;
} else {
$xactions[] = id(new PholioTransaction())
->setTransactionType(PholioTransactionType::TYPE_IMAGE_NAME)
->setNewValue(
array($existing_image->getPHID() => $title));
$xactions[] = id(new PholioTransaction())
->setTransactionType(
PholioTransactionType::TYPE_IMAGE_DESCRIPTION)
->setNewValue(
array($existing_image->getPHID() => $description));
$xactions[] = id(new PholioTransaction())
->setTransactionType(
PholioTransactionType::TYPE_IMAGE_SEQUENCE)
->setNewValue(
array($existing_image->getPHID() => $sequence));
$posted_mock_images[] = $existing_image;
}
}
foreach ($mock_images as $file_phid => $mock_image) {
if (!isset($files[$file_phid]) && !isset($replaces[$file_phid])) {
// this is an outright delete
$xactions[] = id(new PholioTransaction())
->setTransactionType(PholioTransactionType::TYPE_IMAGE_FILE)
->setNewValue(
array('-' => array($mock_image)));
}
}
if (!$errors) {
$proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
$xactions[] = id(new PholioTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $proj_edge_type)
->setNewValue(array('=' => array_fuse($v_projects)));
$mock->openTransaction();
$editor = id(new PholioMockEditor())
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setActor($user);
$xactions = $editor->applyTransactions($mock, $xactions);
$mock->saveTransaction();
return id(new AphrontRedirectResponse())
->setURI('/M'.$mock->getID());
}
}
if ($this->id) {
$submit = id(new AphrontFormSubmitControl())
->addCancelButton('/M'.$this->id)
->setValue(pht('Save'));
} else {
$submit = id(new AphrontFormSubmitControl())
->addCancelButton($this->getApplicationURI())
->setValue(pht('Create'));
}
$policies = id(new PhabricatorPolicyQuery())
->setViewer($user)
->setObject($mock)
->execute();
// NOTE: Make this show up correctly on the rendered form.
$mock->setViewPolicy($v_view);
$mock->setEditPolicy($v_edit);
- $handles = id(new PhabricatorHandleQuery())
- ->setViewer($user)
- ->withPHIDs($v_cc)
- ->execute();
-
$image_elements = array();
if ($posted_mock_images) {
$display_mock_images = $posted_mock_images;
} else {
$display_mock_images = $mock_images;
}
foreach ($display_mock_images as $mock_image) {
$image_elements[] = id(new PholioUploadedImageView())
->setUser($user)
->setImage($mock_image)
->setReplacesPHID($mock_image->getFilePHID());
}
$list_id = celerity_generate_unique_node_id();
$drop_id = celerity_generate_unique_node_id();
$order_id = celerity_generate_unique_node_id();
$list_control = phutil_tag(
'div',
array(
'id' => $list_id,
'class' => 'pholio-edit-list',
),
$image_elements);
$drop_control = phutil_tag(
'div',
array(
'id' => $drop_id,
'class' => 'pholio-edit-drop',
),
'Drag and drop images here to add them to the mock.');
$order_control = phutil_tag(
'input',
array(
'type' => 'hidden',
'name' => 'imageOrder',
'id' => $order_id,
));
Javelin::initBehavior(
'pholio-mock-edit',
array(
'listID' => $list_id,
'dropID' => $drop_id,
'orderID' => $order_id,
'uploadURI' => '/file/dropupload/',
'renderURI' => $this->getApplicationURI('image/upload/'),
'pht' => array(
'uploading' => pht('Uploading Image...'),
'uploaded' => pht('Upload Complete...'),
'undo' => pht('Undo'),
'removed' => pht('This image will be removed from the mock.'),
),
));
- if ($v_projects) {
- $project_handles = $this->loadViewerHandles($v_projects);
- } else {
- $project_handles = array();
- }
-
require_celerity_resource('pholio-edit-css');
$form = id(new AphrontFormView())
->setUser($user)
->appendChild($order_control)
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setValue($v_name)
->setLabel(pht('Name'))
->setError($e_name))
->appendChild(
id(new PhabricatorRemarkupControl())
->setName('description')
->setValue($v_desc)
->setLabel(pht('Description'))
->setUser($user));
if ($this->id) {
$form->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Status'))
->setName('status')
->setValue($mock->getStatus())
->setOptions($mock->getStatuses()));
} else {
$form->addHiddenInput('status', 'open');
}
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Projects'))
->setName('projects')
- ->setValue($project_handles)
+ ->setValue($v_projects)
->setDatasource(new PhabricatorProjectDatasource()))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('CC'))
->setName('cc')
- ->setValue($handles)
+ ->setValue($v_cc)
->setUser($user)
->setDatasource(new PhabricatorMetaMTAMailableDatasource()))
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($user)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicyObject($mock)
->setPolicies($policies)
->setName('can_view'))
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($user)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicyObject($mock)
->setPolicies($policies)
->setName('can_edit'))
->appendChild(
id(new AphrontFormMarkupControl())
->setValue($list_control))
->appendChild(
id(new AphrontFormMarkupControl())
->setValue($drop_control)
->setError($e_images))
->appendChild($submit);
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setFormErrors($errors)
->setForm($form);
$crumbs = $this->buildApplicationCrumbs();
if (!$is_new) {
$crumbs->addTextCrumb($mock->getMonogram(), '/'.$mock->getMonogram());
}
$crumbs->addTextCrumb($title);
$content = array(
$crumbs,
$form_box,
);
return $this->buildApplicationPage(
$content,
array(
'title' => $title,
));
}
}
diff --git a/src/applications/pholio/query/PholioMockSearchEngine.php b/src/applications/pholio/query/PholioMockSearchEngine.php
index 478e2cc219..91bc61f323 100644
--- a/src/applications/pholio/query/PholioMockSearchEngine.php
+++ b/src/applications/pholio/query/PholioMockSearchEngine.php
@@ -1,147 +1,143 @@
setParameter(
'authorPHIDs',
$this->readUsersFromRequest($request, 'authors'));
$saved->setParameter(
'statuses',
$request->getStrList('status'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PholioMockQuery())
->needCoverFiles(true)
->needImages(true)
->needTokenCounts(true)
->withAuthorPHIDs($saved->getParameter('authorPHIDs', array()))
->withStatuses($saved->getParameter('statuses', array()));
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
- $phids = $saved_query->getParameter('authorPHIDs', array());
- $author_handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($phids)
- ->execute();
+ $author_phids = $saved_query->getParameter('authorPHIDs', array());
$statuses = array(
'' => pht('Any Status'),
'closed' => pht('Closed'),
'open' => pht('Open'),
);
$status = $saved_query->getParameter('statuses', array());
$status = head($status);
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('authors')
->setLabel(pht('Authors'))
- ->setValue($author_handles))
+ ->setValue($author_phids))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Status'))
->setName('status')
->setOptions($statuses)
->setValue($status));
}
protected function getURI($path) {
return '/pholio/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'open' => pht('Open Mocks'),
'all' => pht('All Mocks'),
);
if ($this->requireViewer()->isLoggedIn()) {
$names['authored'] = pht('Authored');
}
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'open':
return $query->setParameter(
'statuses',
array('open'));
case 'all':
return $query;
case 'authored':
return $query->setParameter(
'authorPHIDs',
array($this->requireViewer()->getPHID()));
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function getRequiredHandlePHIDsForResultList(
array $mocks,
PhabricatorSavedQuery $query) {
return mpull($mocks, 'getAuthorPHID');
}
protected function renderResultList(
array $mocks,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($mocks, 'PholioMock');
$viewer = $this->requireViewer();
$board = new PHUIPinboardView();
foreach ($mocks as $mock) {
$header = 'M'.$mock->getID().' '.$mock->getName();
$item = id(new PHUIPinboardItemView())
->setHeader($header)
->setURI('/M'.$mock->getID())
->setImageURI($mock->getCoverFile()->getThumb280x210URI())
->setImageSize(280, 210)
->setDisabled($mock->isClosed())
->addIconCount('fa-picture-o', count($mock->getImages()))
->addIconCount('fa-trophy', $mock->getTokenCount());
if ($mock->getAuthorPHID()) {
$author_handle = $handles[$mock->getAuthorPHID()];
$datetime = phabricator_date($mock->getDateCreated(), $viewer);
$item->appendChild(
pht('By %s on %s', $author_handle->renderLink(), $datetime));
}
$board->addItem($item);
}
return $board;
}
}
diff --git a/src/applications/phortune/controller/PhortuneAccountEditController.php b/src/applications/phortune/controller/PhortuneAccountEditController.php
index b7632426c0..6d0f86d868 100644
--- a/src/applications/phortune/controller/PhortuneAccountEditController.php
+++ b/src/applications/phortune/controller/PhortuneAccountEditController.php
@@ -1,137 +1,135 @@
id = idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
if ($this->id) {
$account = id(new PhortuneAccountQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$account) {
return new Aphront404Response();
}
$is_new = false;
} else {
$account = PhortuneAccount::initializeNewAccount($viewer);
$account->attachMemberPHIDs(array($viewer->getPHID()));
$is_new = true;
}
$v_name = $account->getName();
$e_name = true;
$v_members = $account->getMemberPHIDs();
$e_members = null;
$validation_exception = null;
if ($request->isFormPost()) {
$v_name = $request->getStr('name');
$v_members = $request->getArr('memberPHIDs');
$type_name = PhortuneAccountTransaction::TYPE_NAME;
$type_edge = PhabricatorTransactions::TYPE_EDGE;
$xactions = array();
$xactions[] = id(new PhortuneAccountTransaction())
->setTransactionType($type_name)
->setNewValue($v_name);
$xactions[] = id(new PhortuneAccountTransaction())
->setTransactionType($type_edge)
->setMetadataValue(
'edge:type',
PhortuneAccountHasMemberEdgeType::EDGECONST)
->setNewValue(
array(
'=' => array_fuse($v_members),
));
$editor = id(new PhortuneAccountEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true);
try {
$editor->applyTransactions($account, $xactions);
$account_uri = $this->getApplicationURI($account->getID().'/');
return id(new AphrontRedirectResponse())->setURI($account_uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
$e_name = $ex->getShortMessage($type_name);
$e_members = $ex->getShortMessage($type_edge);
}
}
$crumbs = $this->buildApplicationCrumbs();
if ($is_new) {
$cancel_uri = $this->getApplicationURI('account/');
$crumbs->addTextCrumb(pht('Accounts'), $cancel_uri);
$crumbs->addTextCrumb(pht('Create Account'));
$title = pht('Create Payment Account');
$submit_button = pht('Create Account');
} else {
$cancel_uri = $this->getApplicationURI($account->getID().'/');
$crumbs->addTextCrumb($account->getName(), $cancel_uri);
$crumbs->addTextCrumb(pht('Edit'));
$title = pht('Edit %s', $account->getName());
$submit_button = pht('Save Changes');
}
- $member_handles = $this->loadViewerHandles($v_members);
-
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setLabel(pht('Name'))
->setValue($v_name)
->setError($e_name))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setLabel(pht('Members'))
->setName('memberPHIDs')
- ->setValue($member_handles)
+ ->setValue($v_members)
->setError($e_members))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue($submit_button)
->addCancelButton($cancel_uri));
$box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setValidationException($validation_exception)
->appendChild($form);
return $this->buildApplicationPage(
array(
$crumbs,
$box,
),
array(
'title' => $title,
));
}
}
diff --git a/src/applications/phortune/controller/PhortuneMerchantEditController.php b/src/applications/phortune/controller/PhortuneMerchantEditController.php
index 2b66209761..6dfef5bd74 100644
--- a/src/applications/phortune/controller/PhortuneMerchantEditController.php
+++ b/src/applications/phortune/controller/PhortuneMerchantEditController.php
@@ -1,181 +1,179 @@
id = idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
if ($this->id) {
$merchant = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$merchant) {
return new Aphront404Response();
}
$is_new = false;
} else {
$this->requireApplicationCapability(
PhortuneMerchantCapability::CAPABILITY);
$merchant = PhortuneMerchant::initializeNewMerchant($viewer);
$merchant->attachMemberPHIDs(array($viewer->getPHID()));
$is_new = true;
}
if ($is_new) {
$title = pht('Create Merchant');
$button_text = pht('Create Merchant');
$cancel_uri = $this->getApplicationURI('merchant/');
} else {
$title = pht(
'Edit Merchant %d %s',
$merchant->getID(),
$merchant->getName());
$button_text = pht('Save Changes');
$cancel_uri = $this->getApplicationURI(
'/merchant/'.$merchant->getID().'/');
}
$e_name = true;
$v_name = $merchant->getName();
$v_desc = $merchant->getDescription();
$v_members = $merchant->getMemberPHIDs();
$e_members = null;
$validation_exception = null;
if ($request->isFormPost()) {
$v_name = $request->getStr('name');
$v_desc = $request->getStr('desc');
$v_view = $request->getStr('viewPolicy');
$v_edit = $request->getStr('editPolicy');
$v_members = $request->getArr('memberPHIDs');
$type_name = PhortuneMerchantTransaction::TYPE_NAME;
$type_desc = PhortuneMerchantTransaction::TYPE_DESCRIPTION;
$type_edge = PhabricatorTransactions::TYPE_EDGE;
$type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
$edge_members = PhortuneMerchantHasMemberEdgeType::EDGECONST;
$xactions = array();
$xactions[] = id(new PhortuneMerchantTransaction())
->setTransactionType($type_name)
->setNewValue($v_name);
$xactions[] = id(new PhortuneMerchantTransaction())
->setTransactionType($type_desc)
->setNewValue($v_desc);
$xactions[] = id(new PhortuneMerchantTransaction())
->setTransactionType($type_view)
->setNewValue($v_view);
$xactions[] = id(new PhortuneMerchantTransaction())
->setTransactionType($type_edge)
->setMetadataValue('edge:type', $edge_members)
->setNewValue(
array(
'=' => array_fuse($v_members),
));
$editor = id(new PhortuneMerchantEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true);
try {
$editor->applyTransactions($merchant, $xactions);
$id = $merchant->getID();
$merchant_uri = $this->getApplicationURI("merchant/{$id}/");
return id(new AphrontRedirectResponse())->setURI($merchant_uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
$e_name = $ex->getShortMessage($type_name);
$e_mbmers = $ex->getShortMessage($type_edge);
$merchant->setViewPolicy($v_view);
}
}
$policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->setObject($merchant)
->execute();
- $member_handles = $this->loadViewerHandles($v_members);
-
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setLabel(pht('Name'))
->setValue($v_name)
->setError($e_name))
->appendChild(
id(new PhabricatorRemarkupControl())
->setUser($viewer)
->setName('desc')
->setLabel(pht('Description'))
->setValue($v_desc))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setLabel(pht('Members'))
->setName('memberPHIDs')
- ->setValue($member_handles)
+ ->setValue($v_members)
->setError($e_members))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('viewPolicy')
->setPolicyObject($merchant)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicies($policies))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue($button_text)
->addCancelButton($cancel_uri));
$crumbs = $this->buildApplicationCrumbs();
if ($is_new) {
$crumbs->addTextCrumb(pht('Create Merchant'));
} else {
$crumbs->addTextCrumb(
pht('Merchant %d', $merchant->getID()),
$this->getApplicationURI('/merchant/'.$merchant->getID().'/'));
$crumbs->addTextCrumb(pht('Edit'));
}
$box = id(new PHUIObjectBoxView())
->setValidationException($validation_exception)
->setHeaderText($title)
->appendChild($form);
return $this->buildApplicationPage(
array(
$crumbs,
$box,
),
array(
'title' => $title,
));
}
}
diff --git a/src/applications/phrequent/query/PhrequentSearchEngine.php b/src/applications/phrequent/query/PhrequentSearchEngine.php
index b5737c945c..4b520ab36b 100644
--- a/src/applications/phrequent/query/PhrequentSearchEngine.php
+++ b/src/applications/phrequent/query/PhrequentSearchEngine.php
@@ -1,201 +1,195 @@
getParameter('limit', 1000);
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter(
'userPHIDs',
$this->readUsersFromRequest($request, 'users'));
$saved->setParameter('ended', $request->getStr('ended'));
$saved->setParameter('order', $request->getStr('order'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhrequentUserTimeQuery())
->needPreemptingEvents(true);
$user_phids = $saved->getParameter('userPHIDs');
if ($user_phids) {
$query->withUserPHIDs($user_phids);
}
$ended = $saved->getParameter('ended');
if ($ended != null) {
$query->withEnded($ended);
}
$order = $saved->getParameter('order');
if ($order != null) {
$query->setOrder($order);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
$user_phids = $saved_query->getParameter('userPHIDs', array());
$ended = $saved_query->getParameter(
'ended', PhrequentUserTimeQuery::ENDED_ALL);
$order = $saved_query->getParameter(
'order', PhrequentUserTimeQuery::ORDER_ENDED_DESC);
- $phids = array_merge($user_phids);
- $handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($phids)
- ->execute();
-
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('users')
->setLabel(pht('Users'))
- ->setValue($handles))
+ ->setValue($user_phids))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Ended'))
->setName('ended')
->setValue($ended)
->setOptions(PhrequentUserTimeQuery::getEndedSearchOptions()))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Order'))
->setName('order')
->setValue($order)
->setOptions(PhrequentUserTimeQuery::getOrderSearchOptions()));
}
protected function getURI($path) {
return '/phrequent/'.$path;
}
protected function getBuiltinQueryNames() {
return array(
'tracking' => pht('Currently Tracking'),
'all' => pht('All Tracked'),
);
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query
->setParameter('order', PhrequentUserTimeQuery::ORDER_ENDED_DESC);
case 'tracking':
return $query
->setParameter('ended', PhrequentUserTimeQuery::ENDED_NO)
->setParameter('order', PhrequentUserTimeQuery::ORDER_ENDED_DESC);
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function getRequiredHandlePHIDsForResultList(
array $usertimes,
PhabricatorSavedQuery $query) {
return array_mergev(
array(
mpull($usertimes, 'getUserPHID'),
mpull($usertimes, 'getObjectPHID'),
));
}
protected function renderResultList(
array $usertimes,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($usertimes, 'PhrequentUserTime');
$viewer = $this->requireViewer();
$view = id(new PHUIObjectItemListView())
->setUser($viewer);
foreach ($usertimes as $usertime) {
$item = new PHUIObjectItemView();
if ($usertime->getObjectPHID() === null) {
$item->setHeader($usertime->getNote());
} else {
$obj = $handles[$usertime->getObjectPHID()];
$item->setHeader($obj->getLinkName());
$item->setHref($obj->getURI());
}
$item->setObject($usertime);
$item->addByline(
pht(
'Tracked: %s',
$handles[$usertime->getUserPHID()]->renderLink()));
$started_date = phabricator_date($usertime->getDateStarted(), $viewer);
$item->addIcon('none', $started_date);
$block = new PhrequentTimeBlock(array($usertime));
$time_spent = $block->getTimeSpentOnObject(
$usertime->getObjectPHID(),
PhabricatorTime::getNow());
$time_spent = $time_spent == 0 ? 'none' :
phutil_format_relative_time_detailed($time_spent);
if ($usertime->getDateEnded() !== null) {
$item->addAttribute(
pht(
'Tracked %s',
$time_spent));
$item->addAttribute(
pht(
'Ended on %s',
phabricator_datetime($usertime->getDateEnded(), $viewer)));
} else {
$item->addAttribute(
pht(
'Tracked %s so far',
$time_spent));
if ($usertime->getObjectPHID() !== null &&
$usertime->getUserPHID() === $viewer->getPHID()) {
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-stop')
->addSigil('phrequent-stop-tracking')
->setWorkflow(true)
->setRenderNameAsTooltip(true)
->setName(pht('Stop'))
->setHref(
'/phrequent/track/stop/'.
$usertime->getObjectPHID().'/'));
}
$item->setBarColor('green');
}
$view->addItem($item);
}
return $view;
}
}
diff --git a/src/applications/ponder/controller/PonderQuestionEditController.php b/src/applications/ponder/controller/PonderQuestionEditController.php
index 117eefa461..331c605eae 100644
--- a/src/applications/ponder/controller/PonderQuestionEditController.php
+++ b/src/applications/ponder/controller/PonderQuestionEditController.php
@@ -1,157 +1,151 @@
id = idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
if ($this->id) {
$question = id(new PonderQuestionQuery())
->setViewer($user)
->withIDs(array($this->id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$question) {
return new Aphront404Response();
}
$v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
$question->getPHID(),
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
$v_projects = array_reverse($v_projects);
} else {
$question = id(new PonderQuestion())
->setStatus(PonderQuestionStatus::STATUS_OPEN)
->setAuthorPHID($user->getPHID())
->setVoteCount(0)
->setAnswerCount(0)
->setHeat(0.0);
$v_projects = array();
}
$v_title = $question->getTitle();
$v_content = $question->getContent();
$errors = array();
$e_title = true;
if ($request->isFormPost()) {
$v_title = $request->getStr('title');
$v_content = $request->getStr('content');
$v_projects = $request->getArr('projects');
$len = phutil_utf8_strlen($v_title);
if ($len < 1) {
$errors[] = pht('Title must not be empty.');
$e_title = pht('Required');
} else if ($len > 255) {
$errors[] = pht('Title is too long.');
$e_title = pht('Too Long');
}
if (!$errors) {
$template = id(new PonderQuestionTransaction());
$xactions = array();
$xactions[] = id(clone $template)
->setTransactionType(PonderQuestionTransaction::TYPE_TITLE)
->setNewValue($v_title);
$xactions[] = id(clone $template)
->setTransactionType(PonderQuestionTransaction::TYPE_CONTENT)
->setNewValue($v_content);
$proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
$xactions[] = id(new PonderQuestionTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $proj_edge_type)
->setNewValue(array('=' => array_fuse($v_projects)));
$editor = id(new PonderQuestionEditor())
->setActor($user)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true);
$editor->applyTransactions($question, $xactions);
return id(new AphrontRedirectResponse())
->setURI('/Q'.$question->getID());
}
}
$form = id(new AphrontFormView())
->setUser($user)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Question'))
->setName('title')
->setValue($v_title)
->setError($e_title))
->appendChild(
id(new PhabricatorRemarkupControl())
->setUser($user)
->setName('content')
->setID('content')
->setValue($v_content)
->setLabel(pht('Description'))
->setUser($user));
- if ($v_projects) {
- $project_handles = $this->loadViewerHandles($v_projects);
- } else {
- $project_handles = array();
- }
-
- $form->appendChild(
+ $form->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Projects'))
->setName('projects')
- ->setValue($project_handles)
+ ->setValue($v_projects)
->setDatasource(new PhabricatorProjectDatasource()));
$form ->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($this->getApplicationURI())
->setValue(pht('Ask Away!')));
$preview = id(new PHUIRemarkupPreviewPanel())
->setHeader(pht('Question Preview'))
->setControlID('content')
->setPreviewURI($this->getApplicationURI('preview/'));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Ask New Question'))
->setFormErrors($errors)
->setForm($form);
$crumbs = $this->buildApplicationCrumbs();
$id = $question->getID();
if ($id) {
$crumbs->addTextCrumb("Q{$id}", "/Q{$id}");
$crumbs->addTextCrumb(pht('Edit'));
} else {
$crumbs->addTextCrumb(pht('Ask Question'));
}
return $this->buildApplicationPage(
array(
$crumbs,
$form_box,
$preview,
),
array(
'title' => pht('Ask New Question'),
));
}
}
diff --git a/src/applications/ponder/query/PonderQuestionSearchEngine.php b/src/applications/ponder/query/PonderQuestionSearchEngine.php
index 462046544c..823b6aaf3c 100644
--- a/src/applications/ponder/query/PonderQuestionSearchEngine.php
+++ b/src/applications/ponder/query/PonderQuestionSearchEngine.php
@@ -1,178 +1,172 @@
setParameter(
'authorPHIDs',
$this->readUsersFromRequest($request, 'authors'));
$saved->setParameter(
'answererPHIDs',
$this->readUsersFromRequest($request, 'answerers'));
$saved->setParameter('status', $request->getStr('status'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PonderQuestionQuery());
$author_phids = $saved->getParameter('authorPHIDs');
if ($author_phids) {
$query->withAuthorPHIDs($author_phids);
}
$answerer_phids = $saved->getParameter('answererPHIDs');
if ($answerer_phids) {
$query->withAnswererPHIDs($answerer_phids);
}
$status = $saved->getParameter('status');
if ($status != null) {
switch ($status) {
case 0:
$query->withStatus(PonderQuestionQuery::STATUS_OPEN);
break;
case 1:
$query->withStatus(PonderQuestionQuery::STATUS_CLOSED);
break;
}
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
$author_phids = $saved_query->getParameter('authorPHIDs', array());
$answerer_phids = $saved_query->getParameter('answererPHIDs', array());
$status = $saved_query->getParameter(
'status', PonderQuestionStatus::STATUS_OPEN);
- $phids = array_merge($author_phids, $answerer_phids);
- $handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($phids)
- ->execute();
-
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('authors')
->setLabel(pht('Authors'))
- ->setValue(array_select_keys($handles, $author_phids)))
- ->appendChild(
+ ->setValue($author_phids))
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('answerers')
->setLabel(pht('Answered By'))
- ->setValue(array_select_keys($handles, $answerer_phids)))
+ ->setValue($answerer_phids))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Status'))
->setName('status')
->setValue($status)
->setOptions(PonderQuestionStatus::getQuestionStatusMap()));
}
protected function getURI($path) {
return '/ponder/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'open' => pht('Open Questions'),
'all' => pht('All Questions'),
);
if ($this->requireViewer()->isLoggedIn()) {
$names['authored'] = pht('Authored');
$names['answered'] = pht('Answered');
}
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
case 'open':
return $query->setParameter('status', PonderQuestionQuery::STATUS_OPEN);
case 'authored':
return $query->setParameter(
'authorPHIDs',
array($this->requireViewer()->getPHID()));
case 'answered':
return $query->setParameter(
'answererPHIDs',
array($this->requireViewer()->getPHID()));
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function getRequiredHandlePHIDsForResultList(
array $questions,
PhabricatorSavedQuery $query) {
return mpull($questions, 'getAuthorPHID');
}
protected function renderResultList(
array $questions,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($questions, 'PonderQuestion');
$viewer = $this->requireViewer();
$view = id(new PHUIObjectItemListView())
->setUser($viewer);
foreach ($questions as $question) {
$item = new PHUIObjectItemView();
$item->setObjectName('Q'.$question->getID());
$item->setHeader($question->getTitle());
$item->setHref('/Q'.$question->getID());
$item->setObject($question);
$item->setBarColor(
PonderQuestionStatus::getQuestionStatusTagColor(
$question->getStatus()));
$created_date = phabricator_date($question->getDateCreated(), $viewer);
$item->addIcon('none', $created_date);
$item->addByline(
pht(
'Asked by %s',
$handles[$question->getAuthorPHID()]->renderLink()));
$item->addAttribute(
pht('%d Answer(s)', $question->getAnswerCount()));
$view->addItem($item);
}
return $view;
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectBoardImportController.php b/src/applications/project/controller/PhabricatorProjectBoardImportController.php
index 4861582a20..b11d6efe31 100644
--- a/src/applications/project/controller/PhabricatorProjectBoardImportController.php
+++ b/src/applications/project/controller/PhabricatorProjectBoardImportController.php
@@ -1,86 +1,87 @@
projectID = $data['projectID'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$project = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->withIDs(array($this->projectID))
->executeOne();
if (!$project) {
return new Aphront404Response();
}
$this->setProject($project);
$columns = id(new PhabricatorProjectColumnQuery())
->setViewer($viewer)
->withProjectPHIDs(array($project->getPHID()))
->execute();
if ($columns) {
return new Aphront400Response();
}
$project_id = $project->getID();
$board_uri = $this->getApplicationURI("board/{$project_id}/");
if ($request->isFormPost()) {
$import_phid = $request->getArr('importProjectPHID');
$import_phid = reset($import_phid);
$import_columns = id(new PhabricatorProjectColumnQuery())
->setViewer($viewer)
->withProjectPHIDs(array($import_phid))
->execute();
if (!$import_columns) {
return new Aphront400Response();
}
$table = id(new PhabricatorProjectColumn())
->openTransaction();
foreach ($import_columns as $import_column) {
if ($import_column->isHidden()) {
continue;
}
$new_column = PhabricatorProjectColumn::initializeNewColumn($viewer)
->setSequence($import_column->getSequence())
->setProjectPHID($project->getPHID())
->setName($import_column->getName())
->setProperties($import_column->getProperties())
->save();
}
$table->saveTransaction();
return id(new AphrontRedirectResponse())->setURI($board_uri);
}
$proj_selector = id(new AphrontFormTokenizerControl())
->setName('importProjectPHID')
->setUser($viewer)
->setDatasource(id(new PhabricatorProjectDatasource())
->setParameters(array('mustHaveColumns' => true))
->setLimit(1));
+
return $this->newDialog()
->setTitle(pht('Import Columns'))
->setWidth(AphrontDialogView::WIDTH_FORM)
->appendParagraph(pht('Choose a project to import columns from:'))
->appendChild($proj_selector)
->addCancelButton($board_uri)
->addSubmitButton(pht('Import'));
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectMembersEditController.php b/src/applications/project/controller/PhabricatorProjectMembersEditController.php
index 26c0ed3709..750df2b77d 100644
--- a/src/applications/project/controller/PhabricatorProjectMembersEditController.php
+++ b/src/applications/project/controller/PhabricatorProjectMembersEditController.php
@@ -1,162 +1,162 @@
id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$id = $request->getURIData('id');
$project = id(new PhabricatorProjectQuery())
->setViewer($user)
->withIDs(array($this->id))
->needMembers(true)
->needImages(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
))
->executeOne();
if (!$project) {
return new Aphront404Response();
}
$member_phids = $project->getMemberPHIDs();
if ($request->isFormPost()) {
$member_spec = array();
$remove = $request->getStr('remove');
if ($remove) {
$member_spec['-'] = array_fuse(array($remove));
}
$add_members = $request->getArr('phids');
if ($add_members) {
$member_spec['+'] = array_fuse($add_members);
}
$type_member = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST;
$xactions = array();
$xactions[] = id(new PhabricatorProjectTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $type_member)
->setNewValue($member_spec);
$editor = id(new PhabricatorProjectTransactionEditor($project))
->setActor($user)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->applyTransactions($project, $xactions);
return id(new AphrontRedirectResponse())
->setURI($request->getRequestURI());
}
$member_phids = array_reverse($member_phids);
$handles = $this->loadViewerHandles($member_phids);
$state = array();
foreach ($handles as $handle) {
$state[] = array(
'phid' => $handle->getPHID(),
'name' => $handle->getFullName(),
);
}
$can_edit = PhabricatorPolicyFilter::hasCapability(
$user,
$project,
PhabricatorPolicyCapability::CAN_EDIT);
$form_box = null;
$title = pht('Add Members');
if ($can_edit) {
$header_name = pht('Edit Members');
$view_uri = $this->getApplicationURI('profile/'.$project->getID().'/');
$form = new AphrontFormView();
$form
->setUser($user)
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setName('phids')
->setLabel(pht('Add Members'))
->setDatasource(new PhabricatorPeopleDatasource()))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($view_uri)
->setValue(pht('Add Members')));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setForm($form);
}
$member_list = $this->renderMemberList($project, $handles);
$nav = $this->buildIconNavView($project);
$nav->selectFilter("members/{$id}/");
$nav->appendChild($form_box);
$nav->appendChild($member_list);
return $this->buildApplicationPage(
$nav,
array(
'title' => $title,
));
}
private function renderMemberList(
PhabricatorProject $project,
array $handles) {
$request = $this->getRequest();
$viewer = $request->getUser();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
PhabricatorPolicyCapability::CAN_EDIT);
$list = id(new PHUIObjectItemListView())
->setNoDataString(pht('This project does not have any members.'))
->setStackable(true);
foreach ($handles as $handle) {
$remove_uri = $this->getApplicationURI(
'/members/'.$project->getID().'/remove/?phid='.$handle->getPHID());
$item = id(new PHUIObjectItemView())
->setHeader($handle->getFullName())
->setHref($handle->getURI())
->setImageURI($handle->getImageURI());
if ($can_edit) {
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-times')
->setName(pht('Remove'))
->setHref($remove_uri)
->setWorkflow(true));
}
$list->addItem($item);
}
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Members'))
->appendChild($list);
return $box;
}
}
diff --git a/src/applications/project/query/PhabricatorProjectSearchEngine.php b/src/applications/project/query/PhabricatorProjectSearchEngine.php
index 0297e910e5..ff2b1c790f 100644
--- a/src/applications/project/query/PhabricatorProjectSearchEngine.php
+++ b/src/applications/project/query/PhabricatorProjectSearchEngine.php
@@ -1,244 +1,240 @@
setParameter(
'memberPHIDs',
$this->readUsersFromRequest($request, 'members'));
$saved->setParameter('status', $request->getStr('status'));
$saved->setParameter('name', $request->getStr('name'));
$saved->setParameter(
'icons',
$this->readListFromRequest($request, 'icons'));
$saved->setParameter(
'colors',
$this->readListFromRequest($request, 'colors'));
$this->readCustomFieldsFromRequest($request, $saved);
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorProjectQuery())
->needImages(true);
$member_phids = $saved->getParameter('memberPHIDs', array());
if ($member_phids && is_array($member_phids)) {
$query->withMemberPHIDs($member_phids);
}
$status = $saved->getParameter('status');
$status = idx($this->getStatusValues(), $status);
if ($status) {
$query->withStatus($status);
}
$name = $saved->getParameter('name');
if (strlen($name)) {
$query->withDatasourceQuery($name);
}
$icons = $saved->getParameter('icons');
if ($icons) {
$query->withIcons($icons);
}
$colors = $saved->getParameter('colors');
if ($colors) {
$query->withColors($colors);
}
$this->applyCustomFieldsToQuery($query, $saved);
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved) {
- $phids = $saved->getParameter('memberPHIDs', array());
- $member_handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($phids)
- ->execute();
+ $member_phids = $saved->getParameter('memberPHIDs', array());
$status = $saved->getParameter('status');
$name_match = $saved->getParameter('name');
$icons = array_fuse($saved->getParameter('icons', array()));
$colors = array_fuse($saved->getParameter('colors', array()));
$icon_control = id(new AphrontFormCheckboxControl())
->setLabel(pht('Icons'));
foreach (PhabricatorProjectIcon::getIconMap() as $icon => $name) {
$image = id(new PHUIIconView())
->setIconFont($icon);
$icon_control->addCheckbox(
'icons[]',
$icon,
array($image, ' ', $name),
isset($icons[$icon]));
}
$color_control = id(new AphrontFormCheckboxControl())
->setLabel(pht('Colors'));
foreach (PhabricatorProjectIcon::getColorMap() as $color => $name) {
$tag = id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE)
->setShade($color)
->setName($name);
$color_control->addCheckbox(
'colors[]',
$color,
$tag,
isset($colors[$color]));
}
$form
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setLabel(pht('Name'))
->setValue($name_match))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('members')
->setLabel(pht('Members'))
- ->setValue($member_handles))
+ ->setValue($member_phids))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Status'))
->setName('status')
->setOptions($this->getStatusOptions())
->setValue($status))
->appendChild($icon_control)
->appendChild($color_control);
$this->appendCustomFieldsToForm($form, $saved);
}
protected function getURI($path) {
return '/project/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array();
if ($this->requireViewer()->isLoggedIn()) {
$names['joined'] = pht('Joined');
}
$names['active'] = pht('Active');
$names['all'] = pht('All');
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
$viewer_phid = $this->requireViewer()->getPHID();
switch ($query_key) {
case 'all':
return $query;
case 'active':
return $query
->setParameter('status', 'active');
case 'joined':
return $query
->setParameter('memberPHIDs', array($viewer_phid))
->setParameter('status', 'active');
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
private function getStatusOptions() {
return array(
'active' => pht('Show Only Active Projects'),
'all' => pht('Show All Projects'),
);
}
private function getStatusValues() {
return array(
'active' => PhabricatorProjectQuery::STATUS_ACTIVE,
'all' => PhabricatorProjectQuery::STATUS_ANY,
);
}
private function getColorValues() {}
private function getIconValues() {}
protected function getRequiredHandlePHIDsForResultList(
array $projects,
PhabricatorSavedQuery $query) {
return mpull($projects, 'getPHID');
}
protected function renderResultList(
array $projects,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($projects, 'PhabricatorProject');
$viewer = $this->requireViewer();
$list = new PHUIObjectItemListView();
$list->setUser($viewer);
$can_edit_projects = id(new PhabricatorPolicyFilter())
->setViewer($viewer)
->requireCapabilities(array(PhabricatorPolicyCapability::CAN_EDIT))
->apply($projects);
foreach ($projects as $key => $project) {
$id = $project->getID();
$tag_list = id(new PHUIHandleTagListView())
->setSlim(true)
->setHandles(array($handles[$project->getPHID()]));
$item = id(new PHUIObjectItemView())
->setHeader($project->getName())
->setHref($this->getApplicationURI("view/{$id}/"))
->setImageURI($project->getProfileImageURI())
->addAttribute($tag_list);
if ($project->getStatus() == PhabricatorProjectStatus::STATUS_ARCHIVED) {
$item->addIcon('delete-grey', pht('Archived'));
$item->setDisabled(true);
}
$list->addItem($item);
}
return $list;
}
}
diff --git a/src/applications/releeph/controller/product/ReleephProductEditController.php b/src/applications/releeph/controller/product/ReleephProductEditController.php
index 2399548206..926f813db0 100644
--- a/src/applications/releeph/controller/product/ReleephProductEditController.php
+++ b/src/applications/releeph/controller/product/ReleephProductEditController.php
@@ -1,280 +1,273 @@
productID = $data['projectID'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$product = id(new ReleephProductQuery())
->setViewer($viewer)
->withIDs(array($this->productID))
->needArcanistProjects(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$product) {
return new Aphront404Response();
}
$this->setProduct($product);
$e_name = true;
$e_trunk_branch = true;
$e_branch_template = false;
$errors = array();
$product_name = $request->getStr('name', $product->getName());
$trunk_branch = $request->getStr('trunkBranch', $product->getTrunkBranch());
$branch_template = $request->getStr('branchTemplate');
if ($branch_template === null) {
$branch_template = $product->getDetail('branchTemplate');
}
$pick_failure_instructions = $request->getStr('pickFailureInstructions',
$product->getDetail('pick_failure_instructions'));
$test_paths = $request->getStr('testPaths');
if ($test_paths !== null) {
$test_paths = array_filter(explode("\n", $test_paths));
} else {
$test_paths = $product->getDetail('testPaths', array());
}
$arc_project_id = $product->getArcanistProjectID();
if ($request->isFormPost()) {
$pusher_phids = $request->getArr('pushers');
if (!$product_name) {
$e_name = pht('Required');
$errors[] =
pht('Your releeph product should have a simple descriptive name.');
}
if (!$trunk_branch) {
$e_trunk_branch = pht('Required');
$errors[] =
pht('You must specify which branch you will be picking from.');
}
$other_releeph_products = id(new ReleephProject())
->loadAllWhere('id != %d', $product->getID());
$other_releeph_product_names = mpull($other_releeph_products,
'getName', 'getID');
if (in_array($product_name, $other_releeph_product_names)) {
$errors[] = pht('Releeph product name %s is already taken',
$product_name);
}
foreach ($test_paths as $test_path) {
$result = @preg_match($test_path, '');
$is_a_valid_regexp = $result !== false;
if (!$is_a_valid_regexp) {
$errors[] = pht('Please provide a valid regular expression: '.
'%s is not valid', $test_path);
}
}
$product
->setName($product_name)
->setTrunkBranch($trunk_branch)
->setDetail('pushers', $pusher_phids)
->setDetail('pick_failure_instructions', $pick_failure_instructions)
->setDetail('branchTemplate', $branch_template)
->setDetail('testPaths', $test_paths);
$fake_commit_handle =
ReleephBranchTemplate::getFakeCommitHandleFor($arc_project_id, $viewer);
if ($branch_template) {
list($branch_name, $template_errors) = id(new ReleephBranchTemplate())
->setCommitHandle($fake_commit_handle)
->setReleephProjectName($product_name)
->interpolate($branch_template);
if ($template_errors) {
$e_branch_template = pht('Whoopsies!');
foreach ($template_errors as $template_error) {
$errors[] = "Template error: {$template_error}";
}
}
}
if (!$errors) {
$product->save();
return id(new AphrontRedirectResponse())->setURI($product->getURI());
}
}
$pusher_phids = $request->getArr(
'pushers',
$product->getDetail('pushers', array()));
- $handles = id(new PhabricatorHandleQuery())
- ->setViewer($request->getUser())
- ->withPHIDs($pusher_phids)
- ->execute();
-
- $pusher_handles = array_select_keys($handles, $pusher_phids);
-
$form = id(new AphrontFormView())
->setUser($request->getUser())
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Name'))
->setName('name')
->setValue($product_name)
->setError($e_name)
->setCaption(pht('A name like "Thrift" but not "Thrift releases".')))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Repository'))
->setValue(
$product->getRepository()->getName()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Arc Project'))
->setValue(
$product->getArcanistProject()->getName()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Releeph Project PHID'))
->setValue(
$product->getPHID()))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Trunk'))
->setValue($trunk_branch)
->setName('trunkBranch')
->setError($e_trunk_branch))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel(pht('Pick Instructions'))
->setValue($pick_failure_instructions)
->setName('pickFailureInstructions')
->setCaption(
pht('Instructions for pick failures, which will be used '.
'in emails generated by failed picks')))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel(pht('Tests paths'))
->setValue(implode("\n", $test_paths))
->setName('testPaths')
->setCaption(
pht('List of strings that all test files contain in their path '.
'in this project. One string per line. '.
'Examples: \'__tests__\', \'/javatests/\'...')));
$branch_template_input = id(new AphrontFormTextControl())
->setName('branchTemplate')
->setValue($branch_template)
->setLabel('Branch Template')
->setError($e_branch_template)
->setCaption(
pht("Leave this blank to use your installation's default."));
$branch_template_preview = id(new ReleephBranchPreviewView())
->setLabel(pht('Preview'))
->addControl('template', $branch_template_input)
->addStatic('arcProjectID', $arc_project_id)
->addStatic('isSymbolic', false)
->addStatic('projectName', $product->getName());
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Pushers'))
->setName('pushers')
->setDatasource(new PhabricatorPeopleDatasource())
- ->setValue($pusher_handles))
+ ->setValue($pusher_phids))
->appendChild($branch_template_input)
->appendChild($branch_template_preview)
->appendRemarkupInstructions($this->getBranchHelpText());
$form
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton('/releeph/product/')
->setValue(pht('Save')));
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Edit Releeph Product'))
->setFormErrors($errors)
->appendChild($form);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit Product'));
return $this->buildStandardPageResponse(
array(
$crumbs,
$box,
),
array(
'title' => pht('Edit Releeph Product'),
'device' => true,
));
}
private function getBranchHelpText() {
return << releases/2012-30-16-rHERGE32cd512a52b7
Include a second hierarchy if you share your repository with other products:
lang=none
releases/%P/%p-release-%Y%m%d-%V
=> releases/Tintin/tintin-release-20121116-32cd512a52b7
Keep your branch names simple, avoiding strange punctuation, most of which is
forbidden or escaped anyway:
lang=none, counterexample
releases//..clown-releases..//`date --iso=seconds`-$(sudo halt)
Include the date early in your template, in an order which sorts properly:
lang=none
releases/%Y%m%d-%v
=> releases/20121116-rHERGE32cd512a52b7 (good!)
releases/%V-%m.%d.%Y
=> releases/32cd512a52b7-11.16.2012 (awful!)
EOTEXT;
}
}
diff --git a/src/applications/releeph/query/ReleephRequestSearchEngine.php b/src/applications/releeph/query/ReleephRequestSearchEngine.php
index 5f2626614b..07e5b06dfb 100644
--- a/src/applications/releeph/query/ReleephRequestSearchEngine.php
+++ b/src/applications/releeph/query/ReleephRequestSearchEngine.php
@@ -1,179 +1,175 @@
branch = $branch;
return $this;
}
public function getBranch() {
return $this->branch;
}
public function setBaseURI($base_uri) {
$this->baseURI = $base_uri;
return $this;
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter('status', $request->getStr('status'));
$saved->setParameter('severity', $request->getStr('severity'));
$saved->setParameter(
'requestorPHIDs',
$this->readUsersFromRequest($request, 'requestors'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new ReleephRequestQuery())
->withBranchIDs(array($this->getBranch()->getID()));
$status = $saved->getParameter('status');
$status = idx($this->getStatusValues(), $status);
if ($status) {
$query->withStatus($status);
}
$severity = $saved->getParameter('severity');
if ($severity) {
$query->withSeverities(array($severity));
}
$requestor_phids = $saved->getParameter('requestorPHIDs');
if ($requestor_phids) {
$query->withRequestorPHIDs($requestor_phids);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
- $phids = $saved_query->getParameter('requestorPHIDs', array());
- $requestor_handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($phids)
- ->execute();
+ $requestor_phids = $saved_query->getParameter('requestorPHIDs', array());
$form
->appendChild(
id(new AphrontFormSelectControl())
->setName('status')
->setLabel(pht('Status'))
->setValue($saved_query->getParameter('status'))
->setOptions($this->getStatusOptions()))
->appendChild(
id(new AphrontFormSelectControl())
->setName('severity')
->setLabel(pht('Severity'))
->setValue($saved_query->getParameter('severity'))
->setOptions($this->getSeverityOptions()))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('requestors')
->setLabel(pht('Requestors'))
- ->setValue($requestor_handles));
+ ->setValue($requestor_phids));
}
protected function getURI($path) {
return $this->baseURI.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'open' => pht('Open Requests'),
'all' => pht('All Requests'),
);
if ($this->requireViewer()->isLoggedIn()) {
$names['requested'] = pht('Requested');
}
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'open':
return $query->setParameter('status', 'open');
case 'all':
return $query;
case 'requested':
return $query->setParameter(
'requestorPHIDs',
array($this->requireViewer()->getPHID()));
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
private function getStatusOptions() {
return array(
'' => pht('(All Requests)'),
'open' => pht('Open Requests'),
'requested' => pht('Pull Requested'),
'needs-pull' => pht('Needs Pull'),
'rejected' => pht('Rejected'),
'abandoned' => pht('Abandoned'),
'pulled' => pht('Pulled'),
'needs-revert' => pht('Needs Revert'),
'reverted' => pht('Reverted'),
);
}
private function getStatusValues() {
return array(
'open' => ReleephRequestQuery::STATUS_OPEN,
'requested' => ReleephRequestQuery::STATUS_REQUESTED,
'needs-pull' => ReleephRequestQuery::STATUS_NEEDS_PULL,
'rejected' => ReleephRequestQuery::STATUS_REJECTED,
'abandoned' => ReleephRequestQuery::STATUS_ABANDONED,
'pulled' => ReleephRequestQuery::STATUS_PULLED,
'needs-revert' => ReleephRequestQuery::STATUS_NEEDS_REVERT,
'reverted' => ReleephRequestQuery::STATUS_REVERTED,
);
}
private function getSeverityOptions() {
if (ReleephDefaultFieldSelector::isFacebook()) {
return array(
'' => pht('(All Severities)'),
11 => 'HOTFIX',
12 => 'PIGGYBACK',
13 => 'RELEASE',
14 => 'DAILY',
15 => 'PARKING',
);
} else {
return array(
'' => pht('(All Severities)'),
ReleephSeverityFieldSpecification::HOTFIX => pht('Hotfix'),
ReleephSeverityFieldSpecification::RELEASE => pht('Release'),
);
}
}
}
diff --git a/src/applications/repository/controller/PhabricatorRepositoryArcanistProjectEditController.php b/src/applications/repository/controller/PhabricatorRepositoryArcanistProjectEditController.php
index 04f50a620b..b3928b94ef 100644
--- a/src/applications/repository/controller/PhabricatorRepositoryArcanistProjectEditController.php
+++ b/src/applications/repository/controller/PhabricatorRepositoryArcanistProjectEditController.php
@@ -1,122 +1,119 @@
id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$project = id(new PhabricatorRepositoryArcanistProject())->load($this->id);
if (!$project) {
return new Aphront404Response();
}
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($user)
->execute();
$repos = array(
0 => 'None',
);
foreach ($repositories as $repository) {
$callsign = $repository->getCallsign();
$name = $repository->getname();
$repos[$repository->getID()] = "r{$callsign} ({$name})";
}
// note "None" will still be first thanks to 'r' prefix
asort($repos);
if ($request->isFormPost()) {
$indexed = $request->getStrList('symbolIndexLanguages');
$indexed = array_map('strtolower', $indexed);
$project->setSymbolIndexLanguages($indexed);
$project->setSymbolIndexProjects($request->getArr('symbolIndexProjects'));
$repo_id = $request->getInt('repository', 0);
if (isset($repos[$repo_id])) {
$project->setRepositoryID($repo_id);
$project->save();
return id(new AphrontRedirectResponse())
->setURI('/repository/');
}
}
$langs = $project->getSymbolIndexLanguages();
if ($langs) {
$langs = implode(', ', $langs);
} else {
$langs = null;
}
if ($project->getSymbolIndexProjects()) {
- $uses = id(new PhabricatorHandleQuery())
- ->setViewer($user)
- ->withPHIDs($project->getSymbolIndexProjects())
- ->execute();
+ $uses = $project->getSymbolIndexProjects();
} else {
$uses = array();
}
$form = id(new AphrontFormView())
->setUser($user)
->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Name'))
->setValue($project->getName()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('PHID')
->setValue($project->getPHID()))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Repository'))
->setOptions($repos)
->setName('repository')
->setValue($project->getRepositoryID()))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Indexed Languages'))
->setName('symbolIndexLanguages')
->setCaption(pht(
'Separate with commas, for example: %s',
phutil_tag('tt', array(), 'php, py')))
->setValue($langs))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Uses Symbols From'))
->setName('symbolIndexProjects')
->setDatasource(new DiffusionArcanistProjectDatasource())
->setValue($uses))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton('/repository/')
->setValue('Save'));
$panel = new PHUIObjectBoxView();
$panel->setHeaderText(pht('Edit Arcanist Project'));
$panel->setForm($form);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit Project'));
return $this->buildApplicationPage(
array(
$crumbs,
$panel,
),
array(
'title' => pht('Edit Project'),
));
}
}
diff --git a/src/applications/repository/query/PhabricatorRepositoryPushLogSearchEngine.php b/src/applications/repository/query/PhabricatorRepositoryPushLogSearchEngine.php
index 25cb871cfc..85cf0938cb 100644
--- a/src/applications/repository/query/PhabricatorRepositoryPushLogSearchEngine.php
+++ b/src/applications/repository/query/PhabricatorRepositoryPushLogSearchEngine.php
@@ -1,134 +1,118 @@
setParameter(
'repositoryPHIDs',
$this->readPHIDsFromRequest(
$request,
'repositories',
array(
PhabricatorRepositoryRepositoryPHIDType::TYPECONST,
)));
$saved->setParameter(
'pusherPHIDs',
$this->readUsersFromRequest(
$request,
'pushers'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorRepositoryPushLogQuery());
$repository_phids = $saved->getParameter('repositoryPHIDs');
if ($repository_phids) {
$query->withRepositoryPHIDs($repository_phids);
}
$pusher_phids = $saved->getParameter('pusherPHIDs');
if ($pusher_phids) {
$query->withPusherPHIDs($pusher_phids);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
$repository_phids = $saved_query->getParameter('repositoryPHIDs', array());
$pusher_phids = $saved_query->getParameter('pusherPHIDs', array());
- $all_phids = array_merge(
- $repository_phids,
- $pusher_phids);
-
- if ($all_phids) {
- $handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($all_phids)
- ->execute();
- } else {
- $handles = array();
- }
-
- $repository_handles = array_select_keys($handles, $repository_phids);
- $pusher_handles = array_select_keys($handles, $pusher_phids);
-
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new DiffusionRepositoryDatasource())
->setName('repositories')
->setLabel(pht('Repositories'))
- ->setValue($repository_handles))
- ->appendChild(
+ ->setValue($repository_phids))
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('pushers')
->setLabel(pht('Pushers'))
- ->setValue($pusher_handles));
+ ->setValue($pusher_phids));
}
protected function getURI($path) {
return '/diffusion/pushlog/'.$path;
}
protected function getBuiltinQueryNames() {
return array(
'all' => pht('All Push Logs'),
);
}
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 getRequiredHandlePHIDsForResultList(
array $logs,
PhabricatorSavedQuery $query) {
return mpull($logs, 'getPusherPHID');
}
protected function renderResultList(
array $logs,
PhabricatorSavedQuery $query,
array $handles) {
$table = id(new DiffusionPushLogListView())
->setUser($this->requireViewer())
->setHandles($handles)
->setLogs($logs);
$box = id(new PHUIBoxView())
->addMargin(PHUI::MARGIN_LARGE)
->appendChild($table);
return $box;
}
}
diff --git a/src/applications/repository/query/PhabricatorRepositorySearchEngine.php b/src/applications/repository/query/PhabricatorRepositorySearchEngine.php
index fe7736fea2..103f07c87f 100644
--- a/src/applications/repository/query/PhabricatorRepositorySearchEngine.php
+++ b/src/applications/repository/query/PhabricatorRepositorySearchEngine.php
@@ -1,307 +1,298 @@
setParameter('callsigns', $request->getStrList('callsigns'));
$saved->setParameter('status', $request->getStr('status'));
$saved->setParameter('order', $request->getStr('order'));
$saved->setParameter('hosted', $request->getStr('hosted'));
$saved->setParameter('types', $request->getArr('types'));
$saved->setParameter('name', $request->getStr('name'));
$saved->setParameter('anyProjectPHIDs', $request->getArr('anyProjects'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorRepositoryQuery())
->needProjectPHIDs(true)
->needCommitCounts(true)
->needMostRecentCommits(true);
$callsigns = $saved->getParameter('callsigns');
if ($callsigns) {
$query->withCallsigns($callsigns);
}
$status = $saved->getParameter('status');
$status = idx($this->getStatusValues(), $status);
if ($status) {
$query->withStatus($status);
}
$order = $saved->getParameter('order');
$order = idx($this->getOrderValues(), $order);
if ($order) {
$query->setOrder($order);
} else {
$query->setOrder(head($this->getOrderValues()));
}
$hosted = $saved->getParameter('hosted');
$hosted = idx($this->getHostedValues(), $hosted);
if ($hosted) {
$query->withHosted($hosted);
}
$types = $saved->getParameter('types');
if ($types) {
$query->withTypes($types);
}
$name = $saved->getParameter('name');
if (strlen($name)) {
$query->withNameContains($name);
}
$any_project_phids = $saved->getParameter('anyProjectPHIDs');
if ($any_project_phids) {
$query->withAnyProjects($any_project_phids);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
$callsigns = $saved_query->getParameter('callsigns', array());
$types = $saved_query->getParameter('types', array());
$types = array_fuse($types);
$name = $saved_query->getParameter('name');
$any_project_phids = $saved_query->getParameter('anyProjectPHIDs', array());
- if ($any_project_phids) {
- $any_project_handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($any_project_phids)
- ->execute();
- } else {
- $any_project_handles = array();
- }
-
$form
->appendChild(
id(new AphrontFormTextControl())
->setName('callsigns')
->setLabel(pht('Callsigns'))
->setValue(implode(', ', $callsigns)))
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setLabel(pht('Name Contains'))
->setValue($name))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorProjectDatasource())
->setName('anyProjects')
->setLabel(pht('In Any Project'))
- ->setValue($any_project_handles))
+ ->setValue($any_project_phids))
->appendChild(
id(new AphrontFormSelectControl())
->setName('status')
->setLabel(pht('Status'))
->setValue($saved_query->getParameter('status'))
->setOptions($this->getStatusOptions()))
->appendChild(
id(new AphrontFormSelectControl())
->setName('hosted')
->setLabel(pht('Hosted'))
->setValue($saved_query->getParameter('hosted'))
->setOptions($this->getHostedOptions()));
$type_control = id(new AphrontFormCheckboxControl())
->setLabel(pht('Types'));
$all_types = PhabricatorRepositoryType::getAllRepositoryTypes();
foreach ($all_types as $key => $name) {
$type_control->addCheckbox(
'types[]',
$key,
$name,
isset($types[$key]));
}
$form
->appendChild($type_control)
->appendChild(
id(new AphrontFormSelectControl())
->setName('order')
->setLabel(pht('Order'))
->setValue($saved_query->getParameter('order'))
->setOptions($this->getOrderOptions()));
}
protected function getURI($path) {
return '/diffusion/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'active' => pht('Active Repositories'),
'all' => pht('All Repositories'),
);
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'active':
return $query->setParameter('status', 'open');
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
private function getStatusOptions() {
return array(
'' => pht('Active and Inactive Repositories'),
'open' => pht('Active Repositories'),
'closed' => pht('Inactive Repositories'),
);
}
private function getStatusValues() {
return array(
'' => PhabricatorRepositoryQuery::STATUS_ALL,
'open' => PhabricatorRepositoryQuery::STATUS_OPEN,
'closed' => PhabricatorRepositoryQuery::STATUS_CLOSED,
);
}
private function getOrderOptions() {
return array(
'committed' => pht('Most Recent Commit'),
'name' => pht('Name'),
'callsign' => pht('Callsign'),
'created' => pht('Date Created'),
'size' => pht('Commit Count'),
);
}
private function getOrderValues() {
return array(
'committed' => PhabricatorRepositoryQuery::ORDER_COMMITTED,
'name' => PhabricatorRepositoryQuery::ORDER_NAME,
'callsign' => PhabricatorRepositoryQuery::ORDER_CALLSIGN,
'created' => PhabricatorRepositoryQuery::ORDER_CREATED,
'size' => PhabricatorRepositoryQuery::ORDER_SIZE,
);
}
private function getHostedOptions() {
return array(
'' => pht('Hosted and Remote Repositories'),
'phabricator' => pht('Hosted Repositories'),
'remote' => pht('Remote Repositories'),
);
}
private function getHostedValues() {
return array(
'' => PhabricatorRepositoryQuery::HOSTED_ALL,
'phabricator' => PhabricatorRepositoryQuery::HOSTED_PHABRICATOR,
'remote' => PhabricatorRepositoryQuery::HOSTED_REMOTE,
);
}
protected function getRequiredHandlePHIDsForResultList(
array $repositories,
PhabricatorSavedQuery $query) {
return array_mergev(mpull($repositories, 'getProjectPHIDs'));
}
protected function renderResultList(
array $repositories,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($repositories, 'PhabricatorRepository');
$viewer = $this->requireViewer();;
$list = new PHUIObjectItemListView();
foreach ($repositories as $repository) {
$id = $repository->getID();
$item = id(new PHUIObjectItemView())
->setUser($viewer)
->setHeader($repository->getName())
->setObjectName('r'.$repository->getCallsign())
->setHref($this->getApplicationURI($repository->getCallsign().'/'));
$commit = $repository->getMostRecentCommit();
if ($commit) {
$commit_link = DiffusionView::linkCommit(
$repository,
$commit->getCommitIdentifier(),
$commit->getSummary());
$item->setSubhead($commit_link);
$item->setEpoch($commit->getEpoch());
}
$item->addIcon(
'none',
PhabricatorRepositoryType::getNameForRepositoryType(
$repository->getVersionControlSystem()));
$size = $repository->getCommitCount();
if ($size) {
$history_uri = DiffusionRequest::generateDiffusionURI(
array(
'callsign' => $repository->getCallsign(),
'action' => 'history',
));
$item->addAttribute(
phutil_tag(
'a',
array(
'href' => $history_uri,
),
pht('%s Commit(s)', new PhutilNumber($size))));
} else {
$item->addAttribute(pht('No Commits'));
}
$project_handles = array_select_keys(
$handles,
$repository->getProjectPHIDs());
if ($project_handles) {
$item->addAttribute(
id(new PHUIHandleTagListView())
->setSlim(true)
->setHandles($project_handles));
}
if (!$repository->isTracked()) {
$item->setDisabled(true);
$item->addIcon('disable-grey', pht('Inactive'));
}
$list->addItem($item);
}
return $list;
}
}
diff --git a/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php b/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php
index 0e13f9ffea..3a8f822c8a 100644
--- a/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php
+++ b/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php
@@ -1,274 +1,258 @@
setParameter('query', $request->getStr('query'));
$saved->setParameter(
'statuses',
$this->readListFromRequest($request, 'statuses'));
$saved->setParameter(
'types',
$this->readListFromRequest($request, 'types'));
$saved->setParameter(
'authorPHIDs',
$this->readUsersFromRequest($request, 'authorPHIDs'));
$saved->setParameter(
'ownerPHIDs',
$this->readUsersFromRequest($request, 'ownerPHIDs'));
$saved->setParameter(
'withUnowned',
$this->readBoolFromRequest($request, 'withUnowned'));
$saved->setParameter(
'subscriberPHIDs',
$this->readPHIDsFromRequest($request, 'subscriberPHIDs'));
$saved->setParameter(
'projectPHIDs',
$this->readPHIDsFromRequest($request, 'projectPHIDs'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorSearchDocumentQuery())
->withSavedQuery($saved);
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved) {
$options = array();
$author_value = null;
$owner_value = null;
$subscribers_value = null;
$project_value = null;
$author_phids = $saved->getParameter('authorPHIDs', array());
$owner_phids = $saved->getParameter('ownerPHIDs', array());
$subscriber_phids = $saved->getParameter('subscriberPHIDs', array());
$project_phids = $saved->getParameter('projectPHIDs', array());
- $all_phids = array_merge(
- $author_phids,
- $owner_phids,
- $subscriber_phids,
- $project_phids);
-
- $all_handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($all_phids)
- ->execute();
-
- $author_handles = array_select_keys($all_handles, $author_phids);
- $owner_handles = array_select_keys($all_handles, $owner_phids);
- $subscriber_handles = array_select_keys($all_handles, $subscriber_phids);
- $project_handles = array_select_keys($all_handles, $project_phids);
-
$with_unowned = $saved->getParameter('withUnowned', array());
$status_values = $saved->getParameter('statuses', array());
$status_values = array_fuse($status_values);
$statuses = array(
PhabricatorSearchRelationship::RELATIONSHIP_OPEN => pht('Open'),
PhabricatorSearchRelationship::RELATIONSHIP_CLOSED => pht('Closed'),
);
$status_control = id(new AphrontFormCheckboxControl())
->setLabel(pht('Document Status'));
foreach ($statuses as $status => $name) {
$status_control->addCheckbox(
'statuses[]',
$status,
$name,
isset($status_values[$status]));
}
$type_values = $saved->getParameter('types', array());
$type_values = array_fuse($type_values);
$types = self::getIndexableDocumentTypes($this->requireViewer());
$types_control = id(new AphrontFormCheckboxControl())
->setLabel(pht('Document Types'));
foreach ($types as $type => $name) {
$types_control->addCheckbox(
'types[]',
$type,
$name,
isset($type_values[$type]));
}
$form
->appendChild(
phutil_tag(
'input',
array(
'type' => 'hidden',
'name' => 'jump',
'value' => 'no',
)))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Query')
->setName('query')
->setValue($saved->getParameter('query')))
->appendChild($status_control)
->appendChild($types_control)
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setName('authorPHIDs')
->setLabel('Authors')
->setDatasource(new PhabricatorPeopleDatasource())
- ->setValue($author_handles))
- ->appendChild(
+ ->setValue($author_phids))
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setName('ownerPHIDs')
->setLabel('Owners')
->setDatasource(new PhabricatorTypeaheadOwnerDatasource())
- ->setValue($owner_handles))
+ ->setValue($owner_phids))
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'withUnowned',
1,
pht('Show only unowned documents.'),
$with_unowned))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setName('subscriberPHIDs')
->setLabel('Subscribers')
->setDatasource(new PhabricatorPeopleDatasource())
- ->setValue($subscriber_handles))
- ->appendChild(
+ ->setValue($subscriber_phids))
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setName('projectPHIDs')
->setLabel('In Any Project')
->setDatasource(new PhabricatorProjectDatasource())
- ->setValue($project_handles));
+ ->setValue($project_phids));
}
protected function getURI($path) {
return '/search/'.$path;
}
protected function getBuiltinQueryNames() {
return array(
'all' => pht('All Documents'),
'open' => pht('Open Documents'),
'open-tasks' => pht('Open Tasks'),
);
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
case 'open':
return $query->setParameter('statuses', array('open'));
case 'open-tasks':
return $query
->setParameter('statuses', array('open'))
->setParameter('types', array(ManiphestTaskPHIDType::TYPECONST));
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
public static function getIndexableDocumentTypes(
PhabricatorUser $viewer = null) {
// TODO: This is inelegant and not very efficient, but gets us reasonable
// results. It would be nice to do this more elegantly.
$indexers = id(new PhutilSymbolLoader())
->setAncestorClass('PhabricatorSearchDocumentIndexer')
->loadObjects();
if ($viewer) {
$types = PhabricatorPHIDType::getAllInstalledTypes($viewer);
} else {
$types = PhabricatorPHIDType::getAllTypes();
}
$results = array();
foreach ($types as $type) {
$typeconst = $type->getTypeConstant();
foreach ($indexers as $indexer) {
$fake_phid = 'PHID-'.$typeconst.'-fake';
if ($indexer->shouldIndexDocumentByPHID($fake_phid)) {
$results[$typeconst] = $type->getTypeName();
}
}
}
asort($results);
// Put tasks first, see T4606.
$results = array_select_keys(
$results,
array(
ManiphestTaskPHIDType::TYPECONST,
)) + $results;
return $results;
}
public function shouldUseOffsetPaging() {
return true;
}
protected function renderResultList(
array $results,
PhabricatorSavedQuery $query,
array $handles) {
$viewer = $this->requireViewer();
if ($results) {
$objects = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withPHIDs(mpull($results, 'getPHID'))
->execute();
$list = new PHUIObjectItemListView();
foreach ($results as $phid => $handle) {
$view = id(new PhabricatorSearchResultView())
->setHandle($handle)
->setQuery($query)
->setObject(idx($objects, $phid))
->render();
$list->addItem($view);
}
$results = $list;
} else {
$results = id(new PHUIInfoView())
->appendChild(pht('No results returned for that query.'))
->setSeverity(PHUIInfoView::SEVERITY_NODATA);
}
return $results;
}
}
diff --git a/src/applications/slowvote/controller/PhabricatorSlowvoteEditController.php b/src/applications/slowvote/controller/PhabricatorSlowvoteEditController.php
index d84b255086..7374ea6468 100644
--- a/src/applications/slowvote/controller/PhabricatorSlowvoteEditController.php
+++ b/src/applications/slowvote/controller/PhabricatorSlowvoteEditController.php
@@ -1,283 +1,277 @@
id = idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
if ($this->id) {
$poll = id(new PhabricatorSlowvoteQuery())
->setViewer($user)
->withIDs(array($this->id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$poll) {
return new Aphront404Response();
}
$is_new = false;
} else {
$poll = PhabricatorSlowvotePoll::initializeNewPoll($user);
$is_new = true;
}
if ($is_new) {
$v_projects = array();
} else {
$v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
$poll->getPHID(),
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
$v_projects = array_reverse($v_projects);
}
$e_question = true;
$e_response = true;
$errors = array();
$v_question = $poll->getQuestion();
$v_description = $poll->getDescription();
$v_responses = $poll->getResponseVisibility();
$v_shuffle = $poll->getShuffle();
$responses = $request->getArr('response');
if ($request->isFormPost()) {
$v_question = $request->getStr('question');
$v_description = $request->getStr('description');
$v_responses = (int)$request->getInt('responses');
$v_shuffle = (int)$request->getBool('shuffle');
$v_view_policy = $request->getStr('viewPolicy');
$v_projects = $request->getArr('projects');
if ($is_new) {
$poll->setMethod($request->getInt('method'));
}
if (!strlen($v_question)) {
$e_question = pht('Required');
$errors[] = pht('You must ask a poll question.');
} else {
$e_question = null;
}
if ($is_new) {
$responses = array_filter($responses);
if (empty($responses)) {
$errors[] = pht('You must offer at least one response.');
$e_response = pht('Required');
} else {
$e_response = null;
}
}
$xactions = array();
$template = id(new PhabricatorSlowvoteTransaction());
$xactions[] = id(clone $template)
->setTransactionType(PhabricatorSlowvoteTransaction::TYPE_QUESTION)
->setNewValue($v_question);
$xactions[] = id(clone $template)
->setTransactionType(PhabricatorSlowvoteTransaction::TYPE_DESCRIPTION)
->setNewValue($v_description);
$xactions[] = id(clone $template)
->setTransactionType(PhabricatorSlowvoteTransaction::TYPE_RESPONSES)
->setNewValue($v_responses);
$xactions[] = id(clone $template)
->setTransactionType(PhabricatorSlowvoteTransaction::TYPE_SHUFFLE)
->setNewValue($v_shuffle);
$xactions[] = id(clone $template)
->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
->setNewValue($v_view_policy);
if (empty($errors)) {
$proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
$xactions[] = id(new PhabricatorSlowvoteTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $proj_edge_type)
->setNewValue(array('=' => array_fuse($v_projects)));
$editor = id(new PhabricatorSlowvoteEditor())
->setActor($user)
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request);
$xactions = $editor->applyTransactions($poll, $xactions);
if ($is_new) {
$poll->save();
foreach ($responses as $response) {
$option = new PhabricatorSlowvoteOption();
$option->setName($response);
$option->setPollID($poll->getID());
$option->save();
}
}
return id(new AphrontRedirectResponse())
->setURI('/V'.$poll->getID());
} else {
$poll->setViewPolicy($v_view_policy);
}
}
$instructions =
phutil_tag(
'p',
array(
'class' => 'aphront-form-instructions',
),
pht('Resolve issues and build consensus through '.
'protracted deliberation.'));
- if ($v_projects) {
- $project_handles = $this->loadViewerHandles($v_projects);
- } else {
- $project_handles = array();
- }
-
$form = id(new AphrontFormView())
->setUser($user)
->appendChild($instructions)
->appendChild(
id(new AphrontFormTextAreaControl())
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)
->setLabel(pht('Question'))
->setName('question')
->setValue($v_question)
->setError($e_question))
->appendChild(
id(new PhabricatorRemarkupControl())
->setUser($user)
->setLabel(pht('Description'))
->setName('description')
->setValue($v_description))
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Projects'))
->setName('projects')
- ->setValue($project_handles)
+ ->setValue($v_projects)
->setDatasource(new PhabricatorProjectDatasource()));
if ($is_new) {
for ($ii = 0; $ii < 10; $ii++) {
$n = ($ii + 1);
$response = id(new AphrontFormTextControl())
->setLabel(pht('Response %d', $n))
->setName('response[]')
->setValue(idx($responses, $ii, ''));
if ($ii == 0) {
$response->setError($e_response);
}
$form->appendChild($response);
}
}
$poll_type_options = array(
PhabricatorSlowvotePoll::METHOD_PLURALITY =>
pht('Plurality (Single Choice)'),
PhabricatorSlowvotePoll::METHOD_APPROVAL =>
pht('Approval (Multiple Choice)'),
);
$response_type_options = array(
PhabricatorSlowvotePoll::RESPONSES_VISIBLE
=> pht('Allow anyone to see the responses'),
PhabricatorSlowvotePoll::RESPONSES_VOTERS
=> pht('Require a vote to see the responses'),
PhabricatorSlowvotePoll::RESPONSES_OWNER
=> pht('Only I can see the responses'),
);
if ($is_new) {
$form->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Vote Type'))
->setName('method')
->setValue($poll->getMethod())
->setOptions($poll_type_options));
} else {
$form->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Vote Type'))
->setValue(idx($poll_type_options, $poll->getMethod())));
}
if ($is_new) {
$title = pht('Create Slowvote');
$button = pht('Create');
$cancel_uri = $this->getApplicationURI();
} else {
$title = pht('Edit %s', 'V'.$poll->getID());
$button = pht('Save Changes');
$cancel_uri = '/V'.$poll->getID();
}
$policies = id(new PhabricatorPolicyQuery())
->setViewer($user)
->setObject($poll)
->execute();
$form
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Responses'))
->setName('responses')
->setValue($v_responses)
->setOptions($response_type_options))
->appendChild(
id(new AphrontFormCheckboxControl())
->setLabel(pht('Shuffle'))
->addCheckbox(
'shuffle',
1,
pht('Show choices in random order.'),
$v_shuffle))
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($user)
->setName('viewPolicy')
->setPolicyObject($poll)
->setPolicies($policies)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue($button)
->addCancelButton($cancel_uri));
$crumbs = $this->buildApplicationCrumbs($this->buildSideNavView());
$crumbs->addTextCrumb($title);
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setFormErrors($errors)
->setForm($form);
return $this->buildApplicationPage(
array(
$crumbs,
$form_box,
),
array(
'title' => $title,
));
}
}
diff --git a/src/applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php b/src/applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php
index 33520978a1..fdbd951222 100644
--- a/src/applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php
+++ b/src/applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php
@@ -1,177 +1,173 @@
setParameter(
'authorPHIDs',
$this->readUsersFromRequest($request, 'authors'));
$saved->setParameter('voted', $request->getBool('voted'));
$saved->setParameter('statuses', $request->getArr('statuses'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorSlowvoteQuery())
->withAuthorPHIDs($saved->getParameter('authorPHIDs', array()));
if ($saved->getParameter('voted')) {
$query->withVotesByViewer(true);
}
$statuses = $saved->getParameter('statuses', array());
if (count($statuses) == 1) {
$status = head($statuses);
if ($status == 'open') {
$query->withIsClosed(false);
} else {
$query->withIsClosed(true);
}
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
- $phids = $saved_query->getParameter('authorPHIDs', array());
- $author_handles = id(new PhabricatorHandleQuery())
- ->setViewer($this->requireViewer())
- ->withPHIDs($phids)
- ->execute();
+ $author_phids = $saved_query->getParameter('authorPHIDs', array());
$voted = $saved_query->getParameter('voted', false);
$statuses = $saved_query->getParameter('statuses', array());
$form
- ->appendChild(
+ ->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('authors')
->setLabel(pht('Authors'))
- ->setValue($author_handles))
+ ->setValue($author_phids))
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'voted',
1,
pht("Show only polls I've voted in."),
$voted))
->appendChild(
id(new AphrontFormCheckboxControl())
->setLabel(pht('Status'))
->addCheckbox(
'statuses[]',
'open',
pht('Open'),
in_array('open', $statuses))
->addCheckbox(
'statuses[]',
'closed',
pht('Closed'),
in_array('closed', $statuses)));
}
protected function getURI($path) {
return '/vote/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'open' => pht('Open Polls'),
'all' => pht('All Polls'),
);
if ($this->requireViewer()->isLoggedIn()) {
$names['authored'] = pht('Authored');
$names['voted'] = pht('Voted In');
}
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'open':
return $query->setParameter('statuses', array('open'));
case 'all':
return $query;
case 'authored':
return $query->setParameter(
'authorPHIDs',
array($this->requireViewer()->getPHID()));
case 'voted':
return $query->setParameter('voted', true);
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function getRequiredHandlePHIDsForResultList(
array $polls,
PhabricatorSavedQuery $query) {
return mpull($polls, 'getAuthorPHID');
}
protected function renderResultList(
array $polls,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($polls, 'PhabricatorSlowvotePoll');
$viewer = $this->requireViewer();
$list = id(new PHUIObjectItemListView())
->setUser($viewer);
$phids = mpull($polls, 'getAuthorPHID');
foreach ($polls as $poll) {
$date_created = phabricator_datetime($poll->getDateCreated(), $viewer);
if ($poll->getAuthorPHID()) {
$author = $handles[$poll->getAuthorPHID()]->renderLink();
} else {
$author = null;
}
$item = id(new PHUIObjectItemView())
->setObjectName('V'.$poll->getID())
->setHeader($poll->getQuestion())
->setHref('/V'.$poll->getID())
->setDisabled($poll->getIsClosed())
->addIcon('none', $date_created);
$description = $poll->getDescription();
if (strlen($description)) {
$item->addAttribute(id(new PhutilUTF8StringTruncator())
->setMaximumGlyphs(120)
->truncateString($poll->getDescription()));
}
if ($author) {
$item->addByline(pht('Author: %s', $author));
}
$list->addItem($item);
}
return $list;
}
}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldUsers.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldUsers.php
index 440ac7dab7..7ea66a8a68 100644
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldUsers.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldUsers.php
@@ -1,52 +1,48 @@
getFieldValue();
- if ($value) {
- $control_value = array_select_keys($handles, $value);
- } else {
- $control_value = array();
- }
$control = id(new AphrontFormTokenizerControl())
+ ->setUser($this->getViewer())
->setLabel($this->getFieldName())
->setName($this->getFieldKey())
->setDatasource(new PhabricatorPeopleDatasource())
->setCaption($this->getCaption())
- ->setValue($control_value);
+ ->setValue(nonempty($value, array()));
$limit = $this->getFieldConfigValue('limit');
if ($limit) {
$control->setLimit($limit);
}
return $control;
}
public function appendToApplicationSearchForm(
PhabricatorApplicationSearchEngine $engine,
AphrontFormView $form,
$value,
array $handles) {
$control = id(new AphrontFormTokenizerControl())
->setLabel($this->getFieldName())
->setName($this->getFieldKey())
->setDatasource(new PhabricatorPeopleDatasource())
- ->setValue($handles);
+ ->setValue(nonempty($value, array()));
- $form->appendChild($control);
+ $form->appendControl($control);
}
public function getHeraldFieldValueType($condition) {
return HeraldAdapter::VALUE_USER;
}
}
diff --git a/src/view/form/AphrontFormView.php b/src/view/form/AphrontFormView.php
index f680800f59..ada943d532 100644
--- a/src/view/form/AphrontFormView.php
+++ b/src/view/form/AphrontFormView.php
@@ -1,156 +1,167 @@
metadata = $metadata;
return $this;
}
public function getMetadata() {
return $this->metadata;
}
public function setID($id) {
$this->id = $id;
return $this;
}
public function setAction($action) {
$this->action = $action;
return $this;
}
public function setMethod($method) {
$this->method = $method;
return $this;
}
public function setEncType($enc_type) {
$this->encType = $enc_type;
return $this;
}
public function setShaded($shaded) {
$this->shaded = $shaded;
return $this;
}
public function addHiddenInput($key, $value) {
$this->data[$key] = $value;
return $this;
}
public function setWorkflow($workflow) {
$this->workflow = $workflow;
return $this;
}
public function addSigil($sigil) {
$this->sigils[] = $sigil;
return $this;
}
+ public function setFullWidth($full_width) {
+ $this->fullWidth = $full_width;
+ return $this;
+ }
+
+ public function getFullWidth() {
+ return $this->fullWidth;
+ }
+
public function appendInstructions($text) {
return $this->appendChild(
phutil_tag(
'div',
array(
'class' => 'aphront-form-instructions',
),
$text));
}
public function appendRemarkupInstructions($remarkup) {
return $this->appendInstructions(
PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($remarkup),
'default',
$this->getUser()));
}
public function buildLayoutView() {
return id(new PHUIFormLayoutView())
+ ->setFullWidth($this->getFullWidth())
->appendChild($this->renderDataInputs())
->appendChild($this->renderChildren());
}
/**
* Append a control to the form.
*
* This method behaves like @{method:appendChild}, but it only takes
* controls. It will propagate some information from the form to the
* control to simplify rendering.
*
* @param AphrontFormControl Control to append.
* @return this
*/
public function appendControl(AphrontFormControl $control) {
$this->controls[] = $control;
return $this->appendChild($control);
}
public function render() {
require_celerity_resource('phui-form-view-css');
foreach ($this->controls as $control) {
$control->setUser($this->getUser());
}
$layout = $this->buildLayoutView();
if (!$this->user) {
throw new Exception(pht('You must pass the user to AphrontFormView.'));
}
$sigils = $this->sigils;
if ($this->workflow) {
$sigils[] = 'workflow';
}
return phabricator_form(
$this->user,
array(
'class' => $this->shaded ? 'phui-form-shaded' : null,
'action' => $this->action,
'method' => $this->method,
'enctype' => $this->encType,
'sigil' => $sigils ? implode(' ', $sigils) : null,
'meta' => $this->metadata,
'id' => $this->id,
),
$layout->render());
}
private function renderDataInputs() {
$inputs = array();
foreach ($this->data as $key => $value) {
if ($value === null) {
continue;
}
$inputs[] = phutil_tag(
'input',
array(
'type' => 'hidden',
'name' => $key,
'value' => $value,
));
}
return $inputs;
}
}