Page MenuHomePhorge

No OneTemporary

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
index 58aa364de5..30d6587bac 100644
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -1,2359 +1,2359 @@
<?php
/**
* This file is automatically generated. Use 'bin/celerity map' to rebuild it.
*
* @generated
*/
return array(
'names' => array(
'conpherence.pkg.css' => '3c8a0668',
'conpherence.pkg.js' => '020aebcf',
'core.pkg.css' => '34ce1741',
'core.pkg.js' => 'b96c872e',
'differential.pkg.css' => '8d8360fb',
'differential.pkg.js' => '67e02996',
'diffusion.pkg.css' => '42c75c37',
'diffusion.pkg.js' => '91192d85',
'maniphest.pkg.css' => '35995d6d',
'maniphest.pkg.js' => 'c9308721',
'rsrc/audio/basic/alert.mp3' => '17889334',
'rsrc/audio/basic/bing.mp3' => 'a817a0c3',
'rsrc/audio/basic/pock.mp3' => '0fa843d0',
'rsrc/audio/basic/tap.mp3' => '02d16994',
'rsrc/audio/basic/ting.mp3' => 'a6b6540e',
'rsrc/css/aphront/aphront-bars.css' => '4a327b4a',
'rsrc/css/aphront/dark-console.css' => '7f06cda2',
'rsrc/css/aphront/dialog-view.css' => 'b70c70df',
'rsrc/css/aphront/list-filter-view.css' => 'feb64255',
'rsrc/css/aphront/multi-column.css' => 'fbc00ba3',
'rsrc/css/aphront/notification.css' => '30240bd2',
'rsrc/css/aphront/panel-view.css' => '46923d46',
'rsrc/css/aphront/phabricator-nav-view.css' => 'f8a0c1bf',
'rsrc/css/aphront/table-view.css' => '205053cd',
'rsrc/css/aphront/tokenizer.css' => 'b52d0668',
'rsrc/css/aphront/tooltip.css' => 'e3f2412f',
'rsrc/css/aphront/typeahead-browse.css' => 'b7ed02d2',
'rsrc/css/aphront/typeahead.css' => '8779483d',
'rsrc/css/application/almanac/almanac.css' => '2e050f4f',
'rsrc/css/application/auth/auth.css' => 'add92fd8',
'rsrc/css/application/base/main-menu-view.css' => '8e2d9a28',
'rsrc/css/application/base/notification-menu.css' => 'e6962e89',
'rsrc/css/application/base/phui-theme.css' => '35883b37',
'rsrc/css/application/base/standard-page-view.css' => '8a295cb9',
'rsrc/css/application/chatlog/chatlog.css' => 'abdc76ee',
'rsrc/css/application/conduit/conduit-api.css' => 'ce2cfc41',
'rsrc/css/application/config/config-options.css' => '16c920ae',
'rsrc/css/application/config/config-template.css' => '20babf50',
'rsrc/css/application/config/setup-issue.css' => '5eed85b2',
'rsrc/css/application/config/unhandled-exception.css' => '9ecfc00d',
'rsrc/css/application/conpherence/color.css' => 'b17746b0',
'rsrc/css/application/conpherence/durable-column.css' => '2d57072b',
'rsrc/css/application/conpherence/header-pane.css' => 'c9a3db8e',
'rsrc/css/application/conpherence/menu.css' => '67f4680d',
'rsrc/css/application/conpherence/message-pane.css' => 'd244db1e',
'rsrc/css/application/conpherence/notification.css' => '6a3d4e58',
'rsrc/css/application/conpherence/participant-pane.css' => '69e0058a',
'rsrc/css/application/conpherence/transaction.css' => '3a3f5e7e',
'rsrc/css/application/contentsource/content-source-view.css' => 'cdf0d579',
'rsrc/css/application/countdown/timer.css' => 'bff8012f',
'rsrc/css/application/daemon/bulk-job.css' => '73af99f5',
'rsrc/css/application/dashboard/dashboard.css' => '4267d6c6',
'rsrc/css/application/diff/inline-comment-summary.css' => '81eb368d',
'rsrc/css/application/differential/add-comment.css' => '7e5900d9',
'rsrc/css/application/differential/changeset-view.css' => 'bde53589',
'rsrc/css/application/differential/core.css' => '7300a73e',
'rsrc/css/application/differential/phui-inline-comment.css' => '48acce5b',
'rsrc/css/application/differential/revision-comment.css' => '7dbc8d1d',
'rsrc/css/application/differential/revision-history.css' => '8aa3eac5',
'rsrc/css/application/differential/revision-list.css' => '93d2df7d',
'rsrc/css/application/differential/table-of-contents.css' => '0e3364c7',
'rsrc/css/application/diffusion/diffusion-icons.css' => '23b31a1b',
'rsrc/css/application/diffusion/diffusion-readme.css' => 'b68a76e4',
'rsrc/css/application/diffusion/diffusion-repository.css' => 'b89e8c6c',
'rsrc/css/application/diffusion/diffusion.css' => 'b54c77b0',
'rsrc/css/application/feed/feed.css' => 'd8b6e3f8',
'rsrc/css/application/files/global-drag-and-drop.css' => '1d2713a4',
'rsrc/css/application/flag/flag.css' => '2b77be8d',
'rsrc/css/application/harbormaster/harbormaster.css' => '8dfe16b2',
'rsrc/css/application/herald/herald-test.css' => 'e004176f',
'rsrc/css/application/herald/herald.css' => '648d39e2',
'rsrc/css/application/maniphest/report.css' => '3d53188b',
'rsrc/css/application/maniphest/task-edit.css' => '272daa84',
'rsrc/css/application/maniphest/task-summary.css' => '61d1667e',
'rsrc/css/application/objectselector/object-selector.css' => 'ee77366f',
'rsrc/css/application/owners/owners-path-editor.css' => 'fa7c13ef',
'rsrc/css/application/paste/paste.css' => 'b37bcd38',
'rsrc/css/application/people/people-picture-menu-item.css' => 'fe8e07cf',
'rsrc/css/application/people/people-profile.css' => '2ea2daa1',
'rsrc/css/application/phame/phame.css' => '799febf9',
'rsrc/css/application/pholio/pholio-edit.css' => '4df55b3b',
'rsrc/css/application/pholio/pholio-inline-comments.css' => '722b48c2',
'rsrc/css/application/pholio/pholio.css' => '88ef5ef1',
'rsrc/css/application/phortune/phortune-credit-card-form.css' => '3b9868a8',
'rsrc/css/application/phortune/phortune-invoice.css' => '4436b241',
'rsrc/css/application/phortune/phortune.css' => '12e8251a',
'rsrc/css/application/phrequent/phrequent.css' => 'bd79cc67',
'rsrc/css/application/phriction/phriction-document-css.css' => '03380da0',
'rsrc/css/application/policy/policy-edit.css' => '8794e2ed',
'rsrc/css/application/policy/policy-transaction-detail.css' => 'c02b8384',
'rsrc/css/application/policy/policy.css' => 'ceb56a08',
'rsrc/css/application/ponder/ponder-view.css' => '05a09d0a',
'rsrc/css/application/project/project-card-view.css' => '3b1f7b20',
'rsrc/css/application/project/project-view.css' => '567858b3',
'rsrc/css/application/releeph/releeph-core.css' => 'f81ff2db',
'rsrc/css/application/releeph/releeph-preview-branch.css' => '22db5c07',
'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '0ac1ea31',
'rsrc/css/application/releeph/releeph-request-typeahead.css' => 'bce37359',
'rsrc/css/application/search/application-search-view.css' => '0f7c06d8',
'rsrc/css/application/search/search-results.css' => '9ea70ace',
'rsrc/css/application/slowvote/slowvote.css' => '1694baed',
'rsrc/css/application/tokens/tokens.css' => 'ce5a50bd',
'rsrc/css/application/uiexample/example.css' => 'b4795059',
'rsrc/css/core/core.css' => '1b29ed61',
'rsrc/css/core/remarkup.css' => '9e627d41',
'rsrc/css/core/syntax.css' => '4234f572',
'rsrc/css/core/z-index.css' => '99c0f5eb',
'rsrc/css/diviner/diviner-shared.css' => '4bd263b0',
'rsrc/css/font/font-awesome.css' => '3883938a',
'rsrc/css/font/font-lato.css' => '23631304',
'rsrc/css/font/phui-font-icon-base.css' => 'd7994e06',
'rsrc/css/layout/phabricator-filetree-view.css' => '56cdd875',
'rsrc/css/layout/phabricator-source-code-view.css' => '03d7ac28',
'rsrc/css/phui/button/phui-button-bar.css' => 'a4aa75c4',
'rsrc/css/phui/button/phui-button-simple.css' => '1ff278aa',
'rsrc/css/phui/button/phui-button.css' => 'ea704902',
'rsrc/css/phui/calendar/phui-calendar-day.css' => '9597d706',
'rsrc/css/phui/calendar/phui-calendar-list.css' => 'ccd7e4e2',
'rsrc/css/phui/calendar/phui-calendar-month.css' => 'cb758c42',
'rsrc/css/phui/calendar/phui-calendar.css' => 'f11073aa',
'rsrc/css/phui/object-item/phui-oi-big-ui.css' => '534f1757',
'rsrc/css/phui/object-item/phui-oi-color.css' => 'b517bfa0',
'rsrc/css/phui/object-item/phui-oi-drag-ui.css' => 'da15d3dc',
'rsrc/css/phui/object-item/phui-oi-flush-ui.css' => '490e2e2e',
'rsrc/css/phui/object-item/phui-oi-list-view.css' => '909f3844',
'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => '6a30fa46',
'rsrc/css/phui/phui-action-list.css' => 'c4972757',
'rsrc/css/phui/phui-action-panel.css' => '6c386cbf',
'rsrc/css/phui/phui-badge.css' => '666e25ad',
'rsrc/css/phui/phui-basic-nav-view.css' => '56ebd66d',
'rsrc/css/phui/phui-big-info-view.css' => '362ad37b',
'rsrc/css/phui/phui-box.css' => '5ed3b8cb',
'rsrc/css/phui/phui-bulk-editor.css' => '374d5e30',
'rsrc/css/phui/phui-chart.css' => '7853a69b',
'rsrc/css/phui/phui-cms.css' => '8c05c41e',
'rsrc/css/phui/phui-comment-form.css' => '68a2d99a',
'rsrc/css/phui/phui-comment-panel.css' => 'ec4e31c0',
'rsrc/css/phui/phui-crumbs-view.css' => '614f43cf',
'rsrc/css/phui/phui-curtain-view.css' => '68c5efb6',
'rsrc/css/phui/phui-document-pro.css' => 'b9613a10',
'rsrc/css/phui/phui-document-summary.css' => 'b068eed1',
'rsrc/css/phui/phui-document.css' => '52b748a5',
'rsrc/css/phui/phui-feed-story.css' => 'a0c05029',
'rsrc/css/phui/phui-fontkit.css' => '9b714a5e',
'rsrc/css/phui/phui-form-view.css' => '01b796c0',
'rsrc/css/phui/phui-form.css' => '159e2d9c',
'rsrc/css/phui/phui-head-thing.css' => 'd7f293df',
'rsrc/css/phui/phui-header-view.css' => '285c9139',
'rsrc/css/phui/phui-hovercard.css' => '6ca90fa0',
'rsrc/css/phui/phui-icon-set-selector.css' => '7aa5f3ec',
'rsrc/css/phui/phui-icon.css' => '4cbc684a',
'rsrc/css/phui/phui-image-mask.css' => '62c7f4d2',
'rsrc/css/phui/phui-info-view.css' => '37b8d9ce',
'rsrc/css/phui/phui-invisible-character-view.css' => 'c694c4a4',
'rsrc/css/phui/phui-left-right.css' => '68513c34',
'rsrc/css/phui/phui-lightbox.css' => '4ebf22da',
'rsrc/css/phui/phui-list.css' => '470b1adb',
'rsrc/css/phui/phui-object-box.css' => 'f434b6be',
'rsrc/css/phui/phui-pager.css' => 'd022c7ad',
'rsrc/css/phui/phui-pinboard-view.css' => '1f08f5d8',
'rsrc/css/phui/phui-property-list-view.css' => 'cad62236',
'rsrc/css/phui/phui-remarkup-preview.css' => '91767007',
'rsrc/css/phui/phui-segment-bar-view.css' => '5166b370',
'rsrc/css/phui/phui-spacing.css' => 'b05cadc3',
'rsrc/css/phui/phui-status.css' => 'e5ff8be0',
'rsrc/css/phui/phui-tag-view.css' => '29409667',
'rsrc/css/phui/phui-timeline-view.css' => '1e348e4b',
'rsrc/css/phui/phui-two-column-view.css' => '01e6991e',
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'e86de308',
'rsrc/css/phui/workboards/phui-workboard.css' => '74fc9d98',
'rsrc/css/phui/workboards/phui-workcard.css' => '8c536f90',
'rsrc/css/phui/workboards/phui-workpanel.css' => 'bc16cf33',
'rsrc/css/sprite-login.css' => '18b368a6',
'rsrc/css/sprite-tokens.css' => 'f1896dc5',
'rsrc/css/syntax/syntax-default.css' => '055fc231',
'rsrc/externals/d3/d3.min.js' => 'd67475f5',
'rsrc/externals/font/fontawesome/fontawesome-webfont.eot' => '23f8c698',
'rsrc/externals/font/fontawesome/fontawesome-webfont.ttf' => '70983df0',
'rsrc/externals/font/fontawesome/fontawesome-webfont.woff' => 'cd02f93b',
'rsrc/externals/font/fontawesome/fontawesome-webfont.woff2' => '351fd46a',
'rsrc/externals/font/lato/lato-bold.eot' => '7367aa5e',
'rsrc/externals/font/lato/lato-bold.svg' => '681aa4f5',
'rsrc/externals/font/lato/lato-bold.ttf' => '66d3c296',
'rsrc/externals/font/lato/lato-bold.woff' => '89d9fba7',
'rsrc/externals/font/lato/lato-bold.woff2' => '389fcdb1',
'rsrc/externals/font/lato/lato-bolditalic.eot' => '03eeb4da',
'rsrc/externals/font/lato/lato-bolditalic.svg' => 'f56fa11c',
'rsrc/externals/font/lato/lato-bolditalic.ttf' => '9c3aec21',
'rsrc/externals/font/lato/lato-bolditalic.woff' => 'bfbd0616',
'rsrc/externals/font/lato/lato-bolditalic.woff2' => 'bc7d1274',
'rsrc/externals/font/lato/lato-italic.eot' => '7db5b247',
'rsrc/externals/font/lato/lato-italic.svg' => 'b1ae496f',
'rsrc/externals/font/lato/lato-italic.ttf' => '43eed813',
'rsrc/externals/font/lato/lato-italic.woff' => 'c28975e1',
'rsrc/externals/font/lato/lato-italic.woff2' => 'fffc0d8c',
'rsrc/externals/font/lato/lato-regular.eot' => '06e0c291',
'rsrc/externals/font/lato/lato-regular.svg' => '3ad95f53',
'rsrc/externals/font/lato/lato-regular.ttf' => 'e2e9c398',
'rsrc/externals/font/lato/lato-regular.woff' => '0b13d332',
'rsrc/externals/font/lato/lato-regular.woff2' => '8f846797',
'rsrc/externals/javelin/core/Event.js' => 'c03f2fb4',
'rsrc/externals/javelin/core/Stratcom.js' => '0889b835',
'rsrc/externals/javelin/core/__tests__/event-stop-and-kill.js' => '048472d2',
'rsrc/externals/javelin/core/__tests__/install.js' => '14a7e671',
'rsrc/externals/javelin/core/__tests__/stratcom.js' => 'a28464bb',
'rsrc/externals/javelin/core/__tests__/util.js' => 'e29a4354',
'rsrc/externals/javelin/core/init.js' => '98e6504a',
'rsrc/externals/javelin/core/init_node.js' => '16961339',
'rsrc/externals/javelin/core/install.js' => '5902260c',
'rsrc/externals/javelin/core/util.js' => '22ae1776',
'rsrc/externals/javelin/docs/Base.js' => '5a401d7d',
'rsrc/externals/javelin/docs/onload.js' => 'ee58fb62',
'rsrc/externals/javelin/ext/fx/Color.js' => '78f811c9',
'rsrc/externals/javelin/ext/fx/FX.js' => '34450586',
'rsrc/externals/javelin/ext/reactor/core/DynVal.js' => '202a2e85',
'rsrc/externals/javelin/ext/reactor/core/Reactor.js' => '1c850a26',
'rsrc/externals/javelin/ext/reactor/core/ReactorNode.js' => '72960bc1',
'rsrc/externals/javelin/ext/reactor/core/ReactorNodeCalmer.js' => '225bbb98',
'rsrc/externals/javelin/ext/reactor/dom/RDOM.js' => '6cfa0008',
'rsrc/externals/javelin/ext/view/HTMLView.js' => 'f8c4e135',
'rsrc/externals/javelin/ext/view/View.js' => '289bf236',
'rsrc/externals/javelin/ext/view/ViewInterpreter.js' => '876506b6',
'rsrc/externals/javelin/ext/view/ViewPlaceholder.js' => 'a9942052',
'rsrc/externals/javelin/ext/view/ViewRenderer.js' => '9aae2b66',
'rsrc/externals/javelin/ext/view/ViewVisitor.js' => '308f9fe4',
'rsrc/externals/javelin/ext/view/__tests__/HTMLView.js' => '6e50a13f',
'rsrc/externals/javelin/ext/view/__tests__/View.js' => 'd284be5d',
'rsrc/externals/javelin/ext/view/__tests__/ViewInterpreter.js' => 'a9f35511',
'rsrc/externals/javelin/ext/view/__tests__/ViewRenderer.js' => '3a1b81f6',
'rsrc/externals/javelin/lib/Cookie.js' => '05d290ef',
'rsrc/externals/javelin/lib/DOM.js' => '94681e22',
'rsrc/externals/javelin/lib/History.js' => '030b4f7a',
'rsrc/externals/javelin/lib/JSON.js' => '541f81c3',
'rsrc/externals/javelin/lib/Leader.js' => '0d2490ce',
'rsrc/externals/javelin/lib/Mask.js' => '7c4d8998',
'rsrc/externals/javelin/lib/Quicksand.js' => 'd3799cb4',
'rsrc/externals/javelin/lib/Request.js' => '91863989',
'rsrc/externals/javelin/lib/Resource.js' => '740956e1',
'rsrc/externals/javelin/lib/Routable.js' => '6a18c42e',
'rsrc/externals/javelin/lib/Router.js' => '32755edb',
'rsrc/externals/javelin/lib/Scrollbar.js' => 'a43ae2ae',
'rsrc/externals/javelin/lib/Sound.js' => 'e562708c',
'rsrc/externals/javelin/lib/URI.js' => '2e255291',
'rsrc/externals/javelin/lib/Vector.js' => 'e9c80beb',
'rsrc/externals/javelin/lib/WebSocket.js' => 'fdc13e4e',
'rsrc/externals/javelin/lib/Workflow.js' => '958e9045',
'rsrc/externals/javelin/lib/__tests__/Cookie.js' => 'ca686f71',
'rsrc/externals/javelin/lib/__tests__/DOM.js' => '4566e249',
'rsrc/externals/javelin/lib/__tests__/JSON.js' => '710377ae',
'rsrc/externals/javelin/lib/__tests__/URI.js' => '6fff0c2b',
'rsrc/externals/javelin/lib/__tests__/behavior.js' => '8426ebeb',
'rsrc/externals/javelin/lib/behavior.js' => 'fce5d170',
'rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js' => '89a1ae3a',
'rsrc/externals/javelin/lib/control/typeahead/Typeahead.js' => 'a4356cde',
'rsrc/externals/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js' => 'a241536a',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js' => '22ee68a5',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js' => '23387297',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js' => '5a79f6c3',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js' => '8badee71',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadStaticSource.js' => '80bff3af',
'rsrc/favicons/favicon-16x16.png' => '4c51a03a',
'rsrc/favicons/mask-icon.svg' => 'db699fe1',
'rsrc/image/BFCFDA.png' => '74b5c88b',
'rsrc/image/actions/edit.png' => 'fd987dff',
'rsrc/image/avatar.png' => '0d17c6c4',
'rsrc/image/checker_dark.png' => '7fc8fa7b',
'rsrc/image/checker_light.png' => '3157a202',
'rsrc/image/checker_lighter.png' => 'c45928c1',
'rsrc/image/chevron-in.png' => '1aa2f88f',
'rsrc/image/chevron-out.png' => 'c815e272',
'rsrc/image/controls/checkbox-checked.png' => '1770d7a0',
'rsrc/image/controls/checkbox-unchecked.png' => 'e1deba0a',
'rsrc/image/d5d8e1.png' => '6764616e',
'rsrc/image/darkload.gif' => '5bd41a89',
'rsrc/image/divot.png' => '0fbe2453',
'rsrc/image/examples/hero.png' => '5d8c4b21',
'rsrc/image/grippy_texture.png' => 'a7d222b5',
'rsrc/image/icon/fatcow/arrow_branch.png' => '98149d9f',
'rsrc/image/icon/fatcow/arrow_merge.png' => 'e142f4f8',
'rsrc/image/icon/fatcow/calendar_edit.png' => '5ff44a08',
'rsrc/image/icon/fatcow/document_black.png' => 'd3515fa5',
'rsrc/image/icon/fatcow/flag_blue.png' => '54db2e5c',
'rsrc/image/icon/fatcow/flag_finish.png' => '2953a51b',
'rsrc/image/icon/fatcow/flag_ghost.png' => '7d9ada92',
'rsrc/image/icon/fatcow/flag_green.png' => '010f7161',
'rsrc/image/icon/fatcow/flag_orange.png' => '6c384ca5',
'rsrc/image/icon/fatcow/flag_pink.png' => '11ac6b12',
'rsrc/image/icon/fatcow/flag_purple.png' => 'c4f423a4',
'rsrc/image/icon/fatcow/flag_red.png' => '9e6d8817',
'rsrc/image/icon/fatcow/flag_yellow.png' => '906733f4',
'rsrc/image/icon/fatcow/key_question.png' => 'c10c26db',
'rsrc/image/icon/fatcow/link.png' => '8edbf327',
'rsrc/image/icon/fatcow/page_white_edit.png' => '17ef5625',
'rsrc/image/icon/fatcow/page_white_put.png' => '82430c91',
'rsrc/image/icon/fatcow/source/conduit.png' => '5b55130c',
'rsrc/image/icon/fatcow/source/email.png' => '8a32b77f',
'rsrc/image/icon/fatcow/source/fax.png' => '8bc2a49b',
'rsrc/image/icon/fatcow/source/mobile.png' => '0a918412',
'rsrc/image/icon/fatcow/source/tablet.png' => 'fc50b050',
'rsrc/image/icon/fatcow/source/web.png' => '70433af3',
'rsrc/image/icon/subscribe.png' => '07ef454e',
'rsrc/image/icon/tango/attachment.png' => 'bac9032d',
'rsrc/image/icon/tango/edit.png' => 'e6296206',
'rsrc/image/icon/tango/go-down.png' => '0b903712',
'rsrc/image/icon/tango/log.png' => '86b6a6f4',
'rsrc/image/icon/tango/upload.png' => '3fe6b92d',
'rsrc/image/icon/unsubscribe.png' => 'db04378a',
'rsrc/image/lightblue-header.png' => 'e6d483c6',
'rsrc/image/logo/light-eye.png' => '72337472',
'rsrc/image/main_texture.png' => '894d03c4',
'rsrc/image/menu_texture.png' => '896c9ade',
'rsrc/image/people/harding.png' => '95b2db63',
'rsrc/image/people/jefferson.png' => 'e883a3a2',
'rsrc/image/people/lincoln.png' => 'be2c07c5',
'rsrc/image/people/mckinley.png' => '6af510a0',
'rsrc/image/people/taft.png' => 'b15ab07e',
'rsrc/image/people/user0.png' => '4bc64b40',
'rsrc/image/people/user1.png' => '8063f445',
'rsrc/image/people/user2.png' => 'd28246c0',
'rsrc/image/people/user3.png' => 'fb1ac12d',
'rsrc/image/people/user4.png' => 'fe4fac8f',
'rsrc/image/people/user5.png' => '3d07065c',
'rsrc/image/people/user6.png' => 'e4bd47c8',
'rsrc/image/people/user7.png' => '71d8fe8b',
'rsrc/image/people/user8.png' => '85f86bf7',
'rsrc/image/people/user9.png' => '523db8aa',
'rsrc/image/people/washington.png' => '86159e68',
'rsrc/image/phrequent_active.png' => 'de66dc50',
'rsrc/image/phrequent_inactive.png' => '79c61baf',
'rsrc/image/resize.png' => '9cc83373',
'rsrc/image/sprite-login-X2.png' => '604545f6',
'rsrc/image/sprite-login.png' => '7a001a9a',
'rsrc/image/sprite-tokens-X2.png' => '21621dd9',
'rsrc/image/sprite-tokens.png' => 'bede2580',
'rsrc/image/texture/card-gradient.png' => 'e6892cb4',
'rsrc/image/texture/dark-menu-hover.png' => '390a4fa1',
'rsrc/image/texture/dark-menu.png' => '542f699c',
'rsrc/image/texture/grip.png' => 'bc80753a',
'rsrc/image/texture/panel-header-gradient.png' => '65004dbf',
'rsrc/image/texture/phlnx-bg.png' => '6c9cd31d',
'rsrc/image/texture/pholio-background.gif' => '84910bfc',
'rsrc/image/texture/table_header.png' => '7652d1ad',
'rsrc/image/texture/table_header_hover.png' => '12ea5236',
'rsrc/image/texture/table_header_tall.png' => '5cc420c4',
'rsrc/js/application/aphlict/Aphlict.js' => '022516b4',
'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => 'e9a2940f',
'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => '4e61fa88',
'rsrc/js/application/aphlict/behavior-aphlict-status.js' => 'c3703a16',
'rsrc/js/application/aphlict/behavior-desktop-notifications-control.js' => '070679fe',
'rsrc/js/application/calendar/behavior-day-view.js' => '727a5a61',
'rsrc/js/application/calendar/behavior-event-all-day.js' => '0b1bc990',
'rsrc/js/application/calendar/behavior-month-view.js' => '158c64e0',
'rsrc/js/application/config/behavior-reorder-fields.js' => '2539f834',
'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => 'aec8e38c',
'rsrc/js/application/conpherence/behavior-conpherence-search.js' => '91befbcc',
'rsrc/js/application/conpherence/behavior-durable-column.js' => 'fa6f30b2',
'rsrc/js/application/conpherence/behavior-menu.js' => '8c2ed2bf',
'rsrc/js/application/conpherence/behavior-participant-pane.js' => '43ba89a2',
'rsrc/js/application/conpherence/behavior-pontificate.js' => '4ae58b5a',
'rsrc/js/application/conpherence/behavior-quicksand-blacklist.js' => '5a6f6a06',
'rsrc/js/application/conpherence/behavior-toggle-widget.js' => '8f959ad0',
'rsrc/js/application/countdown/timer.js' => '6a162524',
'rsrc/js/application/daemon/behavior-bulk-job-reload.js' => '3829a3cf',
'rsrc/js/application/dashboard/behavior-dashboard-async-panel.js' => '09ecf50c',
'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '076bd092',
'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '1e413dc9',
'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => '9b1cbd76',
'rsrc/js/application/diff/DiffChangeset.js' => 'd0a85a85',
'rsrc/js/application/diff/DiffChangesetList.js' => '04023d82',
'rsrc/js/application/diff/DiffInline.js' => 'a4a14a94',
'rsrc/js/application/diff/behavior-preview-link.js' => 'f51e9c17',
'rsrc/js/application/differential/behavior-diff-radios.js' => '925fe8cd',
'rsrc/js/application/differential/behavior-populate.js' => 'dfa1d313',
'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => '94243d89',
'rsrc/js/application/diffusion/behavior-audit-preview.js' => 'b7b73831',
'rsrc/js/application/diffusion/behavior-commit-branches.js' => '4b671572',
'rsrc/js/application/diffusion/behavior-commit-graph.js' => '1c88f154',
'rsrc/js/application/diffusion/behavior-locate-file.js' => '87428eb2',
'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'c715c123',
'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => '6a85bc5a',
'rsrc/js/application/drydock/drydock-live-operation-status.js' => '47a0728b',
'rsrc/js/application/files/behavior-document-engine.js' => '243d6c22',
'rsrc/js/application/files/behavior-icon-composer.js' => '38a6cedb',
'rsrc/js/application/files/behavior-launch-icon-composer.js' => 'a17b84f1',
'rsrc/js/application/harbormaster/behavior-harbormaster-log.js' => 'b347a301',
'rsrc/js/application/herald/HeraldRuleEditor.js' => '27daef73',
'rsrc/js/application/herald/PathTypeahead.js' => 'ad486db3',
'rsrc/js/application/herald/herald-rule-editor.js' => '0922e81d',
'rsrc/js/application/maniphest/behavior-batch-selector.js' => '139ef688',
'rsrc/js/application/maniphest/behavior-line-chart.js' => 'c8147a20',
'rsrc/js/application/maniphest/behavior-list-edit.js' => 'c687e867',
'rsrc/js/application/owners/OwnersPathEditor.js' => '2a8b62d9',
'rsrc/js/application/owners/owners-path-editor.js' => 'ff688a7a',
'rsrc/js/application/passphrase/passphrase-credential-control.js' => '48fe33d0',
'rsrc/js/application/pholio/behavior-pholio-mock-edit.js' => '3eed1f2b',
'rsrc/js/application/pholio/behavior-pholio-mock-view.js' => '5aa1544e',
'rsrc/js/application/phortune/behavior-stripe-payment-form.js' => '02cb4398',
'rsrc/js/application/phortune/behavior-test-payment-form.js' => '4a7fb02b',
'rsrc/js/application/phortune/phortune-credit-card-form.js' => 'd12d214f',
'rsrc/js/application/policy/behavior-policy-control.js' => '0eaa33a9',
'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '9347f172',
- 'rsrc/js/application/projects/WorkboardBoard.js' => 'a4f1e85d',
+ 'rsrc/js/application/projects/WorkboardBoard.js' => '902a1551',
'rsrc/js/application/projects/WorkboardCard.js' => '887ef74f',
- 'rsrc/js/application/projects/WorkboardColumn.js' => 'ca444dca',
+ 'rsrc/js/application/projects/WorkboardColumn.js' => '01ea93b3',
'rsrc/js/application/projects/WorkboardController.js' => '42c7a5a7',
'rsrc/js/application/projects/WorkboardHeader.js' => '6e75daea',
'rsrc/js/application/projects/WorkboardHeaderTemplate.js' => '2d641f7d',
'rsrc/js/application/projects/behavior-project-boards.js' => 'e2730b90',
'rsrc/js/application/projects/behavior-project-create.js' => '34c53422',
'rsrc/js/application/projects/behavior-reorder-columns.js' => '8ac32fd9',
'rsrc/js/application/releeph/releeph-preview-branch.js' => '75184d68',
'rsrc/js/application/releeph/releeph-request-state-change.js' => '9f081f05',
'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'aa3a100c',
'rsrc/js/application/repository/repository-crossreference.js' => 'c15122b4',
'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e5bdb730',
'rsrc/js/application/search/behavior-reorder-queries.js' => 'b86f297f',
'rsrc/js/application/transactions/behavior-comment-actions.js' => '4dffaeb2',
'rsrc/js/application/transactions/behavior-reorder-configs.js' => '4842f137',
'rsrc/js/application/transactions/behavior-reorder-fields.js' => '0ad8d31f',
'rsrc/js/application/transactions/behavior-show-older-transactions.js' => '600f440c',
'rsrc/js/application/transactions/behavior-transaction-comment-form.js' => '2bdadf1a',
'rsrc/js/application/transactions/behavior-transaction-list.js' => '9cec214e',
'rsrc/js/application/typeahead/behavior-typeahead-browse.js' => '70245195',
'rsrc/js/application/typeahead/behavior-typeahead-search.js' => '7b139193',
'rsrc/js/application/uiexample/gesture-example.js' => '242dedd0',
'rsrc/js/application/uiexample/notification-example.js' => '29819b75',
'rsrc/js/core/Busy.js' => '5202e831',
'rsrc/js/core/DragAndDropFileUpload.js' => '4370900d',
'rsrc/js/core/DraggableList.js' => '8437c663',
'rsrc/js/core/Favicon.js' => '7930776a',
'rsrc/js/core/FileUpload.js' => 'ab85e184',
'rsrc/js/core/Hovercard.js' => '074f0783',
'rsrc/js/core/KeyboardShortcut.js' => 'c9749dcd',
'rsrc/js/core/KeyboardShortcutManager.js' => '37b8a04a',
'rsrc/js/core/MultirowRowManager.js' => '5b54c823',
'rsrc/js/core/Notification.js' => 'a9b91e3f',
'rsrc/js/core/Prefab.js' => '5793d835',
'rsrc/js/core/ShapedRequest.js' => 'abf88db8',
'rsrc/js/core/TextAreaUtils.js' => 'f340a484',
'rsrc/js/core/Title.js' => '43bc9360',
'rsrc/js/core/ToolTip.js' => '83754533',
'rsrc/js/core/behavior-active-nav.js' => '7353f43d',
'rsrc/js/core/behavior-audio-source.js' => '3dc5ad43',
'rsrc/js/core/behavior-autofocus.js' => '65bb0011',
'rsrc/js/core/behavior-badge-view.js' => '92cdd7b6',
'rsrc/js/core/behavior-bulk-editor.js' => 'aa6d2308',
'rsrc/js/core/behavior-choose-control.js' => '04f8a1e3',
'rsrc/js/core/behavior-copy.js' => 'cf32921f',
'rsrc/js/core/behavior-detect-timezone.js' => '78bc5d94',
'rsrc/js/core/behavior-device.js' => '0cf79f45',
'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '7ad020a5',
'rsrc/js/core/behavior-fancy-datepicker.js' => '956f3eeb',
'rsrc/js/core/behavior-file-tree.js' => 'ee82cedb',
'rsrc/js/core/behavior-form.js' => '55d7b788',
'rsrc/js/core/behavior-gesture.js' => 'b58d1a2a',
'rsrc/js/core/behavior-global-drag-and-drop.js' => '1cab0e9a',
'rsrc/js/core/behavior-high-security-warning.js' => 'dae2d55b',
'rsrc/js/core/behavior-history-install.js' => '6a1583a8',
'rsrc/js/core/behavior-hovercard.js' => '6c379000',
'rsrc/js/core/behavior-keyboard-pager.js' => '1325b731',
'rsrc/js/core/behavior-keyboard-shortcuts.js' => '2cc87f49',
'rsrc/js/core/behavior-lightbox-attachments.js' => 'c7e748bf',
'rsrc/js/core/behavior-line-linker.js' => 'e15c8b1f',
'rsrc/js/core/behavior-linked-container.js' => '74446546',
'rsrc/js/core/behavior-more.js' => '506aa3f4',
'rsrc/js/core/behavior-object-selector.js' => 'a4af0b4a',
'rsrc/js/core/behavior-oncopy.js' => 'ff7b3f22',
'rsrc/js/core/behavior-phabricator-nav.js' => 'f166c949',
'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '2f80333f',
'rsrc/js/core/behavior-read-only-warning.js' => 'b9109f8f',
'rsrc/js/core/behavior-redirect.js' => '407ee861',
'rsrc/js/core/behavior-refresh-csrf.js' => '46116c01',
'rsrc/js/core/behavior-remarkup-load-image.js' => '202bfa3f',
'rsrc/js/core/behavior-remarkup-preview.js' => 'd8a86cfb',
'rsrc/js/core/behavior-reorder-applications.js' => 'aa371860',
'rsrc/js/core/behavior-reveal-content.js' => 'b105a3a6',
'rsrc/js/core/behavior-scrollbar.js' => '92388bae',
'rsrc/js/core/behavior-search-typeahead.js' => '1cb7d027',
'rsrc/js/core/behavior-select-content.js' => 'e8240b50',
'rsrc/js/core/behavior-select-on-click.js' => '66365ee2',
'rsrc/js/core/behavior-setup-check-https.js' => '01384686',
'rsrc/js/core/behavior-time-typeahead.js' => '5803b9e7',
'rsrc/js/core/behavior-toggle-class.js' => 'f5c78ae3',
'rsrc/js/core/behavior-tokenizer.js' => '3b4899b0',
'rsrc/js/core/behavior-tooltip.js' => '73ecc1f8',
'rsrc/js/core/behavior-user-menu.js' => '60cd9241',
'rsrc/js/core/behavior-watch-anchor.js' => '0e6d261f',
'rsrc/js/core/behavior-workflow.js' => '9623adc1',
'rsrc/js/core/darkconsole/DarkLog.js' => '3b869402',
'rsrc/js/core/darkconsole/DarkMessage.js' => '26cd4b73',
'rsrc/js/core/darkconsole/behavior-dark-console.js' => 'f39d968b',
'rsrc/js/core/phtize.js' => '2f1db1ed',
'rsrc/js/phui/behavior-phui-dropdown-menu.js' => '5cf0501a',
'rsrc/js/phui/behavior-phui-file-upload.js' => 'e150bd50',
'rsrc/js/phui/behavior-phui-selectable-list.js' => 'b26a41e4',
'rsrc/js/phui/behavior-phui-submenu.js' => 'b5e9bff9',
'rsrc/js/phui/behavior-phui-tab-group.js' => '242aa08b',
'rsrc/js/phui/behavior-phui-timer-control.js' => 'f84bcbf4',
'rsrc/js/phuix/PHUIXActionListView.js' => 'c68f183f',
'rsrc/js/phuix/PHUIXActionView.js' => 'aaa08f3b',
'rsrc/js/phuix/PHUIXAutocomplete.js' => '8f139ef0',
'rsrc/js/phuix/PHUIXButtonView.js' => '55a24e84',
'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bdce4d78',
'rsrc/js/phuix/PHUIXExample.js' => 'c2c500a7',
'rsrc/js/phuix/PHUIXFormControl.js' => '38c1f3fb',
'rsrc/js/phuix/PHUIXIconView.js' => 'a5257c4e',
),
'symbols' => array(
'almanac-css' => '2e050f4f',
'aphront-bars' => '4a327b4a',
'aphront-dark-console-css' => '7f06cda2',
'aphront-dialog-view-css' => 'b70c70df',
'aphront-list-filter-view-css' => 'feb64255',
'aphront-multi-column-view-css' => 'fbc00ba3',
'aphront-panel-view-css' => '46923d46',
'aphront-table-view-css' => '205053cd',
'aphront-tokenizer-control-css' => 'b52d0668',
'aphront-tooltip-css' => 'e3f2412f',
'aphront-typeahead-control-css' => '8779483d',
'application-search-view-css' => '0f7c06d8',
'auth-css' => 'add92fd8',
'bulk-job-css' => '73af99f5',
'conduit-api-css' => 'ce2cfc41',
'config-options-css' => '16c920ae',
'conpherence-color-css' => 'b17746b0',
'conpherence-durable-column-view' => '2d57072b',
'conpherence-header-pane-css' => 'c9a3db8e',
'conpherence-menu-css' => '67f4680d',
'conpherence-message-pane-css' => 'd244db1e',
'conpherence-notification-css' => '6a3d4e58',
'conpherence-participant-pane-css' => '69e0058a',
'conpherence-thread-manager' => 'aec8e38c',
'conpherence-transaction-css' => '3a3f5e7e',
'd3' => 'd67475f5',
'differential-changeset-view-css' => 'bde53589',
'differential-core-view-css' => '7300a73e',
'differential-revision-add-comment-css' => '7e5900d9',
'differential-revision-comment-css' => '7dbc8d1d',
'differential-revision-history-css' => '8aa3eac5',
'differential-revision-list-css' => '93d2df7d',
'differential-table-of-contents-css' => '0e3364c7',
'diffusion-css' => 'b54c77b0',
'diffusion-icons-css' => '23b31a1b',
'diffusion-readme-css' => 'b68a76e4',
'diffusion-repository-css' => 'b89e8c6c',
'diviner-shared-css' => '4bd263b0',
'font-fontawesome' => '3883938a',
'font-lato' => '23631304',
'global-drag-and-drop-css' => '1d2713a4',
'harbormaster-css' => '8dfe16b2',
'herald-css' => '648d39e2',
'herald-rule-editor' => '27daef73',
'herald-test-css' => 'e004176f',
'inline-comment-summary-css' => '81eb368d',
'javelin-aphlict' => '022516b4',
'javelin-behavior' => 'fce5d170',
'javelin-behavior-aphlict-dropdown' => 'e9a2940f',
'javelin-behavior-aphlict-listen' => '4e61fa88',
'javelin-behavior-aphlict-status' => 'c3703a16',
'javelin-behavior-aphront-basic-tokenizer' => '3b4899b0',
'javelin-behavior-aphront-drag-and-drop-textarea' => '7ad020a5',
'javelin-behavior-aphront-form-disable-on-submit' => '55d7b788',
'javelin-behavior-aphront-more' => '506aa3f4',
'javelin-behavior-audio-source' => '3dc5ad43',
'javelin-behavior-audit-preview' => 'b7b73831',
'javelin-behavior-badge-view' => '92cdd7b6',
'javelin-behavior-bulk-editor' => 'aa6d2308',
'javelin-behavior-bulk-job-reload' => '3829a3cf',
'javelin-behavior-calendar-month-view' => '158c64e0',
'javelin-behavior-choose-control' => '04f8a1e3',
'javelin-behavior-comment-actions' => '4dffaeb2',
'javelin-behavior-config-reorder-fields' => '2539f834',
'javelin-behavior-conpherence-menu' => '8c2ed2bf',
'javelin-behavior-conpherence-participant-pane' => '43ba89a2',
'javelin-behavior-conpherence-pontificate' => '4ae58b5a',
'javelin-behavior-conpherence-search' => '91befbcc',
'javelin-behavior-countdown-timer' => '6a162524',
'javelin-behavior-dark-console' => 'f39d968b',
'javelin-behavior-dashboard-async-panel' => '09ecf50c',
'javelin-behavior-dashboard-move-panels' => '076bd092',
'javelin-behavior-dashboard-query-panel-select' => '1e413dc9',
'javelin-behavior-dashboard-tab-panel' => '9b1cbd76',
'javelin-behavior-day-view' => '727a5a61',
'javelin-behavior-desktop-notifications-control' => '070679fe',
'javelin-behavior-detect-timezone' => '78bc5d94',
'javelin-behavior-device' => '0cf79f45',
'javelin-behavior-diff-preview-link' => 'f51e9c17',
'javelin-behavior-differential-diff-radios' => '925fe8cd',
'javelin-behavior-differential-populate' => 'dfa1d313',
'javelin-behavior-diffusion-commit-branches' => '4b671572',
'javelin-behavior-diffusion-commit-graph' => '1c88f154',
'javelin-behavior-diffusion-locate-file' => '87428eb2',
'javelin-behavior-diffusion-pull-lastmodified' => 'c715c123',
'javelin-behavior-document-engine' => '243d6c22',
'javelin-behavior-doorkeeper-tag' => '6a85bc5a',
'javelin-behavior-drydock-live-operation-status' => '47a0728b',
'javelin-behavior-durable-column' => 'fa6f30b2',
'javelin-behavior-editengine-reorder-configs' => '4842f137',
'javelin-behavior-editengine-reorder-fields' => '0ad8d31f',
'javelin-behavior-event-all-day' => '0b1bc990',
'javelin-behavior-fancy-datepicker' => '956f3eeb',
'javelin-behavior-global-drag-and-drop' => '1cab0e9a',
'javelin-behavior-harbormaster-log' => 'b347a301',
'javelin-behavior-herald-rule-editor' => '0922e81d',
'javelin-behavior-high-security-warning' => 'dae2d55b',
'javelin-behavior-history-install' => '6a1583a8',
'javelin-behavior-icon-composer' => '38a6cedb',
'javelin-behavior-launch-icon-composer' => 'a17b84f1',
'javelin-behavior-lightbox-attachments' => 'c7e748bf',
'javelin-behavior-line-chart' => 'c8147a20',
'javelin-behavior-linked-container' => '74446546',
'javelin-behavior-maniphest-batch-selector' => '139ef688',
'javelin-behavior-maniphest-list-editor' => 'c687e867',
'javelin-behavior-owners-path-editor' => 'ff688a7a',
'javelin-behavior-passphrase-credential-control' => '48fe33d0',
'javelin-behavior-phabricator-active-nav' => '7353f43d',
'javelin-behavior-phabricator-autofocus' => '65bb0011',
'javelin-behavior-phabricator-clipboard-copy' => 'cf32921f',
'javelin-behavior-phabricator-file-tree' => 'ee82cedb',
'javelin-behavior-phabricator-gesture' => 'b58d1a2a',
'javelin-behavior-phabricator-gesture-example' => '242dedd0',
'javelin-behavior-phabricator-keyboard-pager' => '1325b731',
'javelin-behavior-phabricator-keyboard-shortcuts' => '2cc87f49',
'javelin-behavior-phabricator-line-linker' => 'e15c8b1f',
'javelin-behavior-phabricator-nav' => 'f166c949',
'javelin-behavior-phabricator-notification-example' => '29819b75',
'javelin-behavior-phabricator-object-selector' => 'a4af0b4a',
'javelin-behavior-phabricator-oncopy' => 'ff7b3f22',
'javelin-behavior-phabricator-remarkup-assist' => '2f80333f',
'javelin-behavior-phabricator-reveal-content' => 'b105a3a6',
'javelin-behavior-phabricator-search-typeahead' => '1cb7d027',
'javelin-behavior-phabricator-show-older-transactions' => '600f440c',
'javelin-behavior-phabricator-tooltips' => '73ecc1f8',
'javelin-behavior-phabricator-transaction-comment-form' => '2bdadf1a',
'javelin-behavior-phabricator-transaction-list' => '9cec214e',
'javelin-behavior-phabricator-watch-anchor' => '0e6d261f',
'javelin-behavior-pholio-mock-edit' => '3eed1f2b',
'javelin-behavior-pholio-mock-view' => '5aa1544e',
'javelin-behavior-phui-dropdown-menu' => '5cf0501a',
'javelin-behavior-phui-file-upload' => 'e150bd50',
'javelin-behavior-phui-hovercards' => '6c379000',
'javelin-behavior-phui-selectable-list' => 'b26a41e4',
'javelin-behavior-phui-submenu' => 'b5e9bff9',
'javelin-behavior-phui-tab-group' => '242aa08b',
'javelin-behavior-phui-timer-control' => 'f84bcbf4',
'javelin-behavior-phuix-example' => 'c2c500a7',
'javelin-behavior-policy-control' => '0eaa33a9',
'javelin-behavior-policy-rule-editor' => '9347f172',
'javelin-behavior-project-boards' => 'e2730b90',
'javelin-behavior-project-create' => '34c53422',
'javelin-behavior-quicksand-blacklist' => '5a6f6a06',
'javelin-behavior-read-only-warning' => 'b9109f8f',
'javelin-behavior-redirect' => '407ee861',
'javelin-behavior-refresh-csrf' => '46116c01',
'javelin-behavior-releeph-preview-branch' => '75184d68',
'javelin-behavior-releeph-request-state-change' => '9f081f05',
'javelin-behavior-releeph-request-typeahead' => 'aa3a100c',
'javelin-behavior-remarkup-load-image' => '202bfa3f',
'javelin-behavior-remarkup-preview' => 'd8a86cfb',
'javelin-behavior-reorder-applications' => 'aa371860',
'javelin-behavior-reorder-columns' => '8ac32fd9',
'javelin-behavior-reorder-profile-menu-items' => 'e5bdb730',
'javelin-behavior-repository-crossreference' => 'c15122b4',
'javelin-behavior-scrollbar' => '92388bae',
'javelin-behavior-search-reorder-queries' => 'b86f297f',
'javelin-behavior-select-content' => 'e8240b50',
'javelin-behavior-select-on-click' => '66365ee2',
'javelin-behavior-setup-check-https' => '01384686',
'javelin-behavior-stripe-payment-form' => '02cb4398',
'javelin-behavior-test-payment-form' => '4a7fb02b',
'javelin-behavior-time-typeahead' => '5803b9e7',
'javelin-behavior-toggle-class' => 'f5c78ae3',
'javelin-behavior-toggle-widget' => '8f959ad0',
'javelin-behavior-typeahead-browse' => '70245195',
'javelin-behavior-typeahead-search' => '7b139193',
'javelin-behavior-user-menu' => '60cd9241',
'javelin-behavior-view-placeholder' => 'a9942052',
'javelin-behavior-workflow' => '9623adc1',
'javelin-color' => '78f811c9',
'javelin-cookie' => '05d290ef',
'javelin-diffusion-locate-file-source' => '94243d89',
'javelin-dom' => '94681e22',
'javelin-dynval' => '202a2e85',
'javelin-event' => 'c03f2fb4',
'javelin-fx' => '34450586',
'javelin-history' => '030b4f7a',
'javelin-install' => '5902260c',
'javelin-json' => '541f81c3',
'javelin-leader' => '0d2490ce',
'javelin-magical-init' => '98e6504a',
'javelin-mask' => '7c4d8998',
'javelin-quicksand' => 'd3799cb4',
'javelin-reactor' => '1c850a26',
'javelin-reactor-dom' => '6cfa0008',
'javelin-reactor-node-calmer' => '225bbb98',
'javelin-reactornode' => '72960bc1',
'javelin-request' => '91863989',
'javelin-resource' => '740956e1',
'javelin-routable' => '6a18c42e',
'javelin-router' => '32755edb',
'javelin-scrollbar' => 'a43ae2ae',
'javelin-sound' => 'e562708c',
'javelin-stratcom' => '0889b835',
'javelin-tokenizer' => '89a1ae3a',
'javelin-typeahead' => 'a4356cde',
'javelin-typeahead-composite-source' => '22ee68a5',
'javelin-typeahead-normalizer' => 'a241536a',
'javelin-typeahead-ondemand-source' => '23387297',
'javelin-typeahead-preloaded-source' => '5a79f6c3',
'javelin-typeahead-source' => '8badee71',
'javelin-typeahead-static-source' => '80bff3af',
'javelin-uri' => '2e255291',
'javelin-util' => '22ae1776',
'javelin-vector' => 'e9c80beb',
'javelin-view' => '289bf236',
'javelin-view-html' => 'f8c4e135',
'javelin-view-interpreter' => '876506b6',
'javelin-view-renderer' => '9aae2b66',
'javelin-view-visitor' => '308f9fe4',
'javelin-websocket' => 'fdc13e4e',
- 'javelin-workboard-board' => 'a4f1e85d',
+ 'javelin-workboard-board' => '902a1551',
'javelin-workboard-card' => '887ef74f',
- 'javelin-workboard-column' => 'ca444dca',
+ 'javelin-workboard-column' => '01ea93b3',
'javelin-workboard-controller' => '42c7a5a7',
'javelin-workboard-header' => '6e75daea',
'javelin-workboard-header-template' => '2d641f7d',
'javelin-workflow' => '958e9045',
'maniphest-report-css' => '3d53188b',
'maniphest-task-edit-css' => '272daa84',
'maniphest-task-summary-css' => '61d1667e',
'multirow-row-manager' => '5b54c823',
'owners-path-editor' => '2a8b62d9',
'owners-path-editor-css' => 'fa7c13ef',
'paste-css' => 'b37bcd38',
'path-typeahead' => 'ad486db3',
'people-picture-menu-item-css' => 'fe8e07cf',
'people-profile-css' => '2ea2daa1',
'phabricator-action-list-view-css' => 'c4972757',
'phabricator-busy' => '5202e831',
'phabricator-chatlog-css' => 'abdc76ee',
'phabricator-content-source-view-css' => 'cdf0d579',
'phabricator-core-css' => '1b29ed61',
'phabricator-countdown-css' => 'bff8012f',
'phabricator-darklog' => '3b869402',
'phabricator-darkmessage' => '26cd4b73',
'phabricator-dashboard-css' => '4267d6c6',
'phabricator-diff-changeset' => 'd0a85a85',
'phabricator-diff-changeset-list' => '04023d82',
'phabricator-diff-inline' => 'a4a14a94',
'phabricator-drag-and-drop-file-upload' => '4370900d',
'phabricator-draggable-list' => '8437c663',
'phabricator-fatal-config-template-css' => '20babf50',
'phabricator-favicon' => '7930776a',
'phabricator-feed-css' => 'd8b6e3f8',
'phabricator-file-upload' => 'ab85e184',
'phabricator-filetree-view-css' => '56cdd875',
'phabricator-flag-css' => '2b77be8d',
'phabricator-keyboard-shortcut' => 'c9749dcd',
'phabricator-keyboard-shortcut-manager' => '37b8a04a',
'phabricator-main-menu-view' => '8e2d9a28',
'phabricator-nav-view-css' => 'f8a0c1bf',
'phabricator-notification' => 'a9b91e3f',
'phabricator-notification-css' => '30240bd2',
'phabricator-notification-menu-css' => 'e6962e89',
'phabricator-object-selector-css' => 'ee77366f',
'phabricator-phtize' => '2f1db1ed',
'phabricator-prefab' => '5793d835',
'phabricator-remarkup-css' => '9e627d41',
'phabricator-search-results-css' => '9ea70ace',
'phabricator-shaped-request' => 'abf88db8',
'phabricator-slowvote-css' => '1694baed',
'phabricator-source-code-view-css' => '03d7ac28',
'phabricator-standard-page-view' => '8a295cb9',
'phabricator-textareautils' => 'f340a484',
'phabricator-title' => '43bc9360',
'phabricator-tooltip' => '83754533',
'phabricator-ui-example-css' => 'b4795059',
'phabricator-zindex-css' => '99c0f5eb',
'phame-css' => '799febf9',
'pholio-css' => '88ef5ef1',
'pholio-edit-css' => '4df55b3b',
'pholio-inline-comments-css' => '722b48c2',
'phortune-credit-card-form' => 'd12d214f',
'phortune-credit-card-form-css' => '3b9868a8',
'phortune-css' => '12e8251a',
'phortune-invoice-css' => '4436b241',
'phrequent-css' => 'bd79cc67',
'phriction-document-css' => '03380da0',
'phui-action-panel-css' => '6c386cbf',
'phui-badge-view-css' => '666e25ad',
'phui-basic-nav-view-css' => '56ebd66d',
'phui-big-info-view-css' => '362ad37b',
'phui-box-css' => '5ed3b8cb',
'phui-bulk-editor-css' => '374d5e30',
'phui-button-bar-css' => 'a4aa75c4',
'phui-button-css' => 'ea704902',
'phui-button-simple-css' => '1ff278aa',
'phui-calendar-css' => 'f11073aa',
'phui-calendar-day-css' => '9597d706',
'phui-calendar-list-css' => 'ccd7e4e2',
'phui-calendar-month-css' => 'cb758c42',
'phui-chart-css' => '7853a69b',
'phui-cms-css' => '8c05c41e',
'phui-comment-form-css' => '68a2d99a',
'phui-comment-panel-css' => 'ec4e31c0',
'phui-crumbs-view-css' => '614f43cf',
'phui-curtain-view-css' => '68c5efb6',
'phui-document-summary-view-css' => 'b068eed1',
'phui-document-view-css' => '52b748a5',
'phui-document-view-pro-css' => 'b9613a10',
'phui-feed-story-css' => 'a0c05029',
'phui-font-icon-base-css' => 'd7994e06',
'phui-fontkit-css' => '9b714a5e',
'phui-form-css' => '159e2d9c',
'phui-form-view-css' => '01b796c0',
'phui-head-thing-view-css' => 'd7f293df',
'phui-header-view-css' => '285c9139',
'phui-hovercard' => '074f0783',
'phui-hovercard-view-css' => '6ca90fa0',
'phui-icon-set-selector-css' => '7aa5f3ec',
'phui-icon-view-css' => '4cbc684a',
'phui-image-mask-css' => '62c7f4d2',
'phui-info-view-css' => '37b8d9ce',
'phui-inline-comment-view-css' => '48acce5b',
'phui-invisible-character-view-css' => 'c694c4a4',
'phui-left-right-css' => '68513c34',
'phui-lightbox-css' => '4ebf22da',
'phui-list-view-css' => '470b1adb',
'phui-object-box-css' => 'f434b6be',
'phui-oi-big-ui-css' => '534f1757',
'phui-oi-color-css' => 'b517bfa0',
'phui-oi-drag-ui-css' => 'da15d3dc',
'phui-oi-flush-ui-css' => '490e2e2e',
'phui-oi-list-view-css' => '909f3844',
'phui-oi-simple-ui-css' => '6a30fa46',
'phui-pager-css' => 'd022c7ad',
'phui-pinboard-view-css' => '1f08f5d8',
'phui-property-list-view-css' => 'cad62236',
'phui-remarkup-preview-css' => '91767007',
'phui-segment-bar-view-css' => '5166b370',
'phui-spacing-css' => 'b05cadc3',
'phui-status-list-view-css' => 'e5ff8be0',
'phui-tag-view-css' => '29409667',
'phui-theme-css' => '35883b37',
'phui-timeline-view-css' => '1e348e4b',
'phui-two-column-view-css' => '01e6991e',
'phui-workboard-color-css' => 'e86de308',
'phui-workboard-view-css' => '74fc9d98',
'phui-workcard-view-css' => '8c536f90',
'phui-workpanel-view-css' => 'bc16cf33',
'phuix-action-list-view' => 'c68f183f',
'phuix-action-view' => 'aaa08f3b',
'phuix-autocomplete' => '8f139ef0',
'phuix-button-view' => '55a24e84',
'phuix-dropdown-menu' => 'bdce4d78',
'phuix-form-control-view' => '38c1f3fb',
'phuix-icon-view' => 'a5257c4e',
'policy-css' => 'ceb56a08',
'policy-edit-css' => '8794e2ed',
'policy-transaction-detail-css' => 'c02b8384',
'ponder-view-css' => '05a09d0a',
'project-card-view-css' => '3b1f7b20',
'project-view-css' => '567858b3',
'releeph-core' => 'f81ff2db',
'releeph-preview-branch' => '22db5c07',
'releeph-request-differential-create-dialog' => '0ac1ea31',
'releeph-request-typeahead-css' => 'bce37359',
'setup-issue-css' => '5eed85b2',
'sprite-login-css' => '18b368a6',
'sprite-tokens-css' => 'f1896dc5',
'syntax-default-css' => '055fc231',
'syntax-highlighting-css' => '4234f572',
'tokens-css' => 'ce5a50bd',
'typeahead-browse-css' => 'b7ed02d2',
'unhandled-exception-css' => '9ecfc00d',
),
'requires' => array(
'01384686' => array(
'javelin-behavior',
'javelin-uri',
'phabricator-notification',
),
+ '01ea93b3' => array(
+ 'javelin-install',
+ 'javelin-workboard-card',
+ 'javelin-workboard-header',
+ ),
'022516b4' => array(
'javelin-install',
'javelin-util',
'javelin-websocket',
'javelin-leader',
'javelin-json',
),
'02cb4398' => array(
'javelin-behavior',
'javelin-dom',
'phortune-credit-card-form',
),
'030b4f7a' => array(
'javelin-stratcom',
'javelin-install',
'javelin-uri',
'javelin-util',
),
'04023d82' => array(
'javelin-install',
'phuix-button-view',
),
'04f8a1e3' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-workflow',
),
'05d290ef' => array(
'javelin-install',
'javelin-util',
),
'070679fe' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-uri',
'phabricator-notification',
),
'074f0783' => array(
'javelin-install',
'javelin-dom',
'javelin-vector',
'javelin-request',
'javelin-uri',
),
'076bd092' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-workflow',
'phabricator-draggable-list',
),
'0889b835' => array(
'javelin-install',
'javelin-event',
'javelin-util',
'javelin-magical-init',
),
'0922e81d' => array(
'herald-rule-editor',
'javelin-behavior',
),
'09ecf50c' => array(
'javelin-behavior',
'javelin-dom',
'javelin-workflow',
),
'0ad8d31f' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'phabricator-draggable-list',
),
'0cf79f45' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-vector',
'javelin-install',
),
'0d2490ce' => array(
'javelin-install',
),
'0e6d261f' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-vector',
),
'0eaa33a9' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'phuix-dropdown-menu',
'phuix-action-list-view',
'phuix-action-view',
'javelin-workflow',
'phuix-icon-view',
),
'1325b731' => array(
'javelin-behavior',
'javelin-uri',
'phabricator-keyboard-shortcut',
),
'139ef688' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
),
'1c850a26' => array(
'javelin-install',
'javelin-util',
),
'1c88f154' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
),
'1cab0e9a' => array(
'javelin-behavior',
'javelin-dom',
'javelin-uri',
'javelin-mask',
'phabricator-drag-and-drop-file-upload',
),
'1cb7d027' => array(
'javelin-behavior',
'javelin-typeahead-ondemand-source',
'javelin-typeahead',
'javelin-dom',
'javelin-uri',
'javelin-util',
'javelin-stratcom',
'phabricator-prefab',
'phuix-icon-view',
),
'1e413dc9' => array(
'javelin-behavior',
'javelin-dom',
),
'1ff278aa' => array(
'phui-button-css',
),
'202a2e85' => array(
'javelin-install',
'javelin-reactornode',
'javelin-util',
'javelin-reactor',
),
'202bfa3f' => array(
'javelin-behavior',
'javelin-request',
),
'225bbb98' => array(
'javelin-install',
'javelin-reactor',
'javelin-util',
),
'22ee68a5' => array(
'javelin-install',
'javelin-typeahead-source',
'javelin-util',
),
23387297 => array(
'javelin-install',
'javelin-util',
'javelin-request',
'javelin-typeahead-source',
),
23631304 => array(
'phui-fontkit-css',
),
'242aa08b' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'242dedd0' => array(
'javelin-stratcom',
'javelin-behavior',
'javelin-vector',
'javelin-dom',
),
'243d6c22' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
),
'2539f834' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-json',
'phabricator-draggable-list',
),
'27daef73' => array(
'multirow-row-manager',
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-stratcom',
'javelin-json',
'phabricator-prefab',
),
'289bf236' => array(
'javelin-install',
'javelin-util',
),
'29819b75' => array(
'phabricator-notification',
'javelin-stratcom',
'javelin-behavior',
),
'2a8b62d9' => array(
'multirow-row-manager',
'javelin-install',
'path-typeahead',
'javelin-dom',
'javelin-util',
'phabricator-prefab',
'phuix-form-control-view',
),
'2bdadf1a' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-request',
'phabricator-shaped-request',
),
'2cc87f49' => array(
'javelin-behavior',
'javelin-workflow',
'javelin-json',
'javelin-dom',
'phabricator-keyboard-shortcut',
),
'2d641f7d' => array(
'javelin-install',
),
'2e255291' => array(
'javelin-install',
'javelin-util',
'javelin-stratcom',
),
'2f1db1ed' => array(
'javelin-util',
),
'2f80333f' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'phabricator-phtize',
'phabricator-textareautils',
'javelin-workflow',
'javelin-vector',
'phuix-autocomplete',
'javelin-mask',
),
'308f9fe4' => array(
'javelin-install',
'javelin-util',
),
'32755edb' => array(
'javelin-install',
'javelin-util',
),
34450586 => array(
'javelin-color',
'javelin-install',
'javelin-util',
),
'34c53422' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-workflow',
),
'37b8a04a' => array(
'javelin-install',
'javelin-util',
'javelin-stratcom',
'javelin-dom',
'javelin-vector',
),
'3829a3cf' => array(
'javelin-behavior',
'javelin-uri',
),
'38a6cedb' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
),
'38c1f3fb' => array(
'javelin-install',
'javelin-dom',
),
'3b4899b0' => array(
'javelin-behavior',
'phabricator-prefab',
),
'3dc5ad43' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-vector',
'javelin-dom',
),
'3eed1f2b' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-workflow',
'javelin-quicksand',
'phabricator-phtize',
'phabricator-drag-and-drop-file-upload',
'phabricator-draggable-list',
),
'407ee861' => array(
'javelin-behavior',
'javelin-uri',
),
'4234f572' => array(
'syntax-default-css',
),
'42c7a5a7' => array(
'javelin-install',
'javelin-dom',
'javelin-util',
'javelin-vector',
'javelin-stratcom',
'javelin-workflow',
'phabricator-drag-and-drop-file-upload',
'javelin-workboard-board',
),
'4370900d' => array(
'javelin-install',
'javelin-util',
'javelin-request',
'javelin-dom',
'javelin-uri',
'phabricator-file-upload',
),
'43ba89a2' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-workflow',
'javelin-util',
'phabricator-notification',
'conpherence-thread-manager',
),
'43bc9360' => array(
'javelin-install',
),
'46116c01' => array(
'javelin-request',
'javelin-behavior',
'javelin-dom',
'javelin-router',
'javelin-util',
'phabricator-busy',
),
'47a0728b' => array(
'javelin-behavior',
'javelin-dom',
'javelin-request',
),
'4842f137' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'phabricator-draggable-list',
),
'48fe33d0' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-workflow',
'javelin-util',
'javelin-uri',
),
'490e2e2e' => array(
'phui-oi-list-view-css',
),
'4a7fb02b' => array(
'javelin-behavior',
'javelin-dom',
'phortune-credit-card-form',
),
'4ae58b5a' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-workflow',
'javelin-stratcom',
'conpherence-thread-manager',
),
'4b671572' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-request',
),
'4dffaeb2' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'phuix-form-control-view',
'phuix-icon-view',
'javelin-behavior-phabricator-gesture',
),
'4e61fa88' => 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',
),
'506aa3f4' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'5202e831' => array(
'javelin-install',
'javelin-dom',
'javelin-fx',
),
'534f1757' => array(
'phui-oi-list-view-css',
),
'541f81c3' => array(
'javelin-install',
),
'55a24e84' => array(
'javelin-install',
'javelin-dom',
),
'55d7b788' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'5793d835' => 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',
),
'5803b9e7' => array(
'javelin-behavior',
'javelin-util',
'javelin-dom',
'javelin-stratcom',
'javelin-vector',
'javelin-typeahead-static-source',
),
'5902260c' => array(
'javelin-util',
'javelin-magical-init',
),
'5a6f6a06' => array(
'javelin-behavior',
'javelin-quicksand',
),
'5a79f6c3' => array(
'javelin-install',
'javelin-util',
'javelin-request',
'javelin-typeahead-source',
),
'5aa1544e' => 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',
),
'5b54c823' => array(
'javelin-install',
'javelin-stratcom',
'javelin-dom',
'javelin-util',
),
'5cf0501a' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'phuix-dropdown-menu',
),
'600f440c' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'phabricator-busy',
),
'60cd9241' => array(
'javelin-behavior',
),
'65bb0011' => array(
'javelin-behavior',
'javelin-dom',
),
'66365ee2' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'6a1583a8' => array(
'javelin-behavior',
'javelin-history',
),
'6a162524' => array(
'javelin-behavior',
'javelin-dom',
),
'6a18c42e' => array(
'javelin-install',
),
'6a30fa46' => array(
'phui-oi-list-view-css',
),
'6a85bc5a' => array(
'javelin-behavior',
'javelin-dom',
'javelin-json',
'javelin-workflow',
'javelin-magical-init',
),
'6c379000' => array(
'javelin-behavior',
'javelin-behavior-device',
'javelin-stratcom',
'javelin-vector',
'phui-hovercard',
),
'6cfa0008' => array(
'javelin-dom',
'javelin-dynval',
'javelin-reactor',
'javelin-reactornode',
'javelin-install',
'javelin-util',
),
'6e75daea' => array(
'javelin-install',
),
70245195 => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
),
'727a5a61' => array(
'phuix-icon-view',
),
'72960bc1' => array(
'javelin-install',
'javelin-reactor',
'javelin-util',
'javelin-reactor-node-calmer',
),
'7353f43d' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-vector',
'javelin-dom',
'javelin-uri',
),
'73ecc1f8' => array(
'javelin-behavior',
'javelin-behavior-device',
'javelin-stratcom',
'phabricator-tooltip',
),
'740956e1' => array(
'javelin-util',
'javelin-uri',
'javelin-install',
),
74446546 => array(
'javelin-behavior',
'javelin-dom',
),
'75184d68' => array(
'javelin-behavior',
'javelin-dom',
'javelin-uri',
'javelin-request',
),
'78bc5d94' => array(
'javelin-behavior',
'javelin-uri',
'phabricator-notification',
),
'78f811c9' => array(
'javelin-install',
),
'7930776a' => array(
'javelin-install',
'javelin-dom',
),
'7ad020a5' => array(
'javelin-behavior',
'javelin-dom',
'phabricator-drag-and-drop-file-upload',
'phabricator-textareautils',
),
'7b139193' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
),
'7c4d8998' => array(
'javelin-install',
'javelin-dom',
),
'80bff3af' => array(
'javelin-install',
'javelin-typeahead-source',
),
83754533 => array(
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-vector',
),
'8437c663' => array(
'javelin-install',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
'javelin-vector',
'javelin-magical-init',
),
'87428eb2' => array(
'javelin-behavior',
'javelin-diffusion-locate-file-source',
'javelin-dom',
'javelin-typeahead',
'javelin-uri',
),
'876506b6' => array(
'javelin-view',
'javelin-install',
'javelin-dom',
),
'887ef74f' => array(
'javelin-install',
),
'89a1ae3a' => array(
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-install',
),
'8ac32fd9' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'phabricator-draggable-list',
),
'8badee71' => array(
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-typeahead-normalizer',
),
'8c2ed2bf' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-workflow',
'javelin-behavior-device',
'javelin-history',
'javelin-vector',
'javelin-scrollbar',
'phabricator-title',
'phabricator-shaped-request',
'conpherence-thread-manager',
),
'8e2d9a28' => array(
'phui-theme-css',
),
'8f139ef0' => array(
'javelin-install',
'javelin-dom',
'phuix-icon-view',
'phabricator-prefab',
),
'8f959ad0' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-workflow',
'javelin-stratcom',
),
+ '902a1551' => array(
+ 'javelin-install',
+ 'javelin-dom',
+ 'javelin-util',
+ 'javelin-stratcom',
+ 'javelin-workflow',
+ 'phabricator-draggable-list',
+ 'javelin-workboard-column',
+ 'javelin-workboard-header-template',
+ ),
91863989 => array(
'javelin-install',
'javelin-stratcom',
'javelin-util',
'javelin-behavior',
'javelin-json',
'javelin-dom',
'javelin-resource',
'javelin-routable',
),
'91befbcc' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-workflow',
'javelin-stratcom',
),
'92388bae' => array(
'javelin-behavior',
'javelin-scrollbar',
),
'925fe8cd' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'92cdd7b6' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'9347f172' => array(
'javelin-behavior',
'multirow-row-manager',
'javelin-dom',
'javelin-util',
'phabricator-prefab',
'javelin-json',
),
'94243d89' => array(
'javelin-install',
'javelin-dom',
'javelin-typeahead-preloaded-source',
'javelin-util',
),
'94681e22' => array(
'javelin-magical-init',
'javelin-install',
'javelin-util',
'javelin-vector',
'javelin-stratcom',
),
'956f3eeb' => array(
'javelin-behavior',
'javelin-util',
'javelin-dom',
'javelin-stratcom',
'javelin-vector',
),
'958e9045' => array(
'javelin-stratcom',
'javelin-request',
'javelin-dom',
'javelin-vector',
'javelin-install',
'javelin-util',
'javelin-mask',
'javelin-uri',
'javelin-routable',
),
'9623adc1' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'javelin-router',
),
'9aae2b66' => array(
'javelin-install',
'javelin-util',
),
'9b1cbd76' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
),
'9cec214e' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'javelin-uri',
'phabricator-textareautils',
),
'9f081f05' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-workflow',
'javelin-util',
'phabricator-keyboard-shortcut',
),
'a17b84f1' => array(
'javelin-behavior',
'javelin-dom',
'javelin-workflow',
),
'a241536a' => array(
'javelin-install',
),
'a4356cde' => array(
'javelin-install',
'javelin-dom',
'javelin-vector',
'javelin-util',
),
'a43ae2ae' => array(
'javelin-install',
'javelin-dom',
'javelin-stratcom',
'javelin-vector',
),
'a4a14a94' => array(
'javelin-dom',
),
'a4aa75c4' => array(
'phui-button-css',
'phui-button-simple-css',
),
'a4af0b4a' => array(
'javelin-behavior',
'javelin-dom',
'javelin-request',
'javelin-util',
),
- 'a4f1e85d' => array(
- 'javelin-install',
- 'javelin-dom',
- 'javelin-util',
- 'javelin-stratcom',
- 'javelin-workflow',
- 'phabricator-draggable-list',
- 'javelin-workboard-column',
- 'javelin-workboard-header-template',
- ),
'a5257c4e' => array(
'javelin-install',
'javelin-dom',
),
'a9942052' => array(
'javelin-behavior',
'javelin-dom',
'javelin-view-renderer',
'javelin-install',
),
'a9b91e3f' => array(
'javelin-install',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
'phabricator-notification-css',
),
'aa371860' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'phabricator-draggable-list',
),
'aa3a100c' => array(
'javelin-behavior',
'javelin-dom',
'javelin-typeahead',
'javelin-typeahead-ondemand-source',
'javelin-dom',
),
'aa6d2308' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'multirow-row-manager',
'javelin-json',
'phuix-form-control-view',
),
'aaa08f3b' => array(
'javelin-install',
'javelin-dom',
'javelin-util',
),
'ab85e184' => array(
'javelin-install',
'javelin-dom',
'phabricator-notification',
),
'abf88db8' => array(
'javelin-install',
'javelin-util',
'javelin-request',
'javelin-router',
),
'ad486db3' => array(
'javelin-install',
'javelin-typeahead',
'javelin-dom',
'javelin-request',
'javelin-typeahead-ondemand-source',
'javelin-util',
),
'aec8e38c' => array(
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-install',
'javelin-aphlict',
'javelin-workflow',
'javelin-router',
'javelin-behavior-device',
'javelin-vector',
),
'b105a3a6' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'b26a41e4' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'b347a301' => array(
'javelin-behavior',
),
'b517bfa0' => array(
'phui-oi-list-view-css',
),
'b52d0668' => array(
'aphront-typeahead-control-css',
'phui-tag-view-css',
),
'b58d1a2a' => array(
'javelin-behavior',
'javelin-behavior-device',
'javelin-stratcom',
'javelin-vector',
'javelin-dom',
'javelin-magical-init',
),
'b5e9bff9' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'b7b73831' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'phabricator-shaped-request',
),
'b86f297f' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'phabricator-draggable-list',
),
'b9109f8f' => array(
'javelin-behavior',
'javelin-uri',
'phabricator-notification',
),
'bc16cf33' => array(
'phui-workcard-view-css',
),
'bdce4d78' => array(
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-vector',
'javelin-stratcom',
),
'bde53589' => array(
'phui-inline-comment-view-css',
),
'c03f2fb4' => array(
'javelin-install',
),
'c15122b4' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-uri',
),
'c2c500a7' => array(
'javelin-install',
'javelin-dom',
'phuix-button-view',
),
'c3703a16' => array(
'javelin-behavior',
'javelin-aphlict',
'phabricator-phtize',
'javelin-dom',
),
'c687e867' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-workflow',
'javelin-fx',
'javelin-util',
),
'c68f183f' => array(
'javelin-install',
'javelin-dom',
),
'c715c123' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-workflow',
'javelin-json',
),
'c7e748bf' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-mask',
'javelin-util',
'phuix-icon-view',
'phabricator-busy',
),
'c8147a20' => array(
'javelin-behavior',
'javelin-dom',
'javelin-vector',
'phui-chart-css',
),
'c9749dcd' => array(
'javelin-install',
'javelin-util',
'phabricator-keyboard-shortcut-manager',
),
- 'ca444dca' => array(
- 'javelin-install',
- 'javelin-workboard-card',
- 'javelin-workboard-header',
- ),
'cf32921f' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
),
'd0a85a85' => array(
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-install',
'javelin-workflow',
'javelin-router',
'javelin-behavior-device',
'javelin-vector',
'phabricator-diff-inline',
),
'd12d214f' => array(
'javelin-install',
'javelin-dom',
'javelin-json',
'javelin-workflow',
'javelin-util',
),
'd3799cb4' => array(
'javelin-install',
),
'd8a86cfb' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'phabricator-shaped-request',
),
'da15d3dc' => array(
'phui-oi-list-view-css',
),
'dae2d55b' => array(
'javelin-behavior',
'javelin-uri',
'phabricator-notification',
),
'dfa1d313' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'phabricator-tooltip',
'phabricator-diff-changeset-list',
'phabricator-diff-changeset',
),
'e150bd50' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'phuix-dropdown-menu',
),
'e15c8b1f' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-history',
),
'e2730b90' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-vector',
'javelin-stratcom',
'javelin-workflow',
'javelin-workboard-controller',
),
'e562708c' => array(
'javelin-install',
),
'e5bdb730' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'phabricator-draggable-list',
),
'e8240b50' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'e9a2940f' => array(
'javelin-behavior',
'javelin-request',
'javelin-stratcom',
'javelin-vector',
'javelin-dom',
'javelin-uri',
'javelin-behavior-device',
'phabricator-title',
'phabricator-favicon',
),
'e9c80beb' => array(
'javelin-install',
'javelin-event',
),
'ec4e31c0' => array(
'phui-timeline-view-css',
),
'ee77366f' => array(
'aphront-dialog-view-css',
),
'ee82cedb' => array(
'javelin-behavior',
'phabricator-keyboard-shortcut',
'javelin-stratcom',
),
'f166c949' => array(
'javelin-behavior',
'javelin-behavior-device',
'javelin-stratcom',
'javelin-dom',
'javelin-magical-init',
'javelin-vector',
'javelin-request',
'javelin-util',
),
'f340a484' => array(
'javelin-install',
'javelin-dom',
'javelin-vector',
),
'f39d968b' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-util',
'javelin-dom',
'javelin-request',
'phabricator-keyboard-shortcut',
'phabricator-darklog',
'phabricator-darkmessage',
),
'f51e9c17' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'f5c78ae3' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'f84bcbf4' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'f8c4e135' => array(
'javelin-install',
'javelin-dom',
'javelin-view-visitor',
'javelin-util',
),
'fa6f30b2' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-behavior-device',
'javelin-scrollbar',
'javelin-quicksand',
'phabricator-keyboard-shortcut',
'conpherence-thread-manager',
),
'fce5d170' => array(
'javelin-magical-init',
'javelin-util',
),
'fdc13e4e' => array(
'javelin-install',
),
'ff688a7a' => array(
'owners-path-editor',
'javelin-behavior',
),
'ff7b3f22' => array(
'javelin-behavior',
'javelin-dom',
),
),
'packages' => array(
'conpherence.pkg.css' => array(
'conpherence-durable-column-view',
'conpherence-menu-css',
'conpherence-color-css',
'conpherence-message-pane-css',
'conpherence-notification-css',
'conpherence-transaction-css',
'conpherence-participant-pane-css',
'conpherence-header-pane-css',
),
'conpherence.pkg.js' => array(
'javelin-behavior-conpherence-menu',
'javelin-behavior-conpherence-participant-pane',
'javelin-behavior-conpherence-pontificate',
'javelin-behavior-toggle-widget',
),
'core.pkg.css' => array(
'phabricator-core-css',
'phabricator-zindex-css',
'phui-button-css',
'phui-button-simple-css',
'phui-theme-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',
'application-search-view-css',
'phabricator-remarkup-css',
'syntax-highlighting-css',
'syntax-default-css',
'phui-pager-css',
'aphront-tooltip-css',
'phabricator-flag-css',
'phui-info-view-css',
'phabricator-main-menu-view',
'phabricator-notification-css',
'phabricator-notification-menu-css',
'phui-lightbox-css',
'phui-comment-panel-css',
'phui-header-view-css',
'phabricator-nav-view-css',
'phui-basic-nav-view-css',
'phui-crumbs-view-css',
'phui-oi-list-view-css',
'phui-oi-color-css',
'phui-oi-big-ui-css',
'phui-oi-drag-ui-css',
'phui-oi-simple-ui-css',
'phui-oi-flush-ui-css',
'global-drag-and-drop-css',
'phui-spacing-css',
'phui-form-css',
'phui-icon-view-css',
'phabricator-action-list-view-css',
'phui-property-list-view-css',
'phui-tag-view-css',
'phui-list-view-css',
'font-fontawesome',
'font-lato',
'phui-font-icon-base-css',
'phui-fontkit-css',
'phui-box-css',
'phui-object-box-css',
'phui-timeline-view-css',
'phui-two-column-view-css',
'phui-curtain-view-css',
'sprite-login-css',
'sprite-tokens-css',
'tokens-css',
'auth-css',
'phui-status-list-view-css',
'phui-feed-story-css',
'phabricator-feed-css',
'phabricator-dashboard-css',
'aphront-multi-column-view-css',
),
'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',
'phuix-icon-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-sound',
'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',
'phui-hovercard',
'javelin-behavior-phui-hovercards',
'javelin-color',
'javelin-fx',
'phabricator-draggable-list',
'javelin-behavior-phabricator-transaction-list',
'javelin-behavior-phabricator-show-older-transactions',
'javelin-behavior-phui-dropdown-menu',
'javelin-behavior-doorkeeper-tag',
'phabricator-title',
'javelin-leader',
'javelin-websocket',
'javelin-behavior-dashboard-async-panel',
'javelin-behavior-dashboard-tab-panel',
'javelin-quicksand',
'javelin-behavior-quicksand-blacklist',
'javelin-behavior-high-security-warning',
'javelin-behavior-read-only-warning',
'javelin-scrollbar',
'javelin-behavior-scrollbar',
'javelin-behavior-durable-column',
'conpherence-thread-manager',
'javelin-behavior-detect-timezone',
'javelin-behavior-setup-check-https',
'javelin-behavior-aphlict-status',
'javelin-behavior-user-menu',
'phabricator-favicon',
),
'differential.pkg.css' => array(
'differential-core-view-css',
'differential-changeset-view-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',
'phui-inline-comment-view-css',
'phabricator-filetree-view-css',
),
'differential.pkg.js' => array(
'phabricator-drag-and-drop-file-upload',
'phabricator-shaped-request',
'javelin-behavior-differential-populate',
'javelin-behavior-differential-diff-radios',
'javelin-behavior-aphront-drag-and-drop-textarea',
'javelin-behavior-phabricator-object-selector',
'javelin-behavior-repository-crossreference',
'javelin-behavior-aphront-more',
'phabricator-diff-inline',
'phabricator-diff-changeset',
'phabricator-diff-changeset-list',
),
'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-list-editor',
),
),
);
diff --git a/src/applications/maniphest/storage/ManiphestTask.php b/src/applications/maniphest/storage/ManiphestTask.php
index 8d45ed45fa..c19e75604c 100644
--- a/src/applications/maniphest/storage/ManiphestTask.php
+++ b/src/applications/maniphest/storage/ManiphestTask.php
@@ -1,663 +1,660 @@
<?php
final class ManiphestTask extends ManiphestDAO
implements
PhabricatorSubscribableInterface,
PhabricatorMarkupInterface,
PhabricatorPolicyInterface,
PhabricatorTokenReceiverInterface,
PhabricatorFlaggableInterface,
PhabricatorMentionableInterface,
PhrequentTrackableInterface,
PhabricatorCustomFieldInterface,
PhabricatorDestructibleInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorProjectInterface,
PhabricatorSpacesInterface,
PhabricatorConduitResultInterface,
PhabricatorFulltextInterface,
PhabricatorFerretInterface,
DoorkeeperBridgedObjectInterface,
PhabricatorEditEngineSubtypeInterface,
PhabricatorEditEngineLockableInterface,
PhabricatorEditEngineMFAInterface,
PhabricatorPolicyCodexInterface,
PhabricatorUnlockableInterface {
const MARKUP_FIELD_DESCRIPTION = 'markup:desc';
protected $authorPHID;
protected $ownerPHID;
protected $status;
protected $priority;
protected $subpriority = 0;
protected $title = '';
protected $description = '';
protected $originalEmailSource;
protected $mailKey;
protected $viewPolicy = PhabricatorPolicies::POLICY_USER;
protected $editPolicy = PhabricatorPolicies::POLICY_USER;
protected $ownerOrdering;
protected $spacePHID;
protected $bridgedObjectPHID;
protected $properties = array();
protected $points;
protected $subtype;
protected $closedEpoch;
protected $closerPHID;
private $subscriberPHIDs = self::ATTACHABLE;
private $groupByProjectPHID = self::ATTACHABLE;
private $customFields = self::ATTACHABLE;
private $edgeProjectPHIDs = self::ATTACHABLE;
private $bridgedObject = self::ATTACHABLE;
public static function initializeNewTask(PhabricatorUser $actor) {
$app = id(new PhabricatorApplicationQuery())
->setViewer($actor)
->withClasses(array('PhabricatorManiphestApplication'))
->executeOne();
$view_policy = $app->getPolicy(ManiphestDefaultViewCapability::CAPABILITY);
$edit_policy = $app->getPolicy(ManiphestDefaultEditCapability::CAPABILITY);
return id(new ManiphestTask())
->setStatus(ManiphestTaskStatus::getDefaultStatus())
->setPriority(ManiphestTaskPriority::getDefaultPriority())
->setAuthorPHID($actor->getPHID())
->setViewPolicy($view_policy)
->setEditPolicy($edit_policy)
->setSpacePHID($actor->getDefaultSpacePHID())
->setSubtype(PhabricatorEditEngineSubtype::SUBTYPE_DEFAULT)
->attachProjectPHIDs(array())
->attachSubscriberPHIDs(array());
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'properties' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'ownerPHID' => 'phid?',
'status' => 'text64',
'priority' => 'uint32',
'title' => 'sort',
'description' => 'text',
'mailKey' => 'bytes20',
'ownerOrdering' => 'text64?',
'originalEmailSource' => 'text255?',
'subpriority' => 'double',
'points' => 'double?',
'bridgedObjectPHID' => 'phid?',
'subtype' => 'text64',
'closedEpoch' => 'epoch?',
'closerPHID' => 'phid?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_phid' => null,
'phid' => array(
'columns' => array('phid'),
'unique' => true,
),
'priority' => array(
'columns' => array('priority', 'status'),
),
'status' => array(
'columns' => array('status'),
),
'ownerPHID' => array(
'columns' => array('ownerPHID', 'status'),
),
'authorPHID' => array(
'columns' => array('authorPHID', 'status'),
),
'ownerOrdering' => array(
'columns' => array('ownerOrdering'),
),
'priority_2' => array(
'columns' => array('priority', 'subpriority'),
),
'key_dateCreated' => array(
'columns' => array('dateCreated'),
),
'key_dateModified' => array(
'columns' => array('dateModified'),
),
'key_title' => array(
'columns' => array('title(64)'),
),
'key_bridgedobject' => array(
'columns' => array('bridgedObjectPHID'),
'unique' => true,
),
'key_subtype' => array(
'columns' => array('subtype'),
),
'key_closed' => array(
'columns' => array('closedEpoch'),
),
'key_closer' => array(
'columns' => array('closerPHID', 'closedEpoch'),
),
),
) + parent::getConfiguration();
}
public function loadDependsOnTaskPHIDs() {
return PhabricatorEdgeQuery::loadDestinationPHIDs(
$this->getPHID(),
ManiphestTaskDependsOnTaskEdgeType::EDGECONST);
}
public function loadDependedOnByTaskPHIDs() {
return PhabricatorEdgeQuery::loadDestinationPHIDs(
$this->getPHID(),
ManiphestTaskDependedOnByTaskEdgeType::EDGECONST);
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(ManiphestTaskPHIDType::TYPECONST);
}
public function getSubscriberPHIDs() {
return $this->assertAttached($this->subscriberPHIDs);
}
public function getProjectPHIDs() {
return $this->assertAttached($this->edgeProjectPHIDs);
}
public function attachProjectPHIDs(array $phids) {
$this->edgeProjectPHIDs = $phids;
return $this;
}
public function attachSubscriberPHIDs(array $phids) {
$this->subscriberPHIDs = $phids;
return $this;
}
public function setOwnerPHID($phid) {
$this->ownerPHID = nonempty($phid, null);
return $this;
}
public function getMonogram() {
return 'T'.$this->getID();
}
public function getURI() {
return '/'.$this->getMonogram();
}
public function attachGroupByProjectPHID($phid) {
$this->groupByProjectPHID = $phid;
return $this;
}
public function getGroupByProjectPHID() {
return $this->assertAttached($this->groupByProjectPHID);
}
public function save() {
if (!$this->mailKey) {
$this->mailKey = Filesystem::readRandomCharacters(20);
}
$result = parent::save();
return $result;
}
public function isClosed() {
return ManiphestTaskStatus::isClosedStatus($this->getStatus());
}
public function areCommentsLocked() {
if ($this->areEditsLocked()) {
return true;
}
return ManiphestTaskStatus::areCommentsLockedInStatus($this->getStatus());
}
public function areEditsLocked() {
return ManiphestTaskStatus::areEditsLockedInStatus($this->getStatus());
}
public function setProperty($key, $value) {
$this->properties[$key] = $value;
return $this;
}
public function getProperty($key, $default = null) {
return idx($this->properties, $key, $default);
}
public function getCoverImageFilePHID() {
return idx($this->properties, 'cover.filePHID');
}
public function getCoverImageThumbnailPHID() {
return idx($this->properties, 'cover.thumbnailPHID');
}
public function getWorkboardOrderVectors() {
return array(
PhabricatorProjectColumn::ORDER_PRIORITY => array(
(int)-$this->getPriority(),
- PhabricatorProjectColumn::NODETYPE_CARD,
- (double)-$this->getSubpriority(),
- (int)-$this->getID(),
),
);
}
public function getPriorityKeyword() {
$priority = $this->getPriority();
$keyword = ManiphestTaskPriority::getKeywordForTaskPriority($priority);
if ($keyword !== null) {
return $keyword;
}
return ManiphestTaskPriority::UNKNOWN_PRIORITY_KEYWORD;
}
private function comparePriorityTo(ManiphestTask $other) {
$upri = $this->getPriority();
$vpri = $other->getPriority();
if ($upri != $vpri) {
return ($upri - $vpri);
}
$usub = $this->getSubpriority();
$vsub = $other->getSubpriority();
if ($usub != $vsub) {
return ($usub - $vsub);
}
$uid = $this->getID();
$vid = $other->getID();
if ($uid != $vid) {
return ($uid - $vid);
}
return 0;
}
public function isLowerPriorityThan(ManiphestTask $other) {
return ($this->comparePriorityTo($other) < 0);
}
public function isHigherPriorityThan(ManiphestTask $other) {
return ($this->comparePriorityTo($other) > 0);
}
public function getWorkboardProperties() {
return array(
'status' => $this->getStatus(),
'points' => (double)$this->getPoints(),
'priority' => $this->getPriority(),
);
}
/* -( PhabricatorSubscribableInterface )----------------------------------- */
public function isAutomaticallySubscribed($phid) {
return ($phid == $this->getOwnerPHID());
}
/* -( Markup Interface )--------------------------------------------------- */
/**
* @task markup
*/
public function getMarkupFieldKey($field) {
$content = $this->getMarkupText($field);
return PhabricatorMarkupEngine::digestRemarkupContent($this, $content);
}
/**
* @task markup
*/
public function getMarkupText($field) {
return $this->getDescription();
}
/**
* @task markup
*/
public function newMarkupEngine($field) {
return PhabricatorMarkupEngine::newManiphestMarkupEngine();
}
/**
* @task markup
*/
public function didMarkupText(
$field,
$output,
PhutilMarkupEngine $engine) {
return $output;
}
/**
* @task markup
*/
public function shouldUseMarkupCache($field) {
return (bool)$this->getID();
}
/* -( Policy Interface )--------------------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_INTERACT,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_INTERACT:
if ($this->areCommentsLocked()) {
return PhabricatorPolicies::POLICY_NOONE;
} else {
return $this->getViewPolicy();
}
case PhabricatorPolicyCapability::CAN_EDIT:
if ($this->areEditsLocked()) {
return PhabricatorPolicies::POLICY_NOONE;
} else {
return $this->getEditPolicy();
}
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $user) {
// The owner of a task can always view and edit it.
$owner_phid = $this->getOwnerPHID();
if ($owner_phid) {
$user_phid = $user->getPHID();
if ($user_phid == $owner_phid) {
return true;
}
}
return false;
}
public function describeAutomaticCapability($capability) {
return pht('The owner of a task can always view and edit it.');
}
/* -( PhabricatorTokenReceiverInterface )---------------------------------- */
public function getUsersToNotifyOfTokenGiven() {
// Sort of ambiguous who this was intended for; just let them both know.
return array_filter(
array_unique(
array(
$this->getAuthorPHID(),
$this->getOwnerPHID(),
)));
}
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
public function getCustomFieldSpecificationForRole($role) {
return PhabricatorEnv::getEnvConfig('maniphest.fields');
}
public function getCustomFieldBaseClass() {
return 'ManiphestCustomField';
}
public function getCustomFields() {
return $this->assertAttached($this->customFields);
}
public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
$this->customFields = $fields;
return $this;
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->openTransaction();
$this->delete();
$this->saveTransaction();
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new ManiphestTransactionEditor();
}
public function getApplicationTransactionTemplate() {
return new ManiphestTransaction();
}
/* -( PhabricatorSpacesInterface )----------------------------------------- */
public function getSpacePHID() {
return $this->spacePHID;
}
/* -( PhabricatorConduitResultInterface )---------------------------------- */
public function getFieldSpecificationsForConduit() {
return array(
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('title')
->setType('string')
->setDescription(pht('The title of the task.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('description')
->setType('remarkup')
->setDescription(pht('The task description.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('authorPHID')
->setType('phid')
->setDescription(pht('Original task author.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('ownerPHID')
->setType('phid?')
->setDescription(pht('Current task owner, if task is assigned.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('status')
->setType('map<string, wild>')
->setDescription(pht('Information about task status.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('priority')
->setType('map<string, wild>')
->setDescription(pht('Information about task priority.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('points')
->setType('points')
->setDescription(pht('Point value of the task.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('subtype')
->setType('string')
->setDescription(pht('Subtype of the task.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('closerPHID')
->setType('phid?')
->setDescription(
pht('User who closed the task, if the task is closed.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('dateClosed')
->setType('int?')
->setDescription(
pht('Epoch timestamp when the task was closed.')),
);
}
public function getFieldValuesForConduit() {
$status_value = $this->getStatus();
$status_info = array(
'value' => $status_value,
'name' => ManiphestTaskStatus::getTaskStatusName($status_value),
'color' => ManiphestTaskStatus::getStatusColor($status_value),
);
$priority_value = (int)$this->getPriority();
$priority_info = array(
'value' => $priority_value,
'subpriority' => (double)$this->getSubpriority(),
'name' => ManiphestTaskPriority::getTaskPriorityName($priority_value),
'color' => ManiphestTaskPriority::getTaskPriorityColor($priority_value),
);
$closed_epoch = $this->getClosedEpoch();
if ($closed_epoch !== null) {
$closed_epoch = (int)$closed_epoch;
}
return array(
'name' => $this->getTitle(),
'description' => array(
'raw' => $this->getDescription(),
),
'authorPHID' => $this->getAuthorPHID(),
'ownerPHID' => $this->getOwnerPHID(),
'status' => $status_info,
'priority' => $priority_info,
'points' => $this->getPoints(),
'subtype' => $this->getSubtype(),
'closerPHID' => $this->getCloserPHID(),
'dateClosed' => $closed_epoch,
);
}
public function getConduitSearchAttachments() {
return array(
id(new PhabricatorBoardColumnsSearchEngineAttachment())
->setAttachmentKey('columns'),
);
}
public function newSubtypeObject() {
$subtype_key = $this->getEditEngineSubtype();
$subtype_map = $this->newEditEngineSubtypeMap();
return $subtype_map->getSubtype($subtype_key);
}
/* -( PhabricatorFulltextInterface )--------------------------------------- */
public function newFulltextEngine() {
return new ManiphestTaskFulltextEngine();
}
/* -( DoorkeeperBridgedObjectInterface )----------------------------------- */
public function getBridgedObject() {
return $this->assertAttached($this->bridgedObject);
}
public function attachBridgedObject(
DoorkeeperExternalObject $object = null) {
$this->bridgedObject = $object;
return $this;
}
/* -( PhabricatorEditEngineSubtypeInterface )------------------------------ */
public function getEditEngineSubtype() {
return $this->getSubtype();
}
public function setEditEngineSubtype($value) {
return $this->setSubtype($value);
}
public function newEditEngineSubtypeMap() {
$config = PhabricatorEnv::getEnvConfig('maniphest.subtypes');
return PhabricatorEditEngineSubtype::newSubtypeMap($config);
}
/* -( PhabricatorEditEngineLockableInterface )----------------------------- */
public function newEditEngineLock() {
return new ManiphestTaskEditEngineLock();
}
/* -( PhabricatorFerretInterface )----------------------------------------- */
public function newFerretEngine() {
return new ManiphestTaskFerretEngine();
}
/* -( PhabricatorEditEngineMFAInterface )---------------------------------- */
public function newEditEngineMFAEngine() {
return new ManiphestTaskMFAEngine();
}
/* -( PhabricatorPolicyCodexInterface )------------------------------------ */
public function newPolicyCodex() {
return new ManiphestTaskPolicyCodex();
}
/* -( PhabricatorUnlockableInterface )------------------------------------- */
public function newUnlockEngine() {
return new ManiphestTaskUnlockEngine();
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
index 32374ad8be..6df43addd9 100644
--- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php
+++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
@@ -1,1428 +1,1420 @@
<?php
final class PhabricatorProjectBoardViewController
extends PhabricatorProjectBoardController {
const BATCH_EDIT_ALL = 'all';
private $id;
private $slug;
private $queryKey;
private $sortKey;
private $showHidden;
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$response = $this->loadProject();
if ($response) {
return $response;
}
$project = $this->getProject();
$this->readRequestState();
$board_uri = $this->getApplicationURI('board/'.$project->getID().'/');
$search_engine = id(new ManiphestTaskSearchEngine())
->setViewer($viewer)
->setBaseURI($board_uri)
->setIsBoardView(true);
if ($request->isFormPost()
&& !$request->getBool('initialize')
&& !$request->getStr('move')
&& !$request->getStr('queryColumnID')) {
$saved = $search_engine->buildSavedQueryFromRequest($request);
$search_engine->saveQuery($saved);
$filter_form = id(new AphrontFormView())
->setUser($viewer);
$search_engine->buildSearchForm($filter_form, $saved);
if ($search_engine->getErrors()) {
return $this->newDialog()
->setWidth(AphrontDialogView::WIDTH_FULL)
->setTitle(pht('Advanced Filter'))
->appendChild($filter_form->buildLayoutView())
->setErrors($search_engine->getErrors())
->setSubmitURI($board_uri)
->addSubmitButton(pht('Apply Filter'))
->addCancelButton($board_uri);
}
return id(new AphrontRedirectResponse())->setURI(
$this->getURIWithState(
$search_engine->getQueryResultsPageURI($saved->getQueryKey())));
}
$query_key = $this->getDefaultFilter($project);
$request_query = $request->getStr('filter');
if (strlen($request_query)) {
$query_key = $request_query;
}
$uri_query = $request->getURIData('queryKey');
if (strlen($uri_query)) {
$query_key = $uri_query;
}
$this->queryKey = $query_key;
$custom_query = null;
if ($search_engine->isBuiltinQuery($query_key)) {
$saved = $search_engine->buildSavedQueryFromBuiltin($query_key);
} else {
$saved = id(new PhabricatorSavedQueryQuery())
->setViewer($viewer)
->withQueryKeys(array($query_key))
->executeOne();
if (!$saved) {
return new Aphront404Response();
}
$custom_query = $saved;
}
if ($request->getURIData('filter')) {
$filter_form = id(new AphrontFormView())
->setUser($viewer);
$search_engine->buildSearchForm($filter_form, $saved);
return $this->newDialog()
->setWidth(AphrontDialogView::WIDTH_FULL)
->setTitle(pht('Advanced Filter'))
->appendChild($filter_form->buildLayoutView())
->setSubmitURI($board_uri)
->addSubmitButton(pht('Apply Filter'))
->addCancelButton($board_uri);
}
$task_query = $search_engine->buildQueryFromSavedQuery($saved);
$select_phids = array($project->getPHID());
if ($project->getHasSubprojects() || $project->getHasMilestones()) {
$descendants = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withAncestorProjectPHIDs($select_phids)
->execute();
foreach ($descendants as $descendant) {
$select_phids[] = $descendant->getPHID();
}
}
$tasks = $task_query
->withEdgeLogicPHIDs(
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
PhabricatorQueryConstraint::OPERATOR_ANCESTOR,
array($select_phids))
->setOrder(ManiphestTaskQuery::ORDER_PRIORITY)
->setViewer($viewer)
->execute();
$tasks = mpull($tasks, null, 'getPHID');
$board_phid = $project->getPHID();
// Regardless of display order, pass tasks to the layout engine in ID order
// so layout is consistent.
$board_tasks = msort($tasks, 'getID');
$layout_engine = id(new PhabricatorBoardLayoutEngine())
->setViewer($viewer)
->setBoardPHIDs(array($board_phid))
->setObjectPHIDs(array_keys($board_tasks))
->setFetchAllBoards(true)
->executeLayout();
$columns = $layout_engine->getColumns($board_phid);
if (!$columns || !$project->getHasWorkboard()) {
$has_normal_columns = false;
foreach ($columns as $column) {
if (!$column->getProxyPHID()) {
$has_normal_columns = true;
break;
}
}
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
PhabricatorPolicyCapability::CAN_EDIT);
if (!$has_normal_columns) {
if (!$can_edit) {
$content = $this->buildNoAccessContent($project);
} else {
$content = $this->buildInitializeContent($project);
}
} else {
if (!$can_edit) {
$content = $this->buildDisabledContent($project);
} else {
$content = $this->buildEnableContent($project);
}
}
if ($content instanceof AphrontResponse) {
return $content;
}
$nav = $this->getProfileMenu();
$nav->selectFilter(PhabricatorProject::ITEM_WORKBOARD);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Workboard'));
return $this->newPage()
->setTitle(
array(
$project->getDisplayName(),
pht('Workboard'),
))
->setNavigation($nav)
->setCrumbs($crumbs)
->appendChild($content);
}
// If the user wants to turn a particular column into a query, build an
// apropriate filter and redirect them to the query results page.
$query_column_id = $request->getInt('queryColumnID');
if ($query_column_id) {
$column_id_map = mpull($columns, null, 'getID');
$query_column = idx($column_id_map, $query_column_id);
if (!$query_column) {
return new Aphront404Response();
}
// Create a saved query to combine the active filter on the workboard
// with the column filter. If the user currently has constraints on the
// board, we want to add a new column or project constraint, not
// completely replace the constraints.
$saved_query = $saved->newCopy();
if ($query_column->getProxyPHID()) {
$project_phids = $saved_query->getParameter('projectPHIDs');
if (!$project_phids) {
$project_phids = array();
}
$project_phids[] = $query_column->getProxyPHID();
$saved_query->setParameter('projectPHIDs', $project_phids);
} else {
$saved_query->setParameter(
'columnPHIDs',
array($query_column->getPHID()));
}
$search_engine = id(new ManiphestTaskSearchEngine())
->setViewer($viewer);
$search_engine->saveQuery($saved_query);
$query_key = $saved_query->getQueryKey();
$query_uri = new PhutilURI("/maniphest/query/{$query_key}/#R");
return id(new AphrontRedirectResponse())
->setURI($query_uri);
}
$task_can_edit_map = id(new PhabricatorPolicyFilter())
->setViewer($viewer)
->requireCapabilities(array(PhabricatorPolicyCapability::CAN_EDIT))
->apply($tasks);
// If this is a batch edit, select the editable tasks in the chosen column
// and ship the user into the batch editor.
$batch_edit = $request->getStr('batch');
if ($batch_edit) {
if ($batch_edit !== self::BATCH_EDIT_ALL) {
$column_id_map = mpull($columns, null, 'getID');
$batch_column = idx($column_id_map, $batch_edit);
if (!$batch_column) {
return new Aphront404Response();
}
$batch_task_phids = $layout_engine->getColumnObjectPHIDs(
$board_phid,
$batch_column->getPHID());
foreach ($batch_task_phids as $key => $batch_task_phid) {
if (empty($task_can_edit_map[$batch_task_phid])) {
unset($batch_task_phids[$key]);
}
}
$batch_tasks = array_select_keys($tasks, $batch_task_phids);
} else {
$batch_tasks = $task_can_edit_map;
}
if (!$batch_tasks) {
$cancel_uri = $this->getURIWithState($board_uri);
return $this->newDialog()
->setTitle(pht('No Editable Tasks'))
->appendParagraph(
pht(
'The selected column contains no visible tasks which you '.
'have permission to edit.'))
->addCancelButton($board_uri);
}
// Create a saved query to hold the working set. This allows us to get
// around URI length limitations with a long "?ids=..." query string.
// For details, see T10268.
$search_engine = id(new ManiphestTaskSearchEngine())
->setViewer($viewer);
$saved_query = $search_engine->newSavedQuery();
$saved_query->setParameter('ids', mpull($batch_tasks, 'getID'));
$search_engine->saveQuery($saved_query);
$query_key = $saved_query->getQueryKey();
$bulk_uri = new PhutilURI("/maniphest/bulk/query/{$query_key}/");
$bulk_uri->replaceQueryParam('board', $this->id);
return id(new AphrontRedirectResponse())
->setURI($bulk_uri);
}
$move_id = $request->getStr('move');
if (strlen($move_id)) {
$column_id_map = mpull($columns, null, 'getID');
$move_column = idx($column_id_map, $move_id);
if (!$move_column) {
return new Aphront404Response();
}
$move_task_phids = $layout_engine->getColumnObjectPHIDs(
$board_phid,
$move_column->getPHID());
foreach ($move_task_phids as $key => $move_task_phid) {
if (empty($task_can_edit_map[$move_task_phid])) {
unset($move_task_phids[$key]);
}
}
$move_tasks = array_select_keys($tasks, $move_task_phids);
$cancel_uri = $this->getURIWithState($board_uri);
if (!$move_tasks) {
return $this->newDialog()
->setTitle(pht('No Movable Tasks'))
->appendParagraph(
pht(
'The selected column contains no visible tasks which you '.
'have permission to move.'))
->addCancelButton($cancel_uri);
}
$move_project_phid = $project->getPHID();
$move_column_phid = null;
$move_project = null;
$move_column = null;
$columns = null;
$errors = array();
if ($request->isFormPost()) {
$move_project_phid = head($request->getArr('moveProjectPHID'));
if (!$move_project_phid) {
$move_project_phid = $request->getStr('moveProjectPHID');
}
if (!$move_project_phid) {
if ($request->getBool('hasProject')) {
$errors[] = pht('Choose a project to move tasks to.');
}
} else {
$target_project = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withPHIDs(array($move_project_phid))
->executeOne();
if (!$target_project) {
$errors[] = pht('You must choose a valid project.');
} else if (!$project->getHasWorkboard()) {
$errors[] = pht(
'You must choose a project with a workboard.');
} else {
$move_project = $target_project;
}
}
if ($move_project) {
$move_engine = id(new PhabricatorBoardLayoutEngine())
->setViewer($viewer)
->setBoardPHIDs(array($move_project->getPHID()))
->setFetchAllBoards(true)
->executeLayout();
$columns = $move_engine->getColumns($move_project->getPHID());
$columns = mpull($columns, null, 'getPHID');
foreach ($columns as $key => $column) {
if ($column->isHidden()) {
unset($columns[$key]);
}
}
$move_column_phid = $request->getStr('moveColumnPHID');
if (!$move_column_phid) {
if ($request->getBool('hasColumn')) {
$errors[] = pht('Choose a column to move tasks to.');
}
} else {
if (empty($columns[$move_column_phid])) {
$errors[] = pht(
'Choose a valid column on the target workboard to move '.
'tasks to.');
} else if ($columns[$move_column_phid]->getID() == $move_id) {
$errors[] = pht(
'You can not move tasks from a column to itself.');
} else {
$move_column = $columns[$move_column_phid];
}
}
}
}
if ($move_column && $move_project) {
foreach ($move_tasks as $move_task) {
$xactions = array();
// If we're switching projects, get out of the old project first
// and move to the new project.
if ($move_project->getID() != $project->getID()) {
$xactions[] = id(new ManiphestTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue(
'edge:type',
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST)
->setNewValue(
array(
'-' => array(
$project->getPHID() => $project->getPHID(),
),
'+' => array(
$move_project->getPHID() => $move_project->getPHID(),
),
));
}
$xactions[] = id(new ManiphestTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_COLUMNS)
->setNewValue(
array(
array(
'columnPHID' => $move_column->getPHID(),
),
));
$editor = id(new ManiphestTransactionEditor())
->setActor($viewer)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request);
$editor->applyTransactions($move_task, $xactions);
}
return id(new AphrontRedirectResponse())
->setURI($cancel_uri);
}
if ($move_project) {
$column_form = id(new AphrontFormView())
->setViewer($viewer)
->appendControl(
id(new AphrontFormSelectControl())
->setName('moveColumnPHID')
->setLabel(pht('Move to Column'))
->setValue($move_column_phid)
->setOptions(mpull($columns, 'getDisplayName', 'getPHID')));
return $this->newDialog()
->setTitle(pht('Move Tasks'))
->setWidth(AphrontDialogView::WIDTH_FORM)
->setErrors($errors)
->addHiddenInput('move', $move_id)
->addHiddenInput('moveProjectPHID', $move_project->getPHID())
->addHiddenInput('hasColumn', true)
->addHiddenInput('hasProject', true)
->appendParagraph(
pht(
'Choose a column on the %s workboard to move tasks to:',
$viewer->renderHandle($move_project->getPHID())))
->appendForm($column_form)
->addSubmitButton(pht('Move Tasks'))
->addCancelButton($cancel_uri);
}
if ($move_project_phid) {
$move_project_phid_value = array($move_project_phid);
} else {
$move_project_phid_value = array();
}
$project_form = id(new AphrontFormView())
->setViewer($viewer)
->appendControl(
id(new AphrontFormTokenizerControl())
->setName('moveProjectPHID')
->setLimit(1)
->setLabel(pht('Move to Project'))
->setValue($move_project_phid_value)
->setDatasource(new PhabricatorProjectDatasource()));
return $this->newDialog()
->setTitle(pht('Move Tasks'))
->setWidth(AphrontDialogView::WIDTH_FORM)
->setErrors($errors)
->addHiddenInput('move', $move_id)
->addHiddenInput('hasProject', true)
->appendForm($project_form)
->addSubmitButton(pht('Continue'))
->addCancelButton($cancel_uri);
}
$board_id = celerity_generate_unique_node_id();
$board = id(new PHUIWorkboardView())
->setUser($viewer)
->setID($board_id)
->addSigil('jx-workboard')
->setMetadata(
array(
'boardPHID' => $project->getPHID(),
));
$visible_columns = array();
$column_phids = array();
$visible_phids = array();
foreach ($columns as $column) {
if (!$this->showHidden) {
if ($column->isHidden()) {
continue;
}
}
$proxy = $column->getProxy();
if ($proxy && !$proxy->isMilestone()) {
// TODO: For now, don't show subproject columns because we can't
// handle tasks with multiple positions yet.
continue;
}
$task_phids = $layout_engine->getColumnObjectPHIDs(
$board_phid,
$column->getPHID());
$column_tasks = array_select_keys($tasks, $task_phids);
-
- // If we aren't using "natural" order, reorder the column by the original
- // query order.
- if ($this->sortKey != PhabricatorProjectColumn::ORDER_NATURAL) {
- $column_tasks = array_select_keys($column_tasks, array_keys($tasks));
- }
-
$column_phid = $column->getPHID();
$visible_columns[$column_phid] = $column;
$column_phids[$column_phid] = $column_tasks;
foreach ($column_tasks as $phid => $task) {
$visible_phids[$phid] = $phid;
}
}
$rendering_engine = id(new PhabricatorBoardRenderingEngine())
->setViewer($viewer)
->setObjects(array_select_keys($tasks, $visible_phids))
->setEditMap($task_can_edit_map)
->setExcludedProjectPHIDs($select_phids);
$templates = array();
$column_maps = array();
$all_tasks = array();
foreach ($visible_columns as $column_phid => $column) {
$column_tasks = $column_phids[$column_phid];
$panel = id(new PHUIWorkpanelView())
->setHeader($column->getDisplayName())
->setSubHeader($column->getDisplayType())
->addSigil('workpanel');
$proxy = $column->getProxy();
if ($proxy) {
$proxy_id = $proxy->getID();
$href = $this->getApplicationURI("view/{$proxy_id}/");
$panel->setHref($href);
}
$header_icon = $column->getHeaderIcon();
if ($header_icon) {
$panel->setHeaderIcon($header_icon);
}
$display_class = $column->getDisplayClass();
if ($display_class) {
$panel->addClass($display_class);
}
if ($column->isHidden()) {
$panel->addClass('project-panel-hidden');
}
$column_menu = $this->buildColumnMenu($project, $column);
$panel->addHeaderAction($column_menu);
$count_tag = id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE)
->setColor(PHUITagView::COLOR_BLUE)
->addSigil('column-points')
->setName(
javelin_tag(
'span',
array(
'sigil' => 'column-points-content',
),
pht('-')))
->setStyle('display: none');
$panel->setHeaderTag($count_tag);
$cards = id(new PHUIObjectItemListView())
->setUser($viewer)
->setFlush(true)
->setAllowEmptyList(true)
->addSigil('project-column')
->setItemClass('phui-workcard')
->setMetadata(
array(
'columnPHID' => $column->getPHID(),
'pointLimit' => $column->getPointLimit(),
));
foreach ($column_tasks as $task) {
$object_phid = $task->getPHID();
$card = $rendering_engine->renderCard($object_phid);
$templates[$object_phid] = hsprintf('%s', $card->getItem());
$column_maps[$column_phid][] = $object_phid;
$all_tasks[$object_phid] = $task;
}
$panel->setCards($cards);
$board->addPanel($panel);
}
// It's possible for tasks to have an invalid/unknown priority in the
// database. We still want to generate a header for these tasks so we
// don't break the workboard.
$priorities =
ManiphestTaskPriority::getTaskPriorityMap() +
mpull($all_tasks, null, 'getPriority');
$priorities = array_keys($priorities);
$headers = array();
foreach ($priorities as $priority) {
$header_key = sprintf('priority(%s)', $priority);
$priority_name = ManiphestTaskPriority::getTaskPriorityName($priority);
$priority_color = ManiphestTaskPriority::getTaskPriorityColor($priority);
$priority_icon = ManiphestTaskPriority::getTaskPriorityIcon($priority);
$icon_view = id(new PHUIIconView())
->setIcon("{$priority_icon} {$priority_color}");
$template = phutil_tag(
'li',
array(
'class' => 'workboard-group-header',
),
array(
$icon_view,
$priority_name,
));
$headers[] = array(
'order' => PhabricatorProjectColumn::ORDER_PRIORITY,
'key' => $header_key,
'template' => hsprintf('%s', $template),
'vector' => array(
(int)-$priority,
PhabricatorProjectColumn::NODETYPE_HEADER,
),
'editProperties' => array(
PhabricatorProjectColumn::ORDER_PRIORITY => (int)$priority,
),
);
}
$behavior_config = array(
'moveURI' => $this->getApplicationURI('move/'.$project->getID().'/'),
'uploadURI' => '/file/dropupload/',
'coverURI' => $this->getApplicationURI('cover/'),
'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold(),
'pointsEnabled' => ManiphestTaskPoints::getIsEnabled(),
'boardPHID' => $project->getPHID(),
'order' => $this->sortKey,
'headers' => $headers,
'templateMap' => $templates,
'columnMaps' => $column_maps,
'orderMaps' => mpull($all_tasks, 'getWorkboardOrderVectors'),
'propertyMaps' => mpull($all_tasks, 'getWorkboardProperties'),
'boardID' => $board_id,
'projectPHID' => $project->getPHID(),
);
$this->initBehavior('project-boards', $behavior_config);
-
$sort_menu = $this->buildSortMenu(
$viewer,
$project,
$this->sortKey);
$filter_menu = $this->buildFilterMenu(
$viewer,
$project,
$custom_query,
$search_engine,
$query_key);
$manage_menu = $this->buildManageMenu($project, $this->showHidden);
$header_link = phutil_tag(
'a',
array(
'href' => $this->getApplicationURI('profile/'.$project->getID().'/'),
),
$project->getName());
$board_box = id(new PHUIBoxView())
->appendChild($board)
->addClass('project-board-wrapper');
$nav = $this->getProfileMenu();
$divider = id(new PHUIListItemView())
->setType(PHUIListItemView::TYPE_DIVIDER);
$fullscreen = $this->buildFullscreenMenu();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Workboard'));
$crumbs->setBorder(true);
$crumbs->addAction($sort_menu);
$crumbs->addAction($filter_menu);
$crumbs->addAction($divider);
$crumbs->addAction($manage_menu);
$crumbs->addAction($fullscreen);
$page = $this->newPage()
->setTitle(
array(
$project->getDisplayName(),
pht('Workboard'),
))
->setPageObjectPHIDs(array($project->getPHID()))
->setShowFooter(false)
->setNavigation($nav)
->setCrumbs($crumbs)
->addQuicksandConfig(
array(
'boardConfig' => $behavior_config,
))
->appendChild(
array(
$board_box,
));
$background = $project->getDisplayWorkboardBackgroundColor();
require_celerity_resource('phui-workboard-color-css');
if ($background !== null) {
$background_color_class = "phui-workboard-{$background}";
$page->addClass('phui-workboard-color');
$page->addClass($background_color_class);
} else {
$page->addClass('phui-workboard-no-color');
}
return $page;
}
private function readRequestState() {
$request = $this->getRequest();
$project = $this->getProject();
$this->showHidden = $request->getBool('hidden');
$this->id = $project->getID();
$sort_key = $this->getDefaultSort($project);
$request_sort = $request->getStr('order');
if ($this->isValidSort($request_sort)) {
$sort_key = $request_sort;
}
$this->sortKey = $sort_key;
}
private function getDefaultSort(PhabricatorProject $project) {
$default_sort = $project->getDefaultWorkboardSort();
if ($this->isValidSort($default_sort)) {
return $default_sort;
}
return PhabricatorProjectColumn::DEFAULT_ORDER;
}
private function getDefaultFilter(PhabricatorProject $project) {
$default_filter = $project->getDefaultWorkboardFilter();
if (strlen($default_filter)) {
return $default_filter;
}
return 'open';
}
private function isValidSort($sort) {
switch ($sort) {
case PhabricatorProjectColumn::ORDER_NATURAL:
case PhabricatorProjectColumn::ORDER_PRIORITY:
return true;
}
return false;
}
private function buildSortMenu(
PhabricatorUser $viewer,
PhabricatorProject $project,
$sort_key) {
$sort_icon = id(new PHUIIconView())
->setIcon('fa-sort-amount-asc bluegrey');
$named = array(
PhabricatorProjectColumn::ORDER_NATURAL => pht('Natural'),
PhabricatorProjectColumn::ORDER_PRIORITY => pht('Sort by Priority'),
);
$base_uri = $this->getURIWithState();
$items = array();
foreach ($named as $key => $name) {
$is_selected = ($key == $sort_key);
if ($is_selected) {
$active_order = $name;
}
$item = id(new PhabricatorActionView())
->setIcon('fa-sort-amount-asc')
->setSelected($is_selected)
->setName($name);
$uri = $base_uri->alter('order', $key);
$item->setHref($uri);
$items[] = $item;
}
$id = $project->getID();
$save_uri = "default/{$id}/sort/";
$save_uri = $this->getApplicationURI($save_uri);
$save_uri = $this->getURIWithState($save_uri, $force = true);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
PhabricatorPolicyCapability::CAN_EDIT);
$items[] = id(new PhabricatorActionView())
->setIcon('fa-floppy-o')
->setName(pht('Save as Default'))
->setHref($save_uri)
->setWorkflow(true)
->setDisabled(!$can_edit);
$sort_menu = id(new PhabricatorActionListView())
->setUser($viewer);
foreach ($items as $item) {
$sort_menu->addAction($item);
}
$sort_button = id(new PHUIListItemView())
->setName($active_order)
->setIcon('fa-sort-amount-asc')
->setHref('#')
->addSigil('boards-dropdown-menu')
->setMetadata(
array(
'items' => hsprintf('%s', $sort_menu),
));
return $sort_button;
}
private function buildFilterMenu(
PhabricatorUser $viewer,
PhabricatorProject $project,
$custom_query,
PhabricatorApplicationSearchEngine $engine,
$query_key) {
$named = array(
'open' => pht('Open Tasks'),
'all' => pht('All Tasks'),
);
if ($viewer->isLoggedIn()) {
$named['assigned'] = pht('Assigned to Me');
}
if ($custom_query) {
$named[$custom_query->getQueryKey()] = pht('Custom Filter');
}
$items = array();
foreach ($named as $key => $name) {
$is_selected = ($key == $query_key);
if ($is_selected) {
$active_filter = $name;
}
$is_custom = false;
if ($custom_query) {
$is_custom = ($key == $custom_query->getQueryKey());
}
$item = id(new PhabricatorActionView())
->setIcon('fa-search')
->setSelected($is_selected)
->setName($name);
if ($is_custom) {
$uri = $this->getApplicationURI(
'board/'.$this->id.'/filter/query/'.$key.'/');
$item->setWorkflow(true);
} else {
$uri = $engine->getQueryResultsPageURI($key);
}
$uri = $this->getURIWithState($uri)
->removeQueryParam('filter');
$item->setHref($uri);
$items[] = $item;
}
$id = $project->getID();
$filter_uri = $this->getApplicationURI("board/{$id}/filter/");
$filter_uri = $this->getURIWithState($filter_uri, $force = true);
$items[] = id(new PhabricatorActionView())
->setIcon('fa-cog')
->setHref($filter_uri)
->setWorkflow(true)
->setName(pht('Advanced Filter...'));
$save_uri = "default/{$id}/filter/";
$save_uri = $this->getApplicationURI($save_uri);
$save_uri = $this->getURIWithState($save_uri, $force = true);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
PhabricatorPolicyCapability::CAN_EDIT);
$items[] = id(new PhabricatorActionView())
->setIcon('fa-floppy-o')
->setName(pht('Save as Default'))
->setHref($save_uri)
->setWorkflow(true)
->setDisabled(!$can_edit);
$filter_menu = id(new PhabricatorActionListView())
->setUser($viewer);
foreach ($items as $item) {
$filter_menu->addAction($item);
}
$filter_button = id(new PHUIListItemView())
->setName($active_filter)
->setIcon('fa-search')
->setHref('#')
->addSigil('boards-dropdown-menu')
->setMetadata(
array(
'items' => hsprintf('%s', $filter_menu),
));
return $filter_button;
}
private function buildManageMenu(
PhabricatorProject $project,
$show_hidden) {
$request = $this->getRequest();
$viewer = $request->getUser();
$id = $project->getID();
$manage_uri = $this->getApplicationURI("board/{$id}/manage/");
$add_uri = $this->getApplicationURI("board/{$id}/edit/");
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
PhabricatorPolicyCapability::CAN_EDIT);
$manage_items = array();
$manage_items[] = id(new PhabricatorActionView())
->setIcon('fa-plus')
->setName(pht('Add Column'))
->setHref($add_uri)
->setDisabled(!$can_edit)
->setWorkflow(true);
$reorder_uri = $this->getApplicationURI("board/{$id}/reorder/");
$manage_items[] = id(new PhabricatorActionView())
->setIcon('fa-exchange')
->setName(pht('Reorder Columns'))
->setHref($reorder_uri)
->setDisabled(!$can_edit)
->setWorkflow(true);
if ($show_hidden) {
$hidden_uri = $this->getURIWithState()
->removeQueryParam('hidden');
$hidden_icon = 'fa-eye-slash';
$hidden_text = pht('Hide Hidden Columns');
} else {
$hidden_uri = $this->getURIWithState()
->replaceQueryParam('hidden', 'true');
$hidden_icon = 'fa-eye';
$hidden_text = pht('Show Hidden Columns');
}
$manage_items[] = id(new PhabricatorActionView())
->setIcon($hidden_icon)
->setName($hidden_text)
->setHref($hidden_uri);
$manage_items[] = id(new PhabricatorActionView())
->setType(PhabricatorActionView::TYPE_DIVIDER);
$background_uri = $this->getApplicationURI("board/{$id}/background/");
$manage_items[] = id(new PhabricatorActionView())
->setIcon('fa-paint-brush')
->setName(pht('Change Background Color'))
->setHref($background_uri)
->setDisabled(!$can_edit)
->setWorkflow(false);
$manage_uri = $this->getApplicationURI("board/{$id}/manage/");
$manage_items[] = id(new PhabricatorActionView())
->setIcon('fa-gear')
->setName(pht('Manage Workboard'))
->setHref($manage_uri);
$batch_edit_uri = $request->getRequestURI();
$batch_edit_uri->replaceQueryParam('batch', self::BATCH_EDIT_ALL);
$can_batch_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
PhabricatorApplication::getByClass('PhabricatorManiphestApplication'),
ManiphestBulkEditCapability::CAPABILITY);
$manage_menu = id(new PhabricatorActionListView())
->setUser($viewer);
foreach ($manage_items as $item) {
$manage_menu->addAction($item);
}
$manage_button = id(new PHUIListItemView())
->setIcon('fa-cog')
->setHref('#')
->addSigil('boards-dropdown-menu')
->addSigil('has-tooltip')
->setMetadata(
array(
'tip' => pht('Manage'),
'align' => 'S',
'items' => hsprintf('%s', $manage_menu),
));
return $manage_button;
}
private function buildFullscreenMenu() {
$up = id(new PHUIListItemView())
->setIcon('fa-arrows-alt')
->setHref('#')
->addClass('phui-workboard-expand-icon')
->addSigil('jx-toggle-class')
->addSigil('has-tooltip')
->setMetaData(array(
'tip' => pht('Fullscreen'),
'map' => array(
'phabricator-standard-page' => 'phui-workboard-fullscreen',
),
));
return $up;
}
private function buildColumnMenu(
PhabricatorProject $project,
PhabricatorProjectColumn $column) {
$request = $this->getRequest();
$viewer = $request->getUser();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
PhabricatorPolicyCapability::CAN_EDIT);
$column_items = array();
if ($column->getProxyPHID()) {
$default_phid = $column->getProxyPHID();
} else {
$default_phid = $column->getProjectPHID();
}
$specs = id(new ManiphestEditEngine())
->setViewer($viewer)
->newCreateActionSpecifications(array());
foreach ($specs as $spec) {
$column_items[] = id(new PhabricatorActionView())
->setIcon($spec['icon'])
->setName($spec['name'])
->setHref($spec['uri'])
->setDisabled($spec['disabled'])
->addSigil('column-add-task')
->setMetadata(
array(
'createURI' => $spec['uri'],
'columnPHID' => $column->getPHID(),
'boardPHID' => $project->getPHID(),
'projectPHID' => $default_phid,
));
}
if (count($specs) > 1) {
$column_items[] = id(new PhabricatorActionView())
->setType(PhabricatorActionView::TYPE_DIVIDER);
}
$batch_edit_uri = $request->getRequestURI();
$batch_edit_uri->replaceQueryParam('batch', $column->getID());
$can_batch_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
PhabricatorApplication::getByClass('PhabricatorManiphestApplication'),
ManiphestBulkEditCapability::CAPABILITY);
$column_items[] = id(new PhabricatorActionView())
->setIcon('fa-list-ul')
->setName(pht('Bulk Edit Tasks...'))
->setHref($batch_edit_uri)
->setDisabled(!$can_batch_edit);
$batch_move_uri = $request->getRequestURI();
$batch_move_uri->replaceQueryParam('move', $column->getID());
$column_items[] = id(new PhabricatorActionView())
->setIcon('fa-arrow-right')
->setName(pht('Move Tasks to Column...'))
->setHref($batch_move_uri)
->setWorkflow(true);
$query_uri = $request->getRequestURI();
$query_uri->replaceQueryParam('queryColumnID', $column->getID());
$column_items[] = id(new PhabricatorActionView())
->setName(pht('View as Query'))
->setIcon('fa-search')
->setHref($query_uri);
$edit_uri = 'board/'.$this->id.'/edit/'.$column->getID().'/';
$column_items[] = id(new PhabricatorActionView())
->setName(pht('Edit Column'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI($edit_uri))
->setDisabled(!$can_edit)
->setWorkflow(true);
$can_hide = ($can_edit && !$column->isDefaultColumn());
$hide_uri = 'board/'.$this->id.'/hide/'.$column->getID().'/';
$hide_uri = $this->getApplicationURI($hide_uri);
$hide_uri = $this->getURIWithState($hide_uri);
if (!$column->isHidden()) {
$column_items[] = id(new PhabricatorActionView())
->setName(pht('Hide Column'))
->setIcon('fa-eye-slash')
->setHref($hide_uri)
->setDisabled(!$can_hide)
->setWorkflow(true);
} else {
$column_items[] = id(new PhabricatorActionView())
->setName(pht('Show Column'))
->setIcon('fa-eye')
->setHref($hide_uri)
->setDisabled(!$can_hide)
->setWorkflow(true);
}
$column_menu = id(new PhabricatorActionListView())
->setUser($viewer);
foreach ($column_items as $item) {
$column_menu->addAction($item);
}
$column_button = id(new PHUIIconView())
->setIcon('fa-caret-down')
->setHref('#')
->addSigil('boards-dropdown-menu')
->setMetadata(
array(
'items' => hsprintf('%s', $column_menu),
));
return $column_button;
}
/**
* Add current state parameters (like order and the visibility of hidden
* columns) to a URI.
*
* This allows actions which toggle or adjust one piece of state to keep
* the rest of the board state persistent. If no URI is provided, this method
* starts with the request URI.
*
* @param string|null URI to add state parameters to.
* @param bool True to explicitly include all state.
* @return PhutilURI URI with state parameters.
*/
private function getURIWithState($base = null, $force = false) {
$project = $this->getProject();
if ($base === null) {
$base = $this->getRequest()->getPath();
}
$base = new PhutilURI($base);
if ($force || ($this->sortKey != $this->getDefaultSort($project))) {
if ($this->sortKey !== null) {
$base->replaceQueryParam('order', $this->sortKey);
} else {
$base->removeQueryParam('order');
}
} else {
$base->removeQueryParam('order');
}
if ($force || ($this->queryKey != $this->getDefaultFilter($project))) {
if ($this->queryKey !== null) {
$base->replaceQueryParam('filter', $this->queryKey);
} else {
$base->removeQueryParam('filter');
}
} else {
$base->removeQueryParam('filter');
}
if ($this->showHidden) {
$base->replaceQueryParam('hidden', 'true');
} else {
$base->removeQueryParam('hidden');
}
return $base;
}
private function buildInitializeContent(PhabricatorProject $project) {
$request = $this->getRequest();
$viewer = $this->getViewer();
$type = $request->getStr('initialize-type');
$id = $project->getID();
$profile_uri = $this->getApplicationURI("profile/{$id}/");
$board_uri = $this->getApplicationURI("board/{$id}/");
$import_uri = $this->getApplicationURI("board/{$id}/import/");
$set_default = $request->getBool('default');
if ($set_default) {
$this
->getProfileMenuEngine()
->adjustDefault(PhabricatorProject::ITEM_WORKBOARD);
}
if ($request->isFormPost()) {
if ($type == 'backlog-only') {
$column = PhabricatorProjectColumn::initializeNewColumn($viewer)
->setSequence(0)
->setProperty('isDefault', true)
->setProjectPHID($project->getPHID())
->save();
$xactions = array();
$xactions[] = id(new PhabricatorProjectTransaction())
->setTransactionType(
PhabricatorProjectWorkboardTransaction::TRANSACTIONTYPE)
->setNewValue(1);
id(new PhabricatorProjectTransactionEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->applyTransactions($project, $xactions);
return id(new AphrontRedirectResponse())
->setURI($board_uri);
} else {
return id(new AphrontRedirectResponse())
->setURI($import_uri);
}
}
// TODO: Tailor this UI if the project is already a parent project. We
// should not offer options for creating a parent project workboard, since
// they can't have their own columns.
$new_selector = id(new AphrontFormRadioButtonControl())
->setLabel(pht('Columns'))
->setName('initialize-type')
->setValue('backlog-only')
->addButton(
'backlog-only',
pht('New Empty Board'),
pht('Create a new board with just a backlog column.'))
->addButton(
'import',
pht('Import Columns'),
pht('Import board columns from another project.'));
$default_checkbox = id(new AphrontFormCheckboxControl())
->setLabel(pht('Make Default'))
->addCheckbox(
'default',
1,
pht('Make the workboard the default view for this project.'),
true);
$form = id(new AphrontFormView())
->setUser($viewer)
->addHiddenInput('initialize', 1)
->appendRemarkupInstructions(
pht('The workboard for this project has not been created yet.'))
->appendControl($new_selector)
->appendControl($default_checkbox)
->appendControl(
id(new AphrontFormSubmitControl())
->addCancelButton($profile_uri)
->setValue(pht('Create Workboard')));
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Create Workboard'))
->setForm($form);
return $box;
}
private function buildNoAccessContent(PhabricatorProject $project) {
$viewer = $this->getViewer();
$id = $project->getID();
$profile_uri = $this->getApplicationURI("profile/{$id}/");
return $this->newDialog()
->setTitle(pht('Unable to Create Workboard'))
->appendParagraph(
pht(
'The workboard for this project has not been created yet, '.
'but you do not have permission to create it. Only users '.
'who can edit this project can create a workboard for it.'))
->addCancelButton($profile_uri);
}
private function buildEnableContent(PhabricatorProject $project) {
$request = $this->getRequest();
$viewer = $this->getViewer();
$id = $project->getID();
$profile_uri = $this->getApplicationURI("profile/{$id}/");
$board_uri = $this->getApplicationURI("board/{$id}/");
if ($request->isFormPost()) {
$xactions = array();
$xactions[] = id(new PhabricatorProjectTransaction())
->setTransactionType(
PhabricatorProjectWorkboardTransaction::TRANSACTIONTYPE)
->setNewValue(1);
id(new PhabricatorProjectTransactionEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->applyTransactions($project, $xactions);
return id(new AphrontRedirectResponse())
->setURI($board_uri);
}
return $this->newDialog()
->setTitle(pht('Workboard Disabled'))
->addHiddenInput('initialize', 1)
->appendParagraph(
pht(
'This workboard has been disabled, but can be restored to its '.
'former glory.'))
->addCancelButton($profile_uri)
->addSubmitButton(pht('Enable Workboard'));
}
private function buildDisabledContent(PhabricatorProject $project) {
$viewer = $this->getViewer();
$id = $project->getID();
$profile_uri = $this->getApplicationURI("profile/{$id}/");
return $this->newDialog()
->setTitle(pht('Workboard Disabled'))
->appendParagraph(
pht(
'This workboard has been disabled, and you do not have permission '.
'to enable it. Only users who can edit this project can restore '.
'the workboard.'))
->addCancelButton($profile_uri);
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectMoveController.php b/src/applications/project/controller/PhabricatorProjectMoveController.php
index 09fca4692d..7a771ea7e8 100644
--- a/src/applications/project/controller/PhabricatorProjectMoveController.php
+++ b/src/applications/project/controller/PhabricatorProjectMoveController.php
@@ -1,255 +1,136 @@
<?php
final class PhabricatorProjectMoveController
extends PhabricatorProjectController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$request->validateCSRF();
$column_phid = $request->getStr('columnPHID');
$object_phid = $request->getStr('objectPHID');
$after_phid = $request->getStr('afterPHID');
$before_phid = $request->getStr('beforePHID');
$order = $request->getStr('order', PhabricatorProjectColumn::DEFAULT_ORDER);
$edit_header = null;
$raw_header = $request->getStr('header');
if (strlen($raw_header)) {
$edit_header = phutil_json_decode($raw_header);
} else {
$edit_header = array();
}
$project = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
))
->withIDs(array($id))
->executeOne();
if (!$project) {
return new Aphront404Response();
}
$board_phid = $project->getPHID();
$object = id(new ManiphestTaskQuery())
->setViewer($viewer)
->withPHIDs(array($object_phid))
->needProjectPHIDs(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$object) {
return new Aphront404Response();
}
$columns = id(new PhabricatorProjectColumnQuery())
->setViewer($viewer)
->withProjectPHIDs(array($project->getPHID()))
->execute();
$columns = mpull($columns, null, 'getPHID');
$column = idx($columns, $column_phid);
if (!$column) {
// User is trying to drop this object into a nonexistent column, just kick
// them out.
return new Aphront404Response();
}
$engine = id(new PhabricatorBoardLayoutEngine())
->setViewer($viewer)
->setBoardPHIDs(array($board_phid))
->setObjectPHIDs(array($object_phid))
->executeLayout();
- $columns = $engine->getObjectColumns($board_phid, $object_phid);
- $old_column_phids = mpull($columns, 'getPHID');
-
- $xactions = array();
-
$order_params = array();
- if ($order == PhabricatorProjectColumn::ORDER_NATURAL) {
- if ($after_phid) {
- $order_params['afterPHID'] = $after_phid;
- } else if ($before_phid) {
- $order_params['beforePHID'] = $before_phid;
- }
+ if ($after_phid) {
+ $order_params['afterPHID'] = $after_phid;
+ } else if ($before_phid) {
+ $order_params['beforePHID'] = $before_phid;
}
+ $xactions = array();
$xactions[] = id(new ManiphestTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_COLUMNS)
->setNewValue(
array(
array(
'columnPHID' => $column->getPHID(),
) + $order_params,
));
- if ($order == PhabricatorProjectColumn::ORDER_PRIORITY) {
- $header_priority = idx(
- $edit_header,
- PhabricatorProjectColumn::ORDER_PRIORITY);
- $priority_xactions = $this->getPriorityTransactions(
- $object,
- $after_phid,
- $before_phid,
- $header_priority);
- foreach ($priority_xactions as $xaction) {
- $xactions[] = $xaction;
- }
+ $header_xactions = $this->newHeaderTransactions(
+ $object,
+ $order,
+ $edit_header);
+ foreach ($header_xactions as $header_xaction) {
+ $xactions[] = $header_xaction;
}
$editor = id(new ManiphestTransactionEditor())
->setActor($viewer)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request);
$editor->applyTransactions($object, $xactions);
return $this->newCardResponse($board_phid, $object_phid);
}
- private function getPriorityTransactions(
+ private function newHeaderTransactions(
ManiphestTask $task,
- $after_phid,
- $before_phid,
- $header_priority) {
+ $order,
+ array $header) {
$xactions = array();
- $must_move = false;
- if ($header_priority !== null) {
- if ($task->getPriority() !== $header_priority) {
- $task = id(clone $task)
- ->setPriority($header_priority);
+ switch ($order) {
+ case PhabricatorProjectColumn::ORDER_PRIORITY:
+ $new_priority = idx($header, $order);
- $keyword_map = ManiphestTaskPriority::getTaskPriorityKeywordsMap();
- $keyword = head(idx($keyword_map, $header_priority));
+ if ($task->getPriority() !== $new_priority) {
+ $keyword_map = ManiphestTaskPriority::getTaskPriorityKeywordsMap();
+ $keyword = head(idx($keyword_map, $new_priority));
- $xactions[] = id(new ManiphestTransaction())
- ->setTransactionType(
- ManiphestTaskPriorityTransaction::TRANSACTIONTYPE)
- ->setNewValue($keyword);
-
- $must_move = true;
- }
- }
-
- list($after_task, $before_task) = $this->loadPriorityTasks(
- $after_phid,
- $before_phid);
-
- if ($after_task && !$task->isLowerPriorityThan($after_task)) {
- $must_move = true;
- }
-
- if ($before_task && !$task->isHigherPriorityThan($before_task)) {
- $must_move = true;
- }
-
- // The move doesn't require a subpriority change to be valid, so don't
- // change the subpriority since we are not being forced to.
- if (!$must_move) {
- return $xactions;
- }
-
- $try = array(
- array($after_task, true),
- array($before_task, false),
- );
-
- $pri = null;
- $sub = null;
- foreach ($try as $spec) {
- list($nearby_task, $is_after) = $spec;
-
- if (!$nearby_task) {
- continue;
- }
-
- list($pri, $sub) = ManiphestTransactionEditor::getAdjacentSubpriority(
- $nearby_task,
- $is_after);
-
- // If we drag under a "Low" header between a "Normal" task and a "Low"
- // task, we don't want to accept a subpriority assignment which changes
- // our priority to "Normal". Only accept a subpriority that keeps us in
- // the right primary priority.
- if ($header_priority !== null) {
- if ($pri !== $header_priority) {
- continue;
+ $xactions[] = id(new ManiphestTransaction())
+ ->setTransactionType(
+ ManiphestTaskPriorityTransaction::TRANSACTIONTYPE)
+ ->setNewValue($keyword);
}
- }
-
- // If we find a priority on the first try, don't keep going.
- break;
- }
-
- if ($pri !== null) {
- if ($header_priority === null) {
- $keyword_map = ManiphestTaskPriority::getTaskPriorityKeywordsMap();
- $keyword = head(idx($keyword_map, $pri));
-
- $xactions[] = id(new ManiphestTransaction())
- ->setTransactionType(
- ManiphestTaskPriorityTransaction::TRANSACTIONTYPE)
- ->setNewValue($keyword);
- }
-
- $xactions[] = id(new ManiphestTransaction())
- ->setTransactionType(
- ManiphestTaskSubpriorityTransaction::TRANSACTIONTYPE)
- ->setNewValue($sub);
+ break;
}
return $xactions;
}
- private function loadPriorityTasks($after_phid, $before_phid) {
- $viewer = $this->getViewer();
-
- $task_phids = array();
-
- if ($after_phid) {
- $task_phids[] = $after_phid;
- }
- if ($before_phid) {
- $task_phids[] = $before_phid;
- }
-
- if (!$task_phids) {
- return array(null, null);
- }
-
- $tasks = id(new ManiphestTaskQuery())
- ->setViewer($viewer)
- ->withPHIDs($task_phids)
- ->execute();
- $tasks = mpull($tasks, null, 'getPHID');
-
- if ($after_phid) {
- $after_task = idx($tasks, $after_phid);
- } else {
- $after_task = null;
- }
-
- if ($before_phid) {
- $before_task = idx($tasks, $before_phid);
- } else {
- $before_task = null;
- }
-
- return array($after_task, $before_task);
- }
-
}
diff --git a/webroot/rsrc/js/application/projects/WorkboardBoard.js b/webroot/rsrc/js/application/projects/WorkboardBoard.js
index b0bcfe97c6..c49e4c859f 100644
--- a/webroot/rsrc/js/application/projects/WorkboardBoard.js
+++ b/webroot/rsrc/js/application/projects/WorkboardBoard.js
@@ -1,354 +1,384 @@
/**
* @provides javelin-workboard-board
* @requires javelin-install
* javelin-dom
* javelin-util
* javelin-stratcom
* javelin-workflow
* phabricator-draggable-list
* javelin-workboard-column
* javelin-workboard-header-template
* @javelin
*/
JX.install('WorkboardBoard', {
construct: function(controller, phid, root) {
this._controller = controller;
this._phid = phid;
this._root = root;
this._templates = {};
this._orderMaps = {};
this._propertiesMap = {};
this._headers = {};
this._buildColumns();
},
properties: {
order: null,
pointsEnabled: false
},
members: {
_controller: null,
_phid: null,
_root: null,
_columns: null,
_templates: null,
_orderMaps: null,
_propertiesMap: null,
_headers: null,
getRoot: function() {
return this._root;
},
getColumns: function() {
return this._columns;
},
getColumn: function(k) {
return this._columns[k];
},
getPHID: function() {
return this._phid;
},
setCardTemplate: function(phid, template) {
this._templates[phid] = template;
return this;
},
getHeaderTemplate: function(header_key) {
if (!this._headers[header_key]) {
this._headers[header_key] = new JX.WorkboardHeaderTemplate(header_key);
}
return this._headers[header_key];
},
getHeaderTemplatesForOrder: function(order) {
var templates = [];
for (var k in this._headers) {
var header = this._headers[k];
if (header.getOrder() !== order) {
continue;
}
templates.push(header);
}
templates.sort(JX.bind(this, this._sortHeaderTemplates));
return templates;
},
_sortHeaderTemplates: function(u, v) {
return this.compareVectors(u.getVector(), v.getVector());
},
setObjectProperties: function(phid, properties) {
this._propertiesMap[phid] = properties;
return this;
},
getObjectProperties: function(phid) {
return this._propertiesMap[phid];
},
getCardTemplate: function(phid) {
return this._templates[phid];
},
getController: function() {
return this._controller;
},
setOrderMap: function(phid, map) {
this._orderMaps[phid] = map;
return this;
},
getOrderVector: function(phid, key) {
return this._orderMaps[phid][key];
},
compareVectors: function(u_vec, v_vec) {
for (var ii = 0; ii < u_vec.length; ii++) {
if (u_vec[ii] > v_vec[ii]) {
return 1;
}
if (u_vec[ii] < v_vec[ii]) {
return -1;
}
}
return 0;
},
start: function() {
this._setupDragHandlers();
for (var k in this._columns) {
this._columns[k].redraw();
}
},
_buildColumns: function() {
var nodes = JX.DOM.scry(this.getRoot(), 'ul', 'project-column');
this._columns = {};
for (var ii = 0; ii < nodes.length; ii++) {
var node = nodes[ii];
var data = JX.Stratcom.getData(node);
var phid = data.columnPHID;
this._columns[phid] = new JX.WorkboardColumn(this, phid, node);
}
},
_setupDragHandlers: function() {
var columns = this.getColumns();
var lists = [];
for (var k in columns) {
var column = columns[k];
var list = new JX.DraggableList('project-card', column.getRoot())
.setOuterContainer(this.getRoot())
.setFindItemsHandler(JX.bind(column, column.getDropTargetNodes))
.setCanDragX(true)
.setHasInfiniteHeight(true)
.setIsDropTargetHandler(JX.bind(column, column.setIsDropTarget));
var default_handler = list.getGhostHandler();
list.setGhostHandler(
JX.bind(column, column.handleDragGhost, default_handler));
if (this.getOrder() !== 'natural') {
list.setCompareHandler(JX.bind(column, column.compareHandler));
}
list.listen('didDrop', JX.bind(this, this._onmovecard, list));
lists.push(list);
}
for (var ii = 0; ii < lists.length; ii++) {
lists[ii].setGroup(lists);
}
},
_findCardsInColumn: function(column_node) {
return JX.DOM.scry(column_node, 'li', 'project-card');
},
_onmovecard: function(list, item, after_node, src_list) {
list.lock();
JX.DOM.alterClass(item, 'drag-sending', true);
var src_phid = JX.Stratcom.getData(src_list.getRootNode()).columnPHID;
var dst_phid = JX.Stratcom.getData(list.getRootNode()).columnPHID;
var item_phid = JX.Stratcom.getData(item).objectPHID;
var data = {
objectPHID: item_phid,
columnPHID: dst_phid,
order: this.getOrder()
};
+ // We're going to send an "afterPHID" and a "beforePHID" if the card
+ // was dropped immediately adjacent to another card. If a card was
+ // dropped before or after a header, we don't send a PHID for the card
+ // on the other side of the header.
+
+ // If the view has headers, we always send the header the card was
+ // dropped under.
+
var after_data;
var after_card = after_node;
while (after_card) {
after_data = JX.Stratcom.getData(after_card);
if (after_data.objectPHID) {
break;
}
+ if (after_data.headerKey) {
+ break;
+ }
after_card = after_card.previousSibling;
}
if (after_data) {
- data.afterPHID = after_data.objectPHID;
+ if (after_data.objectPHID) {
+ data.afterPHID = after_data.objectPHID;
+ }
}
var before_data;
var before_card = item.nextSibling;
while (before_card) {
before_data = JX.Stratcom.getData(before_card);
if (before_data.objectPHID) {
break;
}
+ if (before_data.headerKey) {
+ break;
+ }
before_card = before_card.nextSibling;
}
if (before_data) {
- data.beforePHID = before_data.objectPHID;
+ if (before_data.objectPHID) {
+ data.beforePHID = before_data.objectPHID;
+ }
}
- var header_key = JX.Stratcom.getData(after_node).headerKey;
- if (header_key) {
- var properties = this.getHeaderTemplate(header_key)
- .getEditProperties();
- data.header = JX.JSON.stringify(properties);
+ var header_data;
+ var header_node = after_node;
+ while (header_node) {
+ header_data = JX.Stratcom.getData(header_node);
+ if (header_data.headerKey) {
+ break;
+ }
+ header_node = header_node.previousSibling;
+ }
+
+ if (header_data) {
+ var header_key = header_data.headerKey;
+ if (header_key) {
+ var properties = this.getHeaderTemplate(header_key)
+ .getEditProperties();
+ data.header = JX.JSON.stringify(properties);
+ }
}
var visible_phids = [];
var column = this.getColumn(dst_phid);
for (var object_phid in column.getCards()) {
visible_phids.push(object_phid);
}
data.visiblePHIDs = visible_phids.join(',');
var onupdate = JX.bind(
this,
this._oncardupdate,
list,
src_phid,
dst_phid,
data.afterPHID);
new JX.Workflow(this.getController().getMoveURI(), data)
.setHandler(onupdate)
.start();
},
_oncardupdate: function(list, src_phid, dst_phid, after_phid, response) {
var src_column = this.getColumn(src_phid);
var dst_column = this.getColumn(dst_phid);
var card = src_column.removeCard(response.objectPHID);
dst_column.addCard(card, after_phid);
src_column.markForRedraw();
dst_column.markForRedraw();
this.updateCard(response);
list.unlock();
},
updateCard: function(response, options) {
options = options || {};
options.dirtyColumns = options.dirtyColumns || {};
var columns = this.getColumns();
var phid = response.objectPHID;
if (!this._templates[phid]) {
for (var add_phid in response.columnMaps) {
var target_column = this.getColumn(add_phid);
if (!target_column) {
// If the column isn't visible, don't try to add a card to it.
continue;
}
target_column.newCard(phid);
}
}
this.setCardTemplate(phid, response.cardHTML);
var order_maps = response.orderMaps;
for (var order_phid in order_maps) {
this.setOrderMap(order_phid, order_maps[order_phid]);
}
var column_maps = response.columnMaps;
var natural_column;
for (var natural_phid in column_maps) {
natural_column = this.getColumn(natural_phid);
if (!natural_column) {
// Our view of the board may be out of date, so we might get back
// information about columns that aren't visible. Just ignore the
// position information for any columns we aren't displaying on the
// client.
continue;
}
natural_column.setNaturalOrder(column_maps[natural_phid]);
}
var property_maps = response.propertyMaps;
for (var property_phid in property_maps) {
this.setObjectProperties(property_phid, property_maps[property_phid]);
}
for (var column_phid in columns) {
var column = columns[column_phid];
var cards = column.getCards();
for (var object_phid in cards) {
if (object_phid !== phid) {
continue;
}
var card = cards[object_phid];
card.redraw();
column.markForRedraw();
}
}
this._redrawColumns();
},
_redrawColumns: function() {
var columns = this.getColumns();
for (var k in columns) {
if (columns[k].isMarkedForRedraw()) {
columns[k].redraw();
}
}
}
}
});
diff --git a/webroot/rsrc/js/application/projects/WorkboardColumn.js b/webroot/rsrc/js/application/projects/WorkboardColumn.js
index 262e80c2da..e2ec18ae75 100644
--- a/webroot/rsrc/js/application/projects/WorkboardColumn.js
+++ b/webroot/rsrc/js/application/projects/WorkboardColumn.js
@@ -1,414 +1,454 @@
/**
* @provides javelin-workboard-column
* @requires javelin-install
* javelin-workboard-card
* javelin-workboard-header
* @javelin
*/
JX.install('WorkboardColumn', {
construct: function(board, phid, root) {
this._board = board;
this._phid = phid;
this._root = root;
this._panel = JX.DOM.findAbove(root, 'div', 'workpanel');
this._pointsNode = JX.DOM.find(this._panel, 'span', 'column-points');
this._pointsContentNode = JX.DOM.find(
this._panel,
'span',
'column-points-content');
this._cards = {};
this._headers = {};
this._objects = [];
this._naturalOrder = [];
},
members: {
_phid: null,
_root: null,
_board: null,
_cards: null,
_headers: null,
_naturalOrder: null,
+ _orderVectors: null,
_panel: null,
_pointsNode: null,
_pointsContentNode: null,
_dirty: true,
_objects: null,
getPHID: function() {
return this._phid;
},
getRoot: function() {
return this._root;
},
getCards: function() {
return this._cards;
},
_getObjects: function() {
return this._objects;
},
getCard: function(phid) {
return this._cards[phid];
},
getBoard: function() {
return this._board;
},
setNaturalOrder: function(order) {
this._naturalOrder = order;
+ this._orderVectors = null;
return this;
},
getPointsNode: function() {
return this._pointsNode;
},
getPointsContentNode: function() {
return this._pointsContentNode;
},
getWorkpanelNode: function() {
return this._panel;
},
newCard: function(phid) {
var card = new JX.WorkboardCard(this, phid);
this._cards[phid] = card;
this._naturalOrder.push(phid);
+ this._orderVectors = null;
return card;
},
removeCard: function(phid) {
var card = this._cards[phid];
delete this._cards[phid];
for (var ii = 0; ii < this._naturalOrder.length; ii++) {
if (this._naturalOrder[ii] == phid) {
this._naturalOrder.splice(ii, 1);
+ this._orderVectors = null;
break;
}
}
return card;
},
addCard: function(card, after) {
var phid = card.getPHID();
card.setColumn(this);
this._cards[phid] = card;
var index = 0;
if (after) {
for (var ii = 0; ii < this._naturalOrder.length; ii++) {
if (this._naturalOrder[ii] == after) {
index = ii + 1;
break;
}
}
}
if (index > this._naturalOrder.length) {
this._naturalOrder.push(phid);
} else {
this._naturalOrder.splice(index, 0, phid);
}
+ this._orderVectors = null;
+
return this;
},
getDropTargetNodes: function() {
var objects = this._getObjects();
var nodes = [];
for (var ii = 0; ii < objects.length; ii++) {
var object = objects[ii];
nodes.push(object.getNode());
}
return nodes;
},
getCardPHIDs: function() {
return JX.keys(this.getCards());
},
getPointLimit: function() {
return JX.Stratcom.getData(this.getRoot()).pointLimit;
},
markForRedraw: function() {
this._dirty = true;
},
isMarkedForRedraw: function() {
return this._dirty;
},
getHeader: function(key) {
if (!this._headers[key]) {
this._headers[key] = new JX.WorkboardHeader(this, key);
}
return this._headers[key];
},
handleDragGhost: function(default_handler, ghost, node) {
// If the column has headers, don't let the user drag a card above
// the topmost header: for example, you can't change a task to have
// a priority higher than the highest possible priority.
if (this._hasColumnHeaders()) {
if (!node) {
return false;
}
}
return default_handler(ghost, node);
},
_hasColumnHeaders: function() {
var board = this.getBoard();
var order = board.getOrder();
switch (order) {
case 'natural':
return false;
}
return true;
},
_getCardHeaderKey: function(card, order) {
switch (order) {
case 'priority':
return 'priority(' + card.getPriority() + ')';
default:
return null;
}
},
redraw: function() {
var board = this.getBoard();
var order = board.getOrder();
- var list;
- if (order == 'natural') {
- list = this._getCardsSortedNaturally();
- } else {
- list = this._getCardsSortedByKey(order);
- }
+ var list = this._getCardsSortedByKey(order);
var ii;
var objects = [];
var has_headers = this._hasColumnHeaders();
var header_keys = [];
var seen_headers = {};
if (has_headers) {
var header_templates = board.getHeaderTemplatesForOrder(order);
for (var k in header_templates) {
header_keys.push(header_templates[k].getHeaderKey());
}
header_keys.reverse();
}
for (ii = 0; ii < list.length; ii++) {
var card = list[ii];
// If a column has a "High" priority card and a "Low" priority card,
// we need to add the "Normal" header in between them. This allows
// you to change priority to "Normal" even if there are no "Normal"
// cards in a column.
if (has_headers) {
var header_key = this._getCardHeaderKey(card, order);
if (!seen_headers[header_key]) {
while (header_keys.length) {
var next = header_keys.pop();
var header = this.getHeader(next);
objects.push(header);
seen_headers[header_key] = true;
if (next === header_key) {
break;
}
}
}
}
objects.push(card);
}
this._objects = objects;
var content = [];
for (ii = 0; ii < this._objects.length; ii++) {
var object = this._objects[ii];
var node = object.getNode();
content.push(node);
}
JX.DOM.setContent(this.getRoot(), content);
this._redrawFrame();
this._dirty = false;
},
compareHandler: function(src_list, src_node, dst_list, dst_node) {
var board = this.getBoard();
var order = board.getOrder();
var u_vec = this._getNodeOrderVector(src_node, order);
var v_vec = this._getNodeOrderVector(dst_node, order);
return board.compareVectors(u_vec, v_vec);
},
_getNodeOrderVector: function(node, order) {
var board = this.getBoard();
var data = JX.Stratcom.getData(node);
if (data.objectPHID) {
- return board.getOrderVector(data.objectPHID, order);
+ return this._getOrderVector(data.objectPHID, order);
}
return board.getHeaderTemplate(data.headerKey).getVector();
},
setIsDropTarget: function(is_target) {
var node = this.getWorkpanelNode();
JX.DOM.alterClass(node, 'workboard-column-drop-target', is_target);
},
- _getCardsSortedNaturally: function() {
- var list = [];
-
- for (var ii = 0; ii < this._naturalOrder.length; ii++) {
- var phid = this._naturalOrder[ii];
- list.push(this.getCard(phid));
- }
-
- return list;
- },
-
_getCardsSortedByKey: function(order) {
var cards = this.getCards();
var list = [];
for (var k in cards) {
list.push(cards[k]);
}
list.sort(JX.bind(this, this._sortCards, order));
return list;
},
_sortCards: function(order, u, v) {
var board = this.getBoard();
- var u_vec = board.getOrderVector(u.getPHID(), order);
- var v_vec = board.getOrderVector(v.getPHID(), order);
+ var u_vec = this._getOrderVector(u.getPHID(), order);
+ var v_vec = this._getOrderVector(v.getPHID(), order);
return board.compareVectors(u_vec, v_vec);
},
+ _getOrderVector: function(phid, order) {
+ if (!this._orderVectors) {
+ this._orderVectors = {};
+ }
+
+ if (!this._orderVectors[order]) {
+ var board = this.getBoard();
+ var cards = this.getCards();
+ var vectors = {};
+
+ for (var k in cards) {
+ var card_phid = cards[k].getPHID();
+ var vector = board.getOrderVector(card_phid, order);
+ vectors[card_phid] = [].concat(vector);
+
+ // Push a "card" type, so cards always sort after headers; headers
+ // have a "0" in this position.
+ vectors[card_phid].push(1);
+ }
+
+ for (var ii = 0; ii < this._naturalOrder.length; ii++) {
+ var natural_phid = this._naturalOrder[ii];
+ if (vectors[natural_phid]) {
+ vectors[natural_phid].push(ii);
+ }
+ }
+
+ this._orderVectors[order] = vectors;
+ }
+
+ if (!this._orderVectors[order][phid]) {
+ // In this case, we're comparing a card being dragged in from another
+ // column to the cards already in this column. We're just going to
+ // build a temporary vector for it.
+ var incoming_vector = this.getBoard().getOrderVector(phid, order);
+ incoming_vector = [].concat(incoming_vector);
+
+ // Add a "card" type to sort this after headers.
+ incoming_vector.push(1);
+
+ // Add a "0" for the natural ordering to put this on top. A new card
+ // has no natural ordering on a column it isn't part of yet.
+ incoming_vector.push(0);
+
+ return incoming_vector;
+ }
+
+ return this._orderVectors[order][phid];
+ },
+
_redrawFrame: function() {
var cards = this.getCards();
var board = this.getBoard();
var points = {};
var count = 0;
var decimal_places = 0;
for (var phid in cards) {
var card = cards[phid];
var card_points;
if (board.getPointsEnabled()) {
card_points = card.getPoints();
} else {
card_points = 1;
}
if (card_points !== null) {
var status = card.getStatus();
if (!points[status]) {
points[status] = 0;
}
points[status] += card_points;
// Count the number of decimal places in the point value with the
// most decimal digits. We'll use the same precision when rendering
// the point sum. This avoids rounding errors and makes the display
// a little more consistent.
var parts = card_points.toString().split('.');
if (parts[1]) {
decimal_places = Math.max(decimal_places, parts[1].length);
}
}
count++;
}
var total_points = 0;
for (var k in points) {
total_points += points[k];
}
total_points = total_points.toFixed(decimal_places);
var limit = this.getPointLimit();
var display_value;
if (limit !== null && limit !== 0) {
display_value = total_points + ' / ' + limit;
} else {
display_value = total_points;
}
if (board.getPointsEnabled()) {
display_value = count + ' | ' + display_value;
}
var over_limit = ((limit !== null) && (total_points > limit));
var content_node = this.getPointsContentNode();
var points_node = this.getPointsNode();
JX.DOM.setContent(content_node, display_value);
var is_empty = !this.getCardPHIDs().length;
var panel = JX.DOM.findAbove(this.getRoot(), 'div', 'workpanel');
JX.DOM.alterClass(panel, 'project-panel-empty', is_empty);
JX.DOM.alterClass(panel, 'project-panel-over-limit', over_limit);
var color_map = {
'phui-tag-disabled': (total_points === 0),
'phui-tag-blue': (total_points > 0 && !over_limit),
'phui-tag-red': (over_limit)
};
for (var c in color_map) {
JX.DOM.alterClass(points_node, c, !!color_map[c]);
}
JX.DOM.show(points_node);
}
}
});

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jan 19, 16:10 (2 w, 6 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1126344
Default Alt Text
(180 KB)

Event Timeline