diff --git a/CHANGELOG b/CHANGELOG index 09b5bdd0a9..171beb5130 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,27 +1,32 @@ This is not a complete list of changes, just of API or workflow changes that may break existing installs. Newer changes are listed at the top. If you pull new changes and things stop working, check here first! +June 29 2011 - Maniphest project indexes + Old Maniphest tasks will not appear in project filter views until you run + "scripts/search/reindex_maniphest.php" to build indexes. New tasks will have + their indexes built automatically. + May 31 2011 - Javelin submodule moved The externals/javelin submodule location has moved. If you have an older checkout of Phabricator, you may need to edit .git/config to point at facebook/javelin.git instead of epriestley/javelin.git, then remove the externals/javelin directory, and then run "git submodule update --init". May 11 2011 - New session code There's some new code which allows you to establish multiple web sessions. When you update to it, all users will be logged out. This is expected, just log in again and everything should work properly. May 10 2011 - PhabricatorMailImplementationAdapter The signatures of setFrom() and addReplyTo() have changed, and they now accept a second "$name = ''" parameter. This represents a human-readable name component of the address. May 9 2011 - git submodule As of commit 2a39fd0, you must run "git submodule update --init" in your clone. This was added to the install documentation in commit 09b498c, see that commit for details. May 9 2011 - Changelog Created CHANGELOG diff --git a/resources/sql/patches/051.projectfilter.sql b/resources/sql/patches/051.projectfilter.sql new file mode 100644 index 0000000000..42554d01a7 --- /dev/null +++ b/resources/sql/patches/051.projectfilter.sql @@ -0,0 +1,6 @@ +CREATE TABLE phabricator_maniphest.maniphest_taskproject ( + taskPHID varchar(64) BINARY NOT NULL, + projectPHID varchar(64) BINARY NOT NULL, + PRIMARY KEY (projectPHID, taskPHID), + UNIQUE KEY (taskPHID, projectPHID) +); diff --git a/scripts/search/reindex_maniphest.php b/scripts/search/reindex_maniphest.php new file mode 100755 index 0000000000..62550d56d2 --- /dev/null +++ b/scripts/search/reindex_maniphest.php @@ -0,0 +1,32 @@ +#!/usr/bin/env php +loadAll(); +echo "Updating relationships for ".count($tasks)." tasks"; +foreach ($tasks as $task) { + ManiphestTaskProject::updateTaskProjects($task); + echo '.'; +} +echo "\nDone.\n"; + diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 271fcc39f5..d050187ca1 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1,1015 +1,1018 @@ array( 'Aphront304Response' => 'aphront/response/304', 'Aphront400Response' => 'aphront/response/400', 'Aphront404Response' => 'aphront/response/404', 'AphrontAjaxResponse' => 'aphront/response/ajax', 'AphrontApplicationConfiguration' => 'aphront/applicationconfiguration', 'AphrontAttachedFileView' => 'view/control/attachedfile', 'AphrontController' => 'aphront/controller', 'AphrontCrumbsView' => 'view/layout/crumbs', 'AphrontDatabaseConnection' => 'storage/connection/base', 'AphrontDefaultApplicationConfiguration' => 'aphront/default/configuration', 'AphrontDefaultApplicationController' => 'aphront/default/controller', 'AphrontDialogResponse' => 'aphront/response/dialog', 'AphrontDialogView' => 'view/dialog', 'AphrontErrorView' => 'view/form/error', 'AphrontException' => 'aphront/exception/base', 'AphrontFilePreviewView' => 'view/layout/filepreview', 'AphrontFileResponse' => 'aphront/response/file', 'AphrontFormCheckboxControl' => 'view/form/control/checkbox', 'AphrontFormControl' => 'view/form/control/base', 'AphrontFormDividerControl' => 'view/form/control/divider', 'AphrontFormDragAndDropUploadControl' => 'view/form/control/draganddropupload', 'AphrontFormFileControl' => 'view/form/control/file', 'AphrontFormLayoutView' => 'view/form/layout', 'AphrontFormMarkupControl' => 'view/form/control/markup', 'AphrontFormPasswordControl' => 'view/form/control/password', 'AphrontFormRecaptchaControl' => 'view/form/control/recaptcha', 'AphrontFormSelectControl' => 'view/form/control/select', 'AphrontFormStaticControl' => 'view/form/control/static', 'AphrontFormSubmitControl' => 'view/form/control/submit', 'AphrontFormTextAreaControl' => 'view/form/control/textarea', 'AphrontFormTextControl' => 'view/form/control/text', 'AphrontFormToggleButtonsControl' => 'view/form/control/togglebuttons', 'AphrontFormTokenizerControl' => 'view/form/control/tokenizer', 'AphrontFormView' => 'view/form/base', 'AphrontHeadsupActionListView' => 'view/layout/headsup/actionlist', 'AphrontHeadsupActionView' => 'view/layout/headsup/action', 'AphrontIsolatedDatabaseConnection' => 'storage/connection/isolated', 'AphrontIsolatedDatabaseConnectionTestCase' => 'storage/connection/isolated/__tests__', 'AphrontKeyboardShortcutsAvailableView' => 'view/widget/keyboardshortcuts', 'AphrontListFilterView' => 'view/layout/listfilter', 'AphrontMySQLDatabaseConnection' => 'storage/connection/mysql', 'AphrontNullView' => 'view/null', 'AphrontPageView' => 'view/page/base', 'AphrontPagerView' => 'view/control/pager', 'AphrontPanelView' => 'view/layout/panel', 'AphrontQueryAccessDeniedException' => 'storage/exception/accessdenied', 'AphrontQueryConnectionException' => 'storage/exception/connection', 'AphrontQueryConnectionLostException' => 'storage/exception/connectionlost', 'AphrontQueryCountException' => 'storage/exception/count', 'AphrontQueryDuplicateKeyException' => 'storage/exception/duplicatekey', 'AphrontQueryException' => 'storage/exception/base', 'AphrontQueryObjectMissingException' => 'storage/exception/objectmissing', 'AphrontQueryParameterException' => 'storage/exception/parameter', 'AphrontQueryRecoverableException' => 'storage/exception/recoverable', 'AphrontRedirectException' => 'aphront/exception/redirect', 'AphrontRedirectResponse' => 'aphront/response/redirect', 'AphrontReloadResponse' => 'aphront/response/reload', 'AphrontRequest' => 'aphront/request', 'AphrontRequestFailureView' => 'view/page/failure', 'AphrontResponse' => 'aphront/response/base', 'AphrontSideNavView' => 'view/layout/sidenav', 'AphrontTableView' => 'view/control/table', 'AphrontTokenizerTemplateView' => 'view/control/tokenizer', 'AphrontTypeaheadTemplateView' => 'view/control/typeahead', 'AphrontURIMapper' => 'aphront/mapper', 'AphrontView' => 'view/base', 'AphrontWebpageResponse' => 'aphront/response/webpage', 'CelerityAPI' => 'infrastructure/celerity/api', 'CelerityResourceController' => 'infrastructure/celerity/controller', 'CelerityResourceMap' => 'infrastructure/celerity/map', 'CelerityStaticResourceResponse' => 'infrastructure/celerity/response', 'ConduitAPIMethod' => 'applications/conduit/method/base', 'ConduitAPIRequest' => 'applications/conduit/protocol/request', 'ConduitAPI_conduit_connect_Method' => 'applications/conduit/method/conduit/connect', 'ConduitAPI_conduit_getcertificate_Method' => 'applications/conduit/method/conduit/getcertificate', 'ConduitAPI_conduit_ping_Method' => 'applications/conduit/method/conduit/ping', 'ConduitAPI_daemon_launched_Method' => 'applications/conduit/method/daemon/launched', 'ConduitAPI_daemon_log_Method' => 'applications/conduit/method/daemon/log', 'ConduitAPI_differential_creatediff_Method' => 'applications/conduit/method/differential/creatediff', 'ConduitAPI_differential_createrevision_Method' => 'applications/conduit/method/differential/createrevision', 'ConduitAPI_differential_find_Method' => 'applications/conduit/method/differential/find', 'ConduitAPI_differential_getalldiffs_Method' => 'applications/conduit/method/differential/getalldiffs', 'ConduitAPI_differential_getcommitmessage_Method' => 'applications/conduit/method/differential/getcommitmessage', 'ConduitAPI_differential_getcommitpaths_Method' => 'applications/conduit/method/differential/getcommitpaths', 'ConduitAPI_differential_getdiff_Method' => 'applications/conduit/method/differential/getdiff', 'ConduitAPI_differential_getrevision_Method' => 'applications/conduit/method/differential/getrevision', 'ConduitAPI_differential_getrevisionfeedback_Method' => 'applications/conduit/method/differential/getrevisionfeedback', 'ConduitAPI_differential_markcommitted_Method' => 'applications/conduit/method/differential/markcommitted', 'ConduitAPI_differential_parsecommitmessage_Method' => 'applications/conduit/method/differential/parsecommitmessage', 'ConduitAPI_differential_setdiffproperty_Method' => 'applications/conduit/method/differential/setdiffproperty', 'ConduitAPI_differential_updaterevision_Method' => 'applications/conduit/method/differential/updaterevision', 'ConduitAPI_differential_updatetaskrevisionassoc_Method' => 'applications/conduit/method/differential/updatetaskrevisionassoc', 'ConduitAPI_differential_updateunitresults_Method' => 'applications/conduit/method/differential/updateunitresults', 'ConduitAPI_diffusion_getcommits_Method' => 'applications/conduit/method/diffusion/getcommits', 'ConduitAPI_diffusion_getrecentcommitsbypath_Method' => 'applications/conduit/method/diffusion/getrecentcommitsbypath', 'ConduitAPI_file_download_Method' => 'applications/conduit/method/file/download', 'ConduitAPI_file_upload_Method' => 'applications/conduit/method/file/upload', 'ConduitAPI_maniphest_info_Method' => 'applications/conduit/method/maniphest/info', 'ConduitAPI_paste_info_Method' => 'applications/conduit/method/paste/info', 'ConduitAPI_path_getowners_Method' => 'applications/conduit/method/path/getowners', 'ConduitAPI_user_find_Method' => 'applications/conduit/method/user/find', 'ConduitAPI_user_whoami_Method' => 'applications/conduit/method/user/whoami', 'ConduitException' => 'applications/conduit/protocol/exception', 'DarkConsole' => 'aphront/console/api', 'DarkConsoleConfigPlugin' => 'aphront/console/plugin/config', 'DarkConsoleController' => 'aphront/console/controller', 'DarkConsoleCore' => 'aphront/console/core', 'DarkConsoleErrorLogPlugin' => 'aphront/console/plugin/errorlog', 'DarkConsoleErrorLogPluginAPI' => 'aphront/console/plugin/errorlog/api', 'DarkConsolePlugin' => 'aphront/console/plugin/base', 'DarkConsoleRequestPlugin' => 'aphront/console/plugin/request', 'DarkConsoleServicesPlugin' => 'aphront/console/plugin/services', 'DarkConsoleXHProfPlugin' => 'aphront/console/plugin/xhprof', 'DarkConsoleXHProfPluginAPI' => 'aphront/console/plugin/xhprof/api', 'DatabaseConfigurationProvider' => 'applications/base/storage/configuration', 'DifferentialAction' => 'applications/differential/constants/action', 'DifferentialAddCommentView' => 'applications/differential/view/addcomment', 'DifferentialCCWelcomeMail' => 'applications/differential/mail/ccwelcome', 'DifferentialChangeType' => 'applications/differential/constants/changetype', 'DifferentialChangeset' => 'applications/differential/storage/changeset', 'DifferentialChangesetDetailView' => 'applications/differential/view/changesetdetailview', 'DifferentialChangesetListView' => 'applications/differential/view/changesetlistview', 'DifferentialChangesetParser' => 'applications/differential/parser/changeset', 'DifferentialChangesetViewController' => 'applications/differential/controller/changesetview', 'DifferentialComment' => 'applications/differential/storage/comment', 'DifferentialCommentEditor' => 'applications/differential/editor/comment', 'DifferentialCommentMail' => 'applications/differential/mail/comment', 'DifferentialCommentPreviewController' => 'applications/differential/controller/commentpreview', 'DifferentialCommentSaveController' => 'applications/differential/controller/commentsave', 'DifferentialCommitMessage' => 'applications/differential/parser/commitmessage', 'DifferentialCommitMessageData' => 'applications/differential/data/commitmessage', 'DifferentialCommitMessageField' => 'applications/differential/data/commitmessage', 'DifferentialCommitMessageModifier' => 'applications/differential/data/commitmessage', 'DifferentialCommitMessageParserException' => 'applications/differential/parser/commitmessage/exception', 'DifferentialController' => 'applications/differential/controller/base', 'DifferentialDAO' => 'applications/differential/storage/base', 'DifferentialDiff' => 'applications/differential/storage/diff', 'DifferentialDiffContentMail' => 'applications/differential/mail/diffcontent', 'DifferentialDiffCreateController' => 'applications/differential/controller/diffcreate', 'DifferentialDiffProperty' => 'applications/differential/storage/diffproperty', 'DifferentialDiffTableOfContentsView' => 'applications/differential/view/difftableofcontents', 'DifferentialDiffViewController' => 'applications/differential/controller/diffview', 'DifferentialExceptionMail' => 'applications/differential/mail/exception', 'DifferentialHunk' => 'applications/differential/storage/hunk', 'DifferentialInlineComment' => 'applications/differential/storage/inlinecomment', 'DifferentialInlineCommentEditController' => 'applications/differential/controller/inlinecommentedit', 'DifferentialInlineCommentPreviewController' => 'applications/differential/controller/inlinecommentpreview', 'DifferentialInlineCommentView' => 'applications/differential/view/inlinecomment', 'DifferentialLintStatus' => 'applications/differential/constants/lintstatus', 'DifferentialMail' => 'applications/differential/mail/base', 'DifferentialMarkupEngineFactory' => 'applications/differential/parser/markup', 'DifferentialNewDiffMail' => 'applications/differential/mail/newdiff', 'DifferentialPrimaryPaneView' => 'applications/differential/view/primarypane', 'DifferentialReplyHandler' => 'applications/differential/replyhandler', 'DifferentialReviewRequestMail' => 'applications/differential/mail/reviewrequest', 'DifferentialRevision' => 'applications/differential/storage/revision', 'DifferentialRevisionCommentListView' => 'applications/differential/view/revisioncommentlist', 'DifferentialRevisionCommentView' => 'applications/differential/view/revisioncomment', 'DifferentialRevisionControlSystem' => 'applications/differential/constants/revisioncontrolsystem', 'DifferentialRevisionDetailRenderer' => 'applications/differential/controller/customrenderer', 'DifferentialRevisionDetailView' => 'applications/differential/view/revisiondetail', 'DifferentialRevisionEditController' => 'applications/differential/controller/revisionedit', 'DifferentialRevisionEditor' => 'applications/differential/editor/revision', 'DifferentialRevisionListController' => 'applications/differential/controller/revisionlist', 'DifferentialRevisionListData' => 'applications/differential/data/revisionlist', 'DifferentialRevisionStatus' => 'applications/differential/constants/revisionstatus', 'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/revisionupdatehistory', 'DifferentialRevisionViewController' => 'applications/differential/controller/revisionview', 'DifferentialSubscribeController' => 'applications/differential/controller/subscribe', 'DifferentialTasksAttacher' => 'applications/differential/tasks', 'DifferentialUnitStatus' => 'applications/differential/constants/unitstatus', 'DifferentialUnitTestResult' => 'applications/differential/constants/unittestresult', 'DifferentialViewTime' => 'applications/differential/storage/viewtime', 'DiffusionBranchInformation' => 'applications/diffusion/data/branch', 'DiffusionBranchQuery' => 'applications/diffusion/query/branch/base', 'DiffusionBranchTableView' => 'applications/diffusion/view/branchtable', 'DiffusionBrowseController' => 'applications/diffusion/controller/browse', 'DiffusionBrowseFileController' => 'applications/diffusion/controller/file', 'DiffusionBrowseQuery' => 'applications/diffusion/query/browse/base', 'DiffusionBrowseTableView' => 'applications/diffusion/view/browsetable', 'DiffusionChangeController' => 'applications/diffusion/controller/change', 'DiffusionCommitChangeTableView' => 'applications/diffusion/view/commitchangetable', 'DiffusionCommitController' => 'applications/diffusion/controller/commit', 'DiffusionController' => 'applications/diffusion/controller/base', 'DiffusionDiffController' => 'applications/diffusion/controller/diff', 'DiffusionDiffQuery' => 'applications/diffusion/query/diff/base', 'DiffusionFileContent' => 'applications/diffusion/data/filecontent', 'DiffusionFileContentQuery' => 'applications/diffusion/query/filecontent/base', 'DiffusionGitBranchQuery' => 'applications/diffusion/query/branch/git', 'DiffusionGitBrowseQuery' => 'applications/diffusion/query/browse/git', 'DiffusionGitDiffQuery' => 'applications/diffusion/query/diff/git', 'DiffusionGitFileContentQuery' => 'applications/diffusion/query/filecontent/git', 'DiffusionGitHistoryQuery' => 'applications/diffusion/query/history/git', 'DiffusionGitLastModifiedQuery' => 'applications/diffusion/query/lastmodified/git', 'DiffusionGitPathIDQuery' => 'applications/diffusion/query/pathid/base', 'DiffusionGitRequest' => 'applications/diffusion/request/git', 'DiffusionHistoryController' => 'applications/diffusion/controller/history', 'DiffusionHistoryQuery' => 'applications/diffusion/query/history/base', 'DiffusionHistoryTableView' => 'applications/diffusion/view/historytable', 'DiffusionHomeController' => 'applications/diffusion/controller/home', 'DiffusionLastModifiedController' => 'applications/diffusion/controller/lastmodified', 'DiffusionLastModifiedQuery' => 'applications/diffusion/query/lastmodified/base', 'DiffusionPathChange' => 'applications/diffusion/data/pathchange', 'DiffusionPathChangeQuery' => 'applications/diffusion/query/pathchange/base', 'DiffusionPathCompleteController' => 'applications/diffusion/controller/pathcomplete', 'DiffusionPathValidateController' => 'applications/diffusion/controller/pathvalidate', 'DiffusionRepositoryController' => 'applications/diffusion/controller/repository', 'DiffusionRepositoryPath' => 'applications/diffusion/data/repositorypath', 'DiffusionRequest' => 'applications/diffusion/request/base', 'DiffusionSvnBrowseQuery' => 'applications/diffusion/query/browse/svn', 'DiffusionSvnDiffQuery' => 'applications/diffusion/query/diff/svn', 'DiffusionSvnFileContentQuery' => 'applications/diffusion/query/filecontent/svn', 'DiffusionSvnHistoryQuery' => 'applications/diffusion/query/history/svn', 'DiffusionSvnLastModifiedQuery' => 'applications/diffusion/query/lastmodified/svn', 'DiffusionSvnRequest' => 'applications/diffusion/request/svn', 'DiffusionView' => 'applications/diffusion/view/base', 'HeraldAction' => 'applications/herald/storage/action', 'HeraldActionConfig' => 'applications/herald/config/action', 'HeraldApplyTranscript' => 'applications/herald/storage/transcript/apply', 'HeraldCommitAdapter' => 'applications/herald/adapter/commit', 'HeraldCondition' => 'applications/herald/storage/condition', 'HeraldConditionConfig' => 'applications/herald/config/condition', 'HeraldConditionTranscript' => 'applications/herald/storage/transcript/condition', 'HeraldContentTypeConfig' => 'applications/herald/config/contenttype', 'HeraldController' => 'applications/herald/controller/base', 'HeraldDAO' => 'applications/herald/storage/base', 'HeraldDeleteController' => 'applications/herald/controller/delete', 'HeraldDifferentialRevisionAdapter' => 'applications/herald/adapter/differential', 'HeraldDryRunAdapter' => 'applications/herald/adapter/dryrun', 'HeraldEffect' => 'applications/herald/engine/effect', 'HeraldEngine' => 'applications/herald/engine/engine', 'HeraldFieldConfig' => 'applications/herald/config/field', 'HeraldHomeController' => 'applications/herald/controller/home', 'HeraldInvalidConditionException' => 'applications/herald/engine/engine/exception', 'HeraldInvalidFieldException' => 'applications/herald/engine/engine/exception', 'HeraldNewController' => 'applications/herald/controller/new', 'HeraldObjectAdapter' => 'applications/herald/adapter/base', 'HeraldObjectTranscript' => 'applications/herald/storage/transcript/object', 'HeraldRecursiveConditionsException' => 'applications/herald/engine/engine/exception', 'HeraldRepetitionPolicyConfig' => 'applications/herald/config/repetitionpolicy', 'HeraldRule' => 'applications/herald/storage/rule', 'HeraldRuleController' => 'applications/herald/controller/rule', 'HeraldRuleTranscript' => 'applications/herald/storage/transcript/rule', 'HeraldTestConsoleController' => 'applications/herald/controller/test', 'HeraldTranscript' => 'applications/herald/storage/transcript/base', 'HeraldTranscriptController' => 'applications/herald/controller/transcript', 'HeraldTranscriptListController' => 'applications/herald/controller/transcriptlist', 'HeraldValueTypeConfig' => 'applications/herald/config/valuetype', 'Javelin' => 'infrastructure/javelin/api', 'LiskDAO' => 'storage/lisk/dao', 'LiskIsolationTestCase' => 'storage/lisk/dao/__tests__', 'LiskIsolationTestDAO' => 'storage/lisk/dao/__tests__', 'LiskIsolationTestDAOException' => 'storage/lisk/dao/__tests__', 'ManiphestController' => 'applications/maniphest/controller/base', 'ManiphestDAO' => 'applications/maniphest/storage/base', 'ManiphestReplyHandler' => 'applications/maniphest/replyhandler', 'ManiphestTask' => 'applications/maniphest/storage/task', 'ManiphestTaskDescriptionChangeController' => 'applications/maniphest/controller/descriptionchange', 'ManiphestTaskDetailController' => 'applications/maniphest/controller/taskdetail', 'ManiphestTaskEditController' => 'applications/maniphest/controller/taskedit', 'ManiphestTaskListController' => 'applications/maniphest/controller/tasklist', 'ManiphestTaskListView' => 'applications/maniphest/view/tasklist', 'ManiphestTaskOwner' => 'applications/maniphest/constants/owner', 'ManiphestTaskPriority' => 'applications/maniphest/constants/priority', + 'ManiphestTaskProject' => 'applications/maniphest/storage/taskproject', + 'ManiphestTaskQuery' => 'applications/maniphest/query', 'ManiphestTaskStatus' => 'applications/maniphest/constants/status', 'ManiphestTaskSummaryView' => 'applications/maniphest/view/tasksummary', 'ManiphestTransaction' => 'applications/maniphest/storage/transaction', 'ManiphestTransactionDetailView' => 'applications/maniphest/view/transactiondetail', 'ManiphestTransactionEditor' => 'applications/maniphest/editor/transaction', 'ManiphestTransactionListView' => 'applications/maniphest/view/transactionlist', 'ManiphestTransactionPreviewController' => 'applications/maniphest/controller/transactionpreview', 'ManiphestTransactionSaveController' => 'applications/maniphest/controller/transactionsave', 'ManiphestTransactionType' => 'applications/maniphest/constants/transactiontype', 'Phabricator404Controller' => 'applications/base/controller/404', 'PhabricatorAuthController' => 'applications/auth/controller/base', 'PhabricatorConduitAPIController' => 'applications/conduit/controller/api', 'PhabricatorConduitCertificateToken' => 'applications/conduit/storage/token', 'PhabricatorConduitConnectionLog' => 'applications/conduit/storage/connectionlog', 'PhabricatorConduitConsoleController' => 'applications/conduit/controller/console', 'PhabricatorConduitController' => 'applications/conduit/controller/base', 'PhabricatorConduitDAO' => 'applications/conduit/storage/base', 'PhabricatorConduitLogController' => 'applications/conduit/controller/log', 'PhabricatorConduitMethodCallLog' => 'applications/conduit/storage/methodcalllog', 'PhabricatorConduitTokenController' => 'applications/conduit/controller/token', 'PhabricatorController' => 'applications/base/controller/base', 'PhabricatorCountdownController' => 'applications/countdown/controller/base', 'PhabricatorCountdownDAO' => 'applications/countdown/storage/base', 'PhabricatorCountdownDeleteController' => 'applications/countdown/controller/delete', 'PhabricatorCountdownEditController' => 'applications/countdown/controller/edit', 'PhabricatorCountdownListController' => 'applications/countdown/controller/list', 'PhabricatorCountdownViewController' => 'applications/countdown/controller/view', 'PhabricatorDaemon' => 'infrastructure/daemon/base', 'PhabricatorDaemonCombinedLogController' => 'applications/daemon/controller/combined', 'PhabricatorDaemonConsoleController' => 'applications/daemon/controller/console', 'PhabricatorDaemonControl' => 'infrastructure/daemon/control', 'PhabricatorDaemonController' => 'applications/daemon/controller/base', 'PhabricatorDaemonDAO' => 'infrastructure/daemon/storage/base', 'PhabricatorDaemonLog' => 'infrastructure/daemon/storage/log', 'PhabricatorDaemonLogEvent' => 'infrastructure/daemon/storage/event', 'PhabricatorDaemonLogEventsView' => 'applications/daemon/view/daemonlogevents', 'PhabricatorDaemonLogListController' => 'applications/daemon/controller/loglist', 'PhabricatorDaemonLogListView' => 'applications/daemon/view/daemonloglist', 'PhabricatorDaemonLogViewController' => 'applications/daemon/controller/logview', 'PhabricatorDaemonReference' => 'infrastructure/daemon/control/reference', 'PhabricatorDaemonTimelineConsoleController' => 'applications/daemon/controller/timeline', 'PhabricatorDaemonTimelineEventController' => 'applications/daemon/controller/timelineevent', 'PhabricatorDirectoryCategory' => 'applications/directory/storage/category', 'PhabricatorDirectoryCategoryDeleteController' => 'applications/directory/controller/categorydelete', 'PhabricatorDirectoryCategoryEditController' => 'applications/directory/controller/categoryedit', 'PhabricatorDirectoryCategoryListController' => 'applications/directory/controller/categorylist', 'PhabricatorDirectoryController' => 'applications/directory/controller/base', 'PhabricatorDirectoryDAO' => 'applications/directory/storage/base', 'PhabricatorDirectoryItem' => 'applications/directory/storage/item', 'PhabricatorDirectoryItemDeleteController' => 'applications/directory/controller/itemdelete', 'PhabricatorDirectoryItemEditController' => 'applications/directory/controller/itemedit', 'PhabricatorDirectoryItemListController' => 'applications/directory/controller/itemlist', 'PhabricatorDirectoryMainController' => 'applications/directory/controller/main', 'PhabricatorDisabledUserController' => 'applications/auth/controller/disabled', 'PhabricatorDraft' => 'applications/draft/storage/draft', 'PhabricatorDraftDAO' => 'applications/draft/storage/base', 'PhabricatorEditPreferencesController' => 'applications/preferences/controller/edit', 'PhabricatorEmailLoginController' => 'applications/auth/controller/email', 'PhabricatorEmailTokenController' => 'applications/auth/controller/emailtoken', 'PhabricatorEnv' => 'infrastructure/env', 'PhabricatorFile' => 'applications/files/storage/file', 'PhabricatorFileController' => 'applications/files/controller/base', 'PhabricatorFileDAO' => 'applications/files/storage/base', 'PhabricatorFileDropUploadController' => 'applications/files/controller/dropupload', 'PhabricatorFileImageMacro' => 'applications/files/storage/imagemacro', 'PhabricatorFileListController' => 'applications/files/controller/list', 'PhabricatorFileMacroDeleteController' => 'applications/files/controller/macrodelete', 'PhabricatorFileMacroEditController' => 'applications/files/controller/macroedit', 'PhabricatorFileMacroListController' => 'applications/files/controller/macrolist', 'PhabricatorFileProxyController' => 'applications/files/controller/proxy', 'PhabricatorFileProxyImage' => 'applications/files/storage/proxyimage', 'PhabricatorFileStorageBlob' => 'applications/files/storage/storageblob', 'PhabricatorFileTransformController' => 'applications/files/controller/transform', 'PhabricatorFileURI' => 'applications/files/uri', 'PhabricatorFileUploadController' => 'applications/files/controller/upload', 'PhabricatorFileViewController' => 'applications/files/controller/view', 'PhabricatorGoodForNothingWorker' => 'infrastructure/daemon/workers/worker/goodfornothing', 'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/selector', 'PhabricatorHelpController' => 'applications/help/controller/base', 'PhabricatorHelpKeyboardShortcutController' => 'applications/help/controller/keyboardshortcut', 'PhabricatorIRCBot' => 'infrastructure/daemon/irc/bot', 'PhabricatorIRCHandler' => 'infrastructure/daemon/irc/handler/base', 'PhabricatorIRCMessage' => 'infrastructure/daemon/irc/message', 'PhabricatorIRCObjectNameHandler' => 'infrastructure/daemon/irc/handler/objectname', 'PhabricatorIRCProtocolHandler' => 'infrastructure/daemon/irc/handler/protocol', 'PhabricatorImageTransformer' => 'applications/files/transform', 'PhabricatorJavelinLinter' => 'infrastructure/lint/linter/javelin', 'PhabricatorLintEngine' => 'infrastructure/lint/engine', 'PhabricatorLiskDAO' => 'applications/base/storage/lisk', 'PhabricatorLoginController' => 'applications/auth/controller/login', 'PhabricatorLogoutController' => 'applications/auth/controller/logout', 'PhabricatorMailImplementationAdapter' => 'applications/metamta/adapter/base', 'PhabricatorMailImplementationAmazonSESAdapter' => 'applications/metamta/adapter/amazonses', 'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'applications/metamta/adapter/phpmailerlite', 'PhabricatorMailImplementationSendGridAdapter' => 'applications/metamta/adapter/sendgrid', 'PhabricatorMailImplementationTestAdapter' => 'applications/metamta/adapter/test', 'PhabricatorMailReplyHandler' => 'applications/metamta/replyhandler/base', 'PhabricatorMetaMTAController' => 'applications/metamta/controller/base', 'PhabricatorMetaMTADAO' => 'applications/metamta/storage/base', 'PhabricatorMetaMTADaemon' => 'applications/metamta/daemon/mta', 'PhabricatorMetaMTAEmailBodyParser' => 'applications/metamta/parser', 'PhabricatorMetaMTAEmailBodyParserTestCase' => 'applications/metamta/parser/__tests__', 'PhabricatorMetaMTAListController' => 'applications/metamta/controller/list', 'PhabricatorMetaMTAMail' => 'applications/metamta/storage/mail', 'PhabricatorMetaMTAMailTestCase' => 'applications/metamta/storage/mail/__tests__', 'PhabricatorMetaMTAMailingList' => 'applications/metamta/storage/mailinglist', 'PhabricatorMetaMTAMailingListEditController' => 'applications/metamta/controller/mailinglistedit', 'PhabricatorMetaMTAMailingListsController' => 'applications/metamta/controller/mailinglists', 'PhabricatorMetaMTAReceiveController' => 'applications/metamta/controller/receive', 'PhabricatorMetaMTAReceivedListController' => 'applications/metamta/controller/receivedlist', 'PhabricatorMetaMTAReceivedMail' => 'applications/metamta/storage/receivedmail', 'PhabricatorMetaMTASendController' => 'applications/metamta/controller/send', 'PhabricatorMetaMTASendGridReceiveController' => 'applications/metamta/controller/sendgridreceive', 'PhabricatorMetaMTAViewController' => 'applications/metamta/controller/view', 'PhabricatorOAuthDefaultRegistrationController' => 'applications/auth/controller/oauthregistration/default', 'PhabricatorOAuthDiagnosticsController' => 'applications/auth/controller/oauthdiagnostics', 'PhabricatorOAuthFailureView' => 'applications/auth/view/oauthfailure', 'PhabricatorOAuthLoginController' => 'applications/auth/controller/oauth', 'PhabricatorOAuthProvider' => 'applications/auth/oauth/provider/base', 'PhabricatorOAuthProviderFacebook' => 'applications/auth/oauth/provider/facebook', 'PhabricatorOAuthProviderGithub' => 'applications/auth/oauth/provider/github', 'PhabricatorOAuthRegistrationController' => 'applications/auth/controller/oauthregistration/base', 'PhabricatorOAuthUnlinkController' => 'applications/auth/controller/unlink', 'PhabricatorObjectHandle' => 'applications/phid/handle', 'PhabricatorObjectHandleData' => 'applications/phid/handle/data', 'PhabricatorObjectSelectorDialog' => 'view/control/objectselector', 'PhabricatorOwnersController' => 'applications/owners/controller/base', 'PhabricatorOwnersDAO' => 'applications/owners/storage/base', 'PhabricatorOwnersDeleteController' => 'applications/owners/controller/delete', 'PhabricatorOwnersDetailController' => 'applications/owners/controller/detail', 'PhabricatorOwnersEditController' => 'applications/owners/controller/edit', 'PhabricatorOwnersListController' => 'applications/owners/controller/list', 'PhabricatorOwnersOwner' => 'applications/owners/storage/owner', 'PhabricatorOwnersPackage' => 'applications/owners/storage/package', 'PhabricatorOwnersPath' => 'applications/owners/storage/path', 'PhabricatorPHID' => 'applications/phid/storage/phid', 'PhabricatorPHIDAllocateController' => 'applications/phid/controller/allocate', 'PhabricatorPHIDConstants' => 'applications/phid/constants', 'PhabricatorPHIDController' => 'applications/phid/controller/base', 'PhabricatorPHIDDAO' => 'applications/phid/storage/base', 'PhabricatorPHIDListController' => 'applications/phid/controller/list', 'PhabricatorPHIDLookupController' => 'applications/phid/controller/lookup', 'PhabricatorPaste' => 'applications/paste/storage/paste', 'PhabricatorPasteController' => 'applications/paste/controller/base', 'PhabricatorPasteCreateController' => 'applications/paste/controller/create', 'PhabricatorPasteDAO' => 'applications/paste/storage/base', 'PhabricatorPasteListController' => 'applications/paste/controller/list', 'PhabricatorPasteViewController' => 'applications/paste/controller/view', 'PhabricatorPeopleController' => 'applications/people/controller/base', 'PhabricatorPeopleEditController' => 'applications/people/controller/edit', 'PhabricatorPeopleListController' => 'applications/people/controller/list', 'PhabricatorPeopleLogsController' => 'applications/people/controller/logs', 'PhabricatorPeopleProfileController' => 'applications/people/controller/profile', 'PhabricatorPeopleProfileEditController' => 'applications/people/controller/profileedit', 'PhabricatorPreferencesController' => 'applications/preferences/controller/base', 'PhabricatorProfileView' => 'view/layout/profile', 'PhabricatorProject' => 'applications/project/storage/project', 'PhabricatorProjectAffiliation' => 'applications/project/storage/affiliation', 'PhabricatorProjectAffiliationEditController' => 'applications/project/controller/editaffiliation', 'PhabricatorProjectController' => 'applications/project/controller/base', 'PhabricatorProjectCreateController' => 'applications/project/controller/create', 'PhabricatorProjectDAO' => 'applications/project/storage/base', 'PhabricatorProjectListController' => 'applications/project/controller/list', 'PhabricatorProjectProfile' => 'applications/project/storage/profile', 'PhabricatorProjectProfileController' => 'applications/project/controller/profile', 'PhabricatorProjectProfileEditController' => 'applications/project/controller/profileedit', 'PhabricatorProjectStatus' => 'applications/project/constants/status', 'PhabricatorProjectTransactionSearch' => 'applications/project/transactions/search', 'PhabricatorRedirectController' => 'applications/base/controller/redirect', 'PhabricatorRemarkupRuleDifferential' => 'infrastructure/markup/remarkup/markuprule/differential', 'PhabricatorRemarkupRuleDiffusion' => 'infrastructure/markup/remarkup/markuprule/diffusion', 'PhabricatorRemarkupRuleImageMacro' => 'infrastructure/markup/remarkup/markuprule/imagemacro', 'PhabricatorRemarkupRuleManiphest' => 'infrastructure/markup/remarkup/markuprule/maniphest', 'PhabricatorRemarkupRuleMention' => 'infrastructure/markup/remarkup/markuprule/mention', 'PhabricatorRemarkupRuleObjectName' => 'infrastructure/markup/remarkup/markuprule/objectname', 'PhabricatorRemarkupRulePaste' => 'infrastructure/markup/remarkup/markuprule/paste', 'PhabricatorRemarkupRuleProxyImage' => 'infrastructure/markup/remarkup/markuprule/proxyimage', 'PhabricatorRemarkupRuleYoutube' => 'infrastructure/markup/remarkup/markuprule/youtube', 'PhabricatorRepository' => 'applications/repository/storage/repository', 'PhabricatorRepositoryArcanistProject' => 'applications/repository/storage/arcanistproject', 'PhabricatorRepositoryArcanistProjectEditController' => 'applications/repository/controller/arcansistprojectedit', 'PhabricatorRepositoryCommit' => 'applications/repository/storage/commit', 'PhabricatorRepositoryCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/base', 'PhabricatorRepositoryCommitData' => 'applications/repository/storage/commitdata', 'PhabricatorRepositoryCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/base', 'PhabricatorRepositoryCommitHeraldWorker' => 'applications/repository/worker/herald', 'PhabricatorRepositoryCommitMessageDetailParser' => 'applications/repository/parser/base', 'PhabricatorRepositoryCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/base', 'PhabricatorRepositoryCommitParserWorker' => 'applications/repository/worker/base', 'PhabricatorRepositoryCommitTaskDaemon' => 'applications/repository/daemon/committask', 'PhabricatorRepositoryController' => 'applications/repository/controller/base', 'PhabricatorRepositoryCreateController' => 'applications/repository/controller/create', 'PhabricatorRepositoryDAO' => 'applications/repository/storage/base', 'PhabricatorRepositoryDaemon' => 'applications/repository/daemon/base', 'PhabricatorRepositoryDefaultCommitMessageDetailParser' => 'applications/repository/parser/default', 'PhabricatorRepositoryDeleteController' => 'applications/repository/controller/delete', 'PhabricatorRepositoryEditController' => 'applications/repository/controller/edit', 'PhabricatorRepositoryGitCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/git', 'PhabricatorRepositoryGitCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/git', 'PhabricatorRepositoryGitCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/git', 'PhabricatorRepositoryGitFetchDaemon' => 'applications/repository/daemon/gitfetch', 'PhabricatorRepositoryGitHubNotification' => 'applications/repository/storage/githubnotification', 'PhabricatorRepositoryGitHubPostReceiveController' => 'applications/repository/controller/github-post-receive', 'PhabricatorRepositoryListController' => 'applications/repository/controller/list', 'PhabricatorRepositoryShortcut' => 'applications/repository/storage/shortcut', 'PhabricatorRepositorySvnCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/svn', 'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/svn', 'PhabricatorRepositorySvnCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/svn', 'PhabricatorRepositoryType' => 'applications/repository/constants/repositorytype', 'PhabricatorSQLPatchList' => 'infrastructure/setup/sql', 'PhabricatorSearchAbstractDocument' => 'applications/search/index/abstractdocument', 'PhabricatorSearchAttachController' => 'applications/search/controller/attach', 'PhabricatorSearchBaseController' => 'applications/search/controller/base', 'PhabricatorSearchCommitIndexer' => 'applications/search/index/indexer/repository', 'PhabricatorSearchController' => 'applications/search/controller/search', 'PhabricatorSearchDAO' => 'applications/search/storage/base', 'PhabricatorSearchDifferentialIndexer' => 'applications/search/index/indexer/differential', 'PhabricatorSearchDocument' => 'applications/search/storage/document/document', 'PhabricatorSearchDocumentField' => 'applications/search/storage/document/field', 'PhabricatorSearchDocumentIndexer' => 'applications/search/index/indexer/base', 'PhabricatorSearchDocumentRelationship' => 'applications/search/storage/document/relationship', 'PhabricatorSearchExecutor' => 'applications/search/execute/base', 'PhabricatorSearchField' => 'applications/search/constants/field', 'PhabricatorSearchIndexController' => 'applications/search/controller/index', 'PhabricatorSearchManiphestIndexer' => 'applications/search/index/indexer/maniphest', 'PhabricatorSearchMySQLExecutor' => 'applications/search/execute/mysql', 'PhabricatorSearchQuery' => 'applications/search/storage/query', 'PhabricatorSearchRelationship' => 'applications/search/constants/relationship', 'PhabricatorSearchResultView' => 'applications/search/view/searchresult', 'PhabricatorSearchSelectController' => 'applications/search/controller/select', 'PhabricatorSearchUserIndexer' => 'applications/search/index/indexer/user', 'PhabricatorSetup' => 'infrastructure/setup', 'PhabricatorStandardPageView' => 'view/page/standard', 'PhabricatorStatusController' => 'applications/status/base', 'PhabricatorTaskmasterDaemon' => 'infrastructure/daemon/workers/taskmaster', 'PhabricatorTestCase' => 'infrastructure/testing/testcase', 'PhabricatorTimelineCursor' => 'infrastructure/daemon/timeline/storage/cursor', 'PhabricatorTimelineDAO' => 'infrastructure/daemon/timeline/storage/base', 'PhabricatorTimelineEvent' => 'infrastructure/daemon/timeline/storage/event', 'PhabricatorTimelineEventData' => 'infrastructure/daemon/timeline/storage/eventdata', 'PhabricatorTimelineIterator' => 'infrastructure/daemon/timeline/cursor/iterator', 'PhabricatorTimer' => 'applications/countdown/storage/timer', 'PhabricatorTransformedFile' => 'applications/files/storage/transformed', 'PhabricatorTypeaheadCommonDatasourceController' => 'applications/typeahead/controller/common', 'PhabricatorTypeaheadDatasourceController' => 'applications/typeahead/controller/base', 'PhabricatorUIExample' => 'applications/uiexample/examples/base', 'PhabricatorUIExampleController' => 'applications/uiexample/controller/base', 'PhabricatorUIExampleRenderController' => 'applications/uiexample/controller/render', 'PhabricatorUIListFilterExample' => 'applications/uiexample/examples/listfilter', 'PhabricatorUIPagerExample' => 'applications/uiexample/examples/pager', 'PhabricatorUser' => 'applications/people/storage/user', 'PhabricatorUserDAO' => 'applications/people/storage/base', 'PhabricatorUserLog' => 'applications/people/storage/log', 'PhabricatorUserOAuthInfo' => 'applications/people/storage/useroauthinfo', 'PhabricatorUserPreferences' => 'applications/people/storage/preferences', 'PhabricatorUserProfile' => 'applications/people/storage/profile', 'PhabricatorUserSettingsController' => 'applications/people/controller/settings', 'PhabricatorWorker' => 'infrastructure/daemon/workers/worker', 'PhabricatorWorkerDAO' => 'infrastructure/daemon/workers/storage/base', 'PhabricatorWorkerTask' => 'infrastructure/daemon/workers/storage/task', 'PhabricatorWorkerTaskData' => 'infrastructure/daemon/workers/storage/taskdata', 'PhabricatorWorkerTaskDetailController' => 'applications/daemon/controller/workertaskdetail', 'PhabricatorXHPASTViewController' => 'applications/xhpastview/controller/base', 'PhabricatorXHPASTViewDAO' => 'applications/xhpastview/storage/base', 'PhabricatorXHPASTViewFrameController' => 'applications/xhpastview/controller/viewframe', 'PhabricatorXHPASTViewFramesetController' => 'applications/xhpastview/controller/viewframeset', 'PhabricatorXHPASTViewInputController' => 'applications/xhpastview/controller/viewinput', 'PhabricatorXHPASTViewPanelController' => 'applications/xhpastview/controller/viewpanel', 'PhabricatorXHPASTViewParseTree' => 'applications/xhpastview/storage/parsetree', 'PhabricatorXHPASTViewRunController' => 'applications/xhpastview/controller/run', 'PhabricatorXHPASTViewStreamController' => 'applications/xhpastview/controller/viewstream', 'PhabricatorXHPASTViewTreeController' => 'applications/xhpastview/controller/viewtree', 'PhabricatorXHProfController' => 'applications/xhprof/controller/base', 'PhabricatorXHProfProfileController' => 'applications/xhprof/controller/profile', 'PhabricatorXHProfProfileSymbolView' => 'applications/xhprof/view/symbol', 'PhabricatorXHProfProfileTopLevelView' => 'applications/xhprof/view/toplevel', ), 'function' => array( '_qsprintf_check_scalar_type' => 'storage/qsprintf', '_qsprintf_check_type' => 'storage/qsprintf', 'celerity_generate_unique_node_id' => 'infrastructure/celerity/api', 'celerity_register_resource_map' => 'infrastructure/celerity/map', 'javelin_render_tag' => 'infrastructure/javelin/markup', 'phabricator_date' => 'view/utils', 'phabricator_datetime' => 'view/utils', 'phabricator_format_relative_time' => 'view/utils', 'phabricator_format_timestamp' => 'view/utils', 'phabricator_format_units_generic' => 'view/utils', 'phabricator_render_form' => 'infrastructure/javelin/markup', 'phabricator_time' => 'view/utils', 'qsprintf' => 'storage/qsprintf', 'queryfx' => 'storage/queryfx', 'queryfx_all' => 'storage/queryfx', 'queryfx_one' => 'storage/queryfx', 'require_celerity_resource' => 'infrastructure/celerity/api', 'vqsprintf' => 'storage/qsprintf', 'vqueryfx' => 'storage/queryfx', 'vqueryfx_all' => 'storage/queryfx', 'xsprintf_query' => 'storage/qsprintf', ), 'requires_class' => array( 'Aphront304Response' => 'AphrontResponse', 'Aphront400Response' => 'AphrontResponse', 'Aphront404Response' => 'AphrontResponse', 'AphrontAjaxResponse' => 'AphrontResponse', 'AphrontAttachedFileView' => 'AphrontView', 'AphrontCrumbsView' => 'AphrontView', 'AphrontDefaultApplicationConfiguration' => 'AphrontApplicationConfiguration', 'AphrontDefaultApplicationController' => 'AphrontController', 'AphrontDialogResponse' => 'AphrontResponse', 'AphrontDialogView' => 'AphrontView', 'AphrontErrorView' => 'AphrontView', 'AphrontFilePreviewView' => 'AphrontView', 'AphrontFileResponse' => 'AphrontResponse', 'AphrontFormCheckboxControl' => 'AphrontFormControl', 'AphrontFormControl' => 'AphrontView', 'AphrontFormDividerControl' => 'AphrontFormControl', 'AphrontFormDragAndDropUploadControl' => 'AphrontFormControl', 'AphrontFormFileControl' => 'AphrontFormControl', 'AphrontFormLayoutView' => 'AphrontView', 'AphrontFormMarkupControl' => 'AphrontFormControl', 'AphrontFormPasswordControl' => 'AphrontFormControl', 'AphrontFormRecaptchaControl' => 'AphrontFormControl', 'AphrontFormSelectControl' => 'AphrontFormControl', 'AphrontFormStaticControl' => 'AphrontFormControl', 'AphrontFormSubmitControl' => 'AphrontFormControl', 'AphrontFormTextAreaControl' => 'AphrontFormControl', 'AphrontFormTextControl' => 'AphrontFormControl', 'AphrontFormToggleButtonsControl' => 'AphrontFormControl', 'AphrontFormTokenizerControl' => 'AphrontFormControl', 'AphrontFormView' => 'AphrontView', 'AphrontHeadsupActionListView' => 'AphrontView', 'AphrontHeadsupActionView' => 'AphrontView', 'AphrontIsolatedDatabaseConnection' => 'AphrontDatabaseConnection', 'AphrontIsolatedDatabaseConnectionTestCase' => 'PhabricatorTestCase', 'AphrontKeyboardShortcutsAvailableView' => 'AphrontView', 'AphrontListFilterView' => 'AphrontView', 'AphrontMySQLDatabaseConnection' => 'AphrontDatabaseConnection', 'AphrontNullView' => 'AphrontView', 'AphrontPageView' => 'AphrontView', 'AphrontPagerView' => 'AphrontView', 'AphrontPanelView' => 'AphrontView', 'AphrontQueryAccessDeniedException' => 'AphrontQueryRecoverableException', 'AphrontQueryConnectionException' => 'AphrontQueryException', 'AphrontQueryConnectionLostException' => 'AphrontQueryRecoverableException', 'AphrontQueryCountException' => 'AphrontQueryException', 'AphrontQueryDuplicateKeyException' => 'AphrontQueryException', 'AphrontQueryObjectMissingException' => 'AphrontQueryException', 'AphrontQueryParameterException' => 'AphrontQueryException', 'AphrontQueryRecoverableException' => 'AphrontQueryException', 'AphrontRedirectException' => 'AphrontException', 'AphrontRedirectResponse' => 'AphrontResponse', 'AphrontReloadResponse' => 'AphrontRedirectResponse', 'AphrontRequestFailureView' => 'AphrontView', 'AphrontSideNavView' => 'AphrontView', 'AphrontTableView' => 'AphrontView', 'AphrontTokenizerTemplateView' => 'AphrontView', 'AphrontTypeaheadTemplateView' => 'AphrontView', 'AphrontWebpageResponse' => 'AphrontResponse', 'CelerityResourceController' => 'AphrontController', 'ConduitAPI_conduit_connect_Method' => 'ConduitAPIMethod', 'ConduitAPI_conduit_getcertificate_Method' => 'ConduitAPIMethod', 'ConduitAPI_conduit_ping_Method' => 'ConduitAPIMethod', 'ConduitAPI_daemon_launched_Method' => 'ConduitAPIMethod', 'ConduitAPI_daemon_log_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_creatediff_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_createrevision_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_find_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_getalldiffs_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_getcommitmessage_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_getcommitpaths_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_getdiff_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_getrevision_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_getrevisionfeedback_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_markcommitted_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_parsecommitmessage_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_setdiffproperty_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_updaterevision_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_updatetaskrevisionassoc_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_updateunitresults_Method' => 'ConduitAPIMethod', 'ConduitAPI_diffusion_getcommits_Method' => 'ConduitAPIMethod', 'ConduitAPI_diffusion_getrecentcommitsbypath_Method' => 'ConduitAPIMethod', 'ConduitAPI_file_download_Method' => 'ConduitAPIMethod', 'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod', 'ConduitAPI_maniphest_info_Method' => 'ConduitAPIMethod', 'ConduitAPI_paste_info_Method' => 'ConduitAPIMethod', 'ConduitAPI_path_getowners_Method' => 'ConduitAPIMethod', 'ConduitAPI_user_find_Method' => 'ConduitAPIMethod', 'ConduitAPI_user_whoami_Method' => 'ConduitAPIMethod', 'DarkConsoleConfigPlugin' => 'DarkConsolePlugin', 'DarkConsoleController' => 'PhabricatorController', 'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin', 'DarkConsoleRequestPlugin' => 'DarkConsolePlugin', 'DarkConsoleServicesPlugin' => 'DarkConsolePlugin', 'DarkConsoleXHProfPlugin' => 'DarkConsolePlugin', 'DifferentialAddCommentView' => 'AphrontView', 'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail', 'DifferentialChangeset' => 'DifferentialDAO', 'DifferentialChangesetDetailView' => 'AphrontView', 'DifferentialChangesetListView' => 'AphrontView', 'DifferentialChangesetViewController' => 'DifferentialController', 'DifferentialComment' => 'DifferentialDAO', 'DifferentialCommentMail' => 'DifferentialMail', 'DifferentialCommentPreviewController' => 'DifferentialController', 'DifferentialCommentSaveController' => 'DifferentialController', 'DifferentialController' => 'PhabricatorController', 'DifferentialDAO' => 'PhabricatorLiskDAO', 'DifferentialDiff' => 'DifferentialDAO', 'DifferentialDiffContentMail' => 'DifferentialMail', 'DifferentialDiffCreateController' => 'DifferentialController', 'DifferentialDiffProperty' => 'DifferentialDAO', 'DifferentialDiffTableOfContentsView' => 'AphrontView', 'DifferentialDiffViewController' => 'DifferentialController', 'DifferentialExceptionMail' => 'DifferentialMail', 'DifferentialHunk' => 'DifferentialDAO', 'DifferentialInlineComment' => 'DifferentialDAO', 'DifferentialInlineCommentEditController' => 'DifferentialController', 'DifferentialInlineCommentPreviewController' => 'DifferentialController', 'DifferentialInlineCommentView' => 'AphrontView', 'DifferentialNewDiffMail' => 'DifferentialReviewRequestMail', 'DifferentialPrimaryPaneView' => 'AphrontView', 'DifferentialReplyHandler' => 'PhabricatorMailReplyHandler', 'DifferentialReviewRequestMail' => 'DifferentialMail', 'DifferentialRevision' => 'DifferentialDAO', 'DifferentialRevisionCommentListView' => 'AphrontView', 'DifferentialRevisionCommentView' => 'AphrontView', 'DifferentialRevisionDetailView' => 'AphrontView', 'DifferentialRevisionEditController' => 'DifferentialController', 'DifferentialRevisionListController' => 'DifferentialController', 'DifferentialRevisionUpdateHistoryView' => 'AphrontView', 'DifferentialRevisionViewController' => 'DifferentialController', 'DifferentialSubscribeController' => 'DifferentialController', 'DifferentialViewTime' => 'DifferentialDAO', 'DiffusionBranchTableView' => 'DiffusionView', 'DiffusionBrowseController' => 'DiffusionController', 'DiffusionBrowseFileController' => 'DiffusionController', 'DiffusionBrowseTableView' => 'DiffusionView', 'DiffusionChangeController' => 'DiffusionController', 'DiffusionCommitChangeTableView' => 'DiffusionView', 'DiffusionCommitController' => 'DiffusionController', 'DiffusionController' => 'PhabricatorController', 'DiffusionDiffController' => 'DiffusionController', 'DiffusionGitBranchQuery' => 'DiffusionBranchQuery', 'DiffusionGitBrowseQuery' => 'DiffusionBrowseQuery', 'DiffusionGitDiffQuery' => 'DiffusionDiffQuery', 'DiffusionGitFileContentQuery' => 'DiffusionFileContentQuery', 'DiffusionGitHistoryQuery' => 'DiffusionHistoryQuery', 'DiffusionGitLastModifiedQuery' => 'DiffusionLastModifiedQuery', 'DiffusionGitRequest' => 'DiffusionRequest', 'DiffusionHistoryController' => 'DiffusionController', 'DiffusionHistoryTableView' => 'DiffusionView', 'DiffusionHomeController' => 'DiffusionController', 'DiffusionLastModifiedController' => 'DiffusionController', 'DiffusionPathCompleteController' => 'DiffusionController', 'DiffusionPathValidateController' => 'DiffusionController', 'DiffusionRepositoryController' => 'DiffusionController', 'DiffusionSvnBrowseQuery' => 'DiffusionBrowseQuery', 'DiffusionSvnDiffQuery' => 'DiffusionDiffQuery', 'DiffusionSvnFileContentQuery' => 'DiffusionFileContentQuery', 'DiffusionSvnHistoryQuery' => 'DiffusionHistoryQuery', 'DiffusionSvnLastModifiedQuery' => 'DiffusionLastModifiedQuery', 'DiffusionSvnRequest' => 'DiffusionRequest', 'DiffusionView' => 'AphrontView', 'HeraldAction' => 'HeraldDAO', 'HeraldApplyTranscript' => 'HeraldDAO', 'HeraldCommitAdapter' => 'HeraldObjectAdapter', 'HeraldCondition' => 'HeraldDAO', 'HeraldController' => 'PhabricatorController', 'HeraldDAO' => 'PhabricatorLiskDAO', 'HeraldDeleteController' => 'HeraldController', 'HeraldDifferentialRevisionAdapter' => 'HeraldObjectAdapter', 'HeraldDryRunAdapter' => 'HeraldObjectAdapter', 'HeraldHomeController' => 'HeraldController', 'HeraldNewController' => 'HeraldController', 'HeraldRule' => 'HeraldDAO', 'HeraldRuleController' => 'HeraldController', 'HeraldTestConsoleController' => 'HeraldController', 'HeraldTranscript' => 'HeraldDAO', 'HeraldTranscriptController' => 'HeraldController', 'HeraldTranscriptListController' => 'HeraldController', 'LiskIsolationTestCase' => 'PhabricatorTestCase', 'LiskIsolationTestDAO' => 'LiskDAO', 'ManiphestController' => 'PhabricatorController', 'ManiphestDAO' => 'PhabricatorLiskDAO', 'ManiphestReplyHandler' => 'PhabricatorMailReplyHandler', 'ManiphestTask' => 'ManiphestDAO', 'ManiphestTaskDescriptionChangeController' => 'ManiphestController', 'ManiphestTaskDetailController' => 'ManiphestController', 'ManiphestTaskEditController' => 'ManiphestController', 'ManiphestTaskListController' => 'ManiphestController', 'ManiphestTaskListView' => 'AphrontView', + 'ManiphestTaskProject' => 'ManiphestDAO', 'ManiphestTaskSummaryView' => 'AphrontView', 'ManiphestTransaction' => 'ManiphestDAO', 'ManiphestTransactionDetailView' => 'AphrontView', 'ManiphestTransactionListView' => 'AphrontView', 'ManiphestTransactionPreviewController' => 'ManiphestController', 'ManiphestTransactionSaveController' => 'ManiphestController', 'Phabricator404Controller' => 'PhabricatorController', 'PhabricatorAuthController' => 'PhabricatorController', 'PhabricatorConduitAPIController' => 'PhabricatorConduitController', 'PhabricatorConduitCertificateToken' => 'PhabricatorConduitDAO', 'PhabricatorConduitConnectionLog' => 'PhabricatorConduitDAO', 'PhabricatorConduitConsoleController' => 'PhabricatorConduitController', 'PhabricatorConduitController' => 'PhabricatorController', 'PhabricatorConduitDAO' => 'PhabricatorLiskDAO', 'PhabricatorConduitLogController' => 'PhabricatorConduitController', 'PhabricatorConduitMethodCallLog' => 'PhabricatorConduitDAO', 'PhabricatorConduitTokenController' => 'PhabricatorConduitController', 'PhabricatorController' => 'AphrontController', 'PhabricatorCountdownController' => 'PhabricatorController', 'PhabricatorCountdownDAO' => 'PhabricatorLiskDAO', 'PhabricatorCountdownDeleteController' => 'PhabricatorCountdownController', 'PhabricatorCountdownEditController' => 'PhabricatorCountdownController', 'PhabricatorCountdownListController' => 'PhabricatorCountdownController', 'PhabricatorCountdownViewController' => 'PhabricatorCountdownController', 'PhabricatorDaemon' => 'PhutilDaemon', 'PhabricatorDaemonCombinedLogController' => 'PhabricatorDaemonController', 'PhabricatorDaemonConsoleController' => 'PhabricatorDaemonController', 'PhabricatorDaemonController' => 'PhabricatorController', 'PhabricatorDaemonDAO' => 'PhabricatorLiskDAO', 'PhabricatorDaemonLog' => 'PhabricatorDaemonDAO', 'PhabricatorDaemonLogEvent' => 'PhabricatorDaemonDAO', 'PhabricatorDaemonLogEventsView' => 'AphrontView', 'PhabricatorDaemonLogListController' => 'PhabricatorDaemonController', 'PhabricatorDaemonLogListView' => 'AphrontView', 'PhabricatorDaemonLogViewController' => 'PhabricatorDaemonController', 'PhabricatorDaemonTimelineConsoleController' => 'PhabricatorDaemonController', 'PhabricatorDaemonTimelineEventController' => 'PhabricatorDaemonController', 'PhabricatorDirectoryCategory' => 'PhabricatorDirectoryDAO', 'PhabricatorDirectoryCategoryDeleteController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryCategoryEditController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryCategoryListController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryController' => 'PhabricatorController', 'PhabricatorDirectoryDAO' => 'PhabricatorLiskDAO', 'PhabricatorDirectoryItem' => 'PhabricatorDirectoryDAO', 'PhabricatorDirectoryItemDeleteController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryItemEditController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryItemListController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryMainController' => 'PhabricatorDirectoryController', 'PhabricatorDisabledUserController' => 'PhabricatorAuthController', 'PhabricatorDraft' => 'PhabricatorDraftDAO', 'PhabricatorDraftDAO' => 'PhabricatorLiskDAO', 'PhabricatorEditPreferencesController' => 'PhabricatorPreferencesController', 'PhabricatorEmailLoginController' => 'PhabricatorAuthController', 'PhabricatorEmailTokenController' => 'PhabricatorAuthController', 'PhabricatorFile' => 'PhabricatorFileDAO', 'PhabricatorFileController' => 'PhabricatorController', 'PhabricatorFileDAO' => 'PhabricatorLiskDAO', 'PhabricatorFileDropUploadController' => 'PhabricatorFileController', 'PhabricatorFileImageMacro' => 'PhabricatorFileDAO', 'PhabricatorFileListController' => 'PhabricatorFileController', 'PhabricatorFileMacroDeleteController' => 'PhabricatorFileController', 'PhabricatorFileMacroEditController' => 'PhabricatorFileController', 'PhabricatorFileMacroListController' => 'PhabricatorFileController', 'PhabricatorFileProxyController' => 'PhabricatorFileController', 'PhabricatorFileProxyImage' => 'PhabricatorFileDAO', 'PhabricatorFileStorageBlob' => 'PhabricatorFileDAO', 'PhabricatorFileTransformController' => 'PhabricatorFileController', 'PhabricatorFileUploadController' => 'PhabricatorFileController', 'PhabricatorFileViewController' => 'PhabricatorFileController', 'PhabricatorGoodForNothingWorker' => 'PhabricatorWorker', 'PhabricatorHelpController' => 'PhabricatorController', 'PhabricatorHelpKeyboardShortcutController' => 'PhabricatorHelpController', 'PhabricatorIRCBot' => 'PhabricatorDaemon', 'PhabricatorIRCObjectNameHandler' => 'PhabricatorIRCHandler', 'PhabricatorIRCProtocolHandler' => 'PhabricatorIRCHandler', 'PhabricatorJavelinLinter' => 'ArcanistLinter', 'PhabricatorLintEngine' => 'PhutilLintEngine', 'PhabricatorLiskDAO' => 'LiskDAO', 'PhabricatorLoginController' => 'PhabricatorAuthController', 'PhabricatorLogoutController' => 'PhabricatorAuthController', 'PhabricatorMailImplementationAmazonSESAdapter' => 'PhabricatorMailImplementationPHPMailerLiteAdapter', 'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'PhabricatorMailImplementationAdapter', 'PhabricatorMailImplementationSendGridAdapter' => 'PhabricatorMailImplementationAdapter', 'PhabricatorMailImplementationTestAdapter' => 'PhabricatorMailImplementationAdapter', 'PhabricatorMetaMTAController' => 'PhabricatorController', 'PhabricatorMetaMTADAO' => 'PhabricatorLiskDAO', 'PhabricatorMetaMTADaemon' => 'PhabricatorDaemon', 'PhabricatorMetaMTAEmailBodyParserTestCase' => 'PhabricatorTestCase', 'PhabricatorMetaMTAListController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTAMail' => 'PhabricatorMetaMTADAO', 'PhabricatorMetaMTAMailTestCase' => 'PhabricatorTestCase', 'PhabricatorMetaMTAMailingList' => 'PhabricatorMetaMTADAO', 'PhabricatorMetaMTAMailingListEditController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTAMailingListsController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTAReceiveController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTAReceivedListController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTAReceivedMail' => 'PhabricatorMetaMTADAO', 'PhabricatorMetaMTASendController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTASendGridReceiveController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTAViewController' => 'PhabricatorMetaMTAController', 'PhabricatorOAuthDefaultRegistrationController' => 'PhabricatorOAuthRegistrationController', 'PhabricatorOAuthDiagnosticsController' => 'PhabricatorAuthController', 'PhabricatorOAuthFailureView' => 'AphrontView', 'PhabricatorOAuthLoginController' => 'PhabricatorAuthController', 'PhabricatorOAuthProviderFacebook' => 'PhabricatorOAuthProvider', 'PhabricatorOAuthProviderGithub' => 'PhabricatorOAuthProvider', 'PhabricatorOAuthRegistrationController' => 'PhabricatorAuthController', 'PhabricatorOAuthUnlinkController' => 'PhabricatorAuthController', 'PhabricatorOwnersController' => 'PhabricatorController', 'PhabricatorOwnersDAO' => 'PhabricatorLiskDAO', 'PhabricatorOwnersDeleteController' => 'PhabricatorOwnersController', 'PhabricatorOwnersDetailController' => 'PhabricatorOwnersController', 'PhabricatorOwnersEditController' => 'PhabricatorOwnersController', 'PhabricatorOwnersListController' => 'PhabricatorOwnersController', 'PhabricatorOwnersOwner' => 'PhabricatorOwnersDAO', 'PhabricatorOwnersPackage' => 'PhabricatorOwnersDAO', 'PhabricatorOwnersPath' => 'PhabricatorOwnersDAO', 'PhabricatorPHID' => 'PhabricatorPHIDDAO', 'PhabricatorPHIDAllocateController' => 'PhabricatorPHIDController', 'PhabricatorPHIDController' => 'PhabricatorController', 'PhabricatorPHIDDAO' => 'PhabricatorLiskDAO', 'PhabricatorPHIDListController' => 'PhabricatorPHIDController', 'PhabricatorPHIDLookupController' => 'PhabricatorPHIDController', 'PhabricatorPaste' => 'PhabricatorPasteDAO', 'PhabricatorPasteController' => 'PhabricatorController', 'PhabricatorPasteCreateController' => 'PhabricatorPasteController', 'PhabricatorPasteDAO' => 'PhabricatorLiskDAO', 'PhabricatorPasteListController' => 'PhabricatorPasteController', 'PhabricatorPasteViewController' => 'PhabricatorPasteController', 'PhabricatorPeopleController' => 'PhabricatorController', 'PhabricatorPeopleEditController' => 'PhabricatorPeopleController', 'PhabricatorPeopleListController' => 'PhabricatorPeopleController', 'PhabricatorPeopleLogsController' => 'PhabricatorPeopleController', 'PhabricatorPeopleProfileController' => 'PhabricatorPeopleController', 'PhabricatorPeopleProfileEditController' => 'PhabricatorPeopleController', 'PhabricatorPreferencesController' => 'PhabricatorController', 'PhabricatorProfileView' => 'AphrontView', 'PhabricatorProject' => 'PhabricatorProjectDAO', 'PhabricatorProjectAffiliation' => 'PhabricatorProjectDAO', 'PhabricatorProjectAffiliationEditController' => 'PhabricatorProjectController', 'PhabricatorProjectController' => 'PhabricatorController', 'PhabricatorProjectCreateController' => 'PhabricatorProjectController', 'PhabricatorProjectDAO' => 'PhabricatorLiskDAO', 'PhabricatorProjectListController' => 'PhabricatorProjectController', 'PhabricatorProjectProfile' => 'PhabricatorProjectDAO', 'PhabricatorProjectProfileController' => 'PhabricatorProjectController', 'PhabricatorProjectProfileEditController' => 'PhabricatorProjectController', 'PhabricatorRedirectController' => 'PhabricatorController', 'PhabricatorRemarkupRuleDifferential' => 'PhabricatorRemarkupRuleObjectName', 'PhabricatorRemarkupRuleDiffusion' => 'PhutilRemarkupRule', 'PhabricatorRemarkupRuleImageMacro' => 'PhutilRemarkupRule', 'PhabricatorRemarkupRuleManiphest' => 'PhabricatorRemarkupRuleObjectName', 'PhabricatorRemarkupRuleMention' => 'PhutilRemarkupRule', 'PhabricatorRemarkupRuleObjectName' => 'PhutilRemarkupRule', 'PhabricatorRemarkupRulePaste' => 'PhabricatorRemarkupRuleObjectName', 'PhabricatorRemarkupRuleProxyImage' => 'PhutilRemarkupRule', 'PhabricatorRemarkupRuleYoutube' => 'PhutilRemarkupRule', 'PhabricatorRepository' => 'PhabricatorRepositoryDAO', 'PhabricatorRepositoryArcanistProject' => 'PhabricatorRepositoryDAO', 'PhabricatorRepositoryArcanistProjectEditController' => 'PhabricatorRepositoryController', 'PhabricatorRepositoryCommit' => 'PhabricatorRepositoryDAO', 'PhabricatorRepositoryCommitChangeParserWorker' => 'PhabricatorRepositoryCommitParserWorker', 'PhabricatorRepositoryCommitData' => 'PhabricatorRepositoryDAO', 'PhabricatorRepositoryCommitDiscoveryDaemon' => 'PhabricatorRepositoryDaemon', 'PhabricatorRepositoryCommitHeraldWorker' => 'PhabricatorRepositoryCommitParserWorker', 'PhabricatorRepositoryCommitMessageParserWorker' => 'PhabricatorRepositoryCommitParserWorker', 'PhabricatorRepositoryCommitParserWorker' => 'PhabricatorWorker', 'PhabricatorRepositoryCommitTaskDaemon' => 'PhabricatorRepositoryDaemon', 'PhabricatorRepositoryController' => 'PhabricatorController', 'PhabricatorRepositoryCreateController' => 'PhabricatorRepositoryController', 'PhabricatorRepositoryDAO' => 'PhabricatorLiskDAO', 'PhabricatorRepositoryDaemon' => 'PhabricatorDaemon', 'PhabricatorRepositoryDefaultCommitMessageDetailParser' => 'PhabricatorRepositoryCommitMessageDetailParser', 'PhabricatorRepositoryDeleteController' => 'PhabricatorRepositoryController', 'PhabricatorRepositoryEditController' => 'PhabricatorRepositoryController', 'PhabricatorRepositoryGitCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker', 'PhabricatorRepositoryGitCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon', 'PhabricatorRepositoryGitCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker', 'PhabricatorRepositoryGitFetchDaemon' => 'PhabricatorRepositoryDaemon', 'PhabricatorRepositoryGitHubNotification' => 'PhabricatorRepositoryDAO', 'PhabricatorRepositoryGitHubPostReceiveController' => 'PhabricatorRepositoryController', 'PhabricatorRepositoryListController' => 'PhabricatorRepositoryController', 'PhabricatorRepositoryShortcut' => 'PhabricatorRepositoryDAO', 'PhabricatorRepositorySvnCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker', 'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon', 'PhabricatorRepositorySvnCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker', 'PhabricatorSearchAttachController' => 'PhabricatorSearchController', 'PhabricatorSearchBaseController' => 'PhabricatorController', 'PhabricatorSearchCommitIndexer' => 'PhabricatorSearchDocumentIndexer', 'PhabricatorSearchController' => 'PhabricatorSearchBaseController', 'PhabricatorSearchDAO' => 'PhabricatorLiskDAO', 'PhabricatorSearchDifferentialIndexer' => 'PhabricatorSearchDocumentIndexer', 'PhabricatorSearchDocument' => 'PhabricatorSearchDAO', 'PhabricatorSearchDocumentField' => 'PhabricatorSearchDAO', 'PhabricatorSearchDocumentRelationship' => 'PhabricatorSearchDAO', 'PhabricatorSearchIndexController' => 'PhabricatorSearchBaseController', 'PhabricatorSearchManiphestIndexer' => 'PhabricatorSearchDocumentIndexer', 'PhabricatorSearchMySQLExecutor' => 'PhabricatorSearchExecutor', 'PhabricatorSearchQuery' => 'PhabricatorSearchDAO', 'PhabricatorSearchResultView' => 'AphrontView', 'PhabricatorSearchSelectController' => 'PhabricatorSearchController', 'PhabricatorSearchUserIndexer' => 'PhabricatorSearchDocumentIndexer', 'PhabricatorStandardPageView' => 'AphrontPageView', 'PhabricatorStatusController' => 'PhabricatorController', 'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon', 'PhabricatorTestCase' => 'ArcanistPhutilTestCase', 'PhabricatorTimelineCursor' => 'PhabricatorTimelineDAO', 'PhabricatorTimelineDAO' => 'PhabricatorLiskDAO', 'PhabricatorTimelineEvent' => 'PhabricatorTimelineDAO', 'PhabricatorTimelineEventData' => 'PhabricatorTimelineDAO', 'PhabricatorTimer' => 'PhabricatorCountdownDAO', 'PhabricatorTransformedFile' => 'PhabricatorFileDAO', 'PhabricatorTypeaheadCommonDatasourceController' => 'PhabricatorTypeaheadDatasourceController', 'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController', 'PhabricatorUIExampleController' => 'PhabricatorController', 'PhabricatorUIExampleRenderController' => 'PhabricatorUIExampleController', 'PhabricatorUIListFilterExample' => 'PhabricatorUIExample', 'PhabricatorUIPagerExample' => 'PhabricatorUIExample', 'PhabricatorUser' => 'PhabricatorUserDAO', 'PhabricatorUserDAO' => 'PhabricatorLiskDAO', 'PhabricatorUserLog' => 'PhabricatorUserDAO', 'PhabricatorUserOAuthInfo' => 'PhabricatorUserDAO', 'PhabricatorUserPreferences' => 'PhabricatorUserDAO', 'PhabricatorUserProfile' => 'PhabricatorUserDAO', 'PhabricatorUserSettingsController' => 'PhabricatorPeopleController', 'PhabricatorWorkerDAO' => 'PhabricatorLiskDAO', 'PhabricatorWorkerTask' => 'PhabricatorWorkerDAO', 'PhabricatorWorkerTaskData' => 'PhabricatorWorkerDAO', 'PhabricatorWorkerTaskDetailController' => 'PhabricatorDaemonController', 'PhabricatorXHPASTViewController' => 'PhabricatorController', 'PhabricatorXHPASTViewDAO' => 'PhabricatorLiskDAO', 'PhabricatorXHPASTViewFrameController' => 'PhabricatorXHPASTViewController', 'PhabricatorXHPASTViewFramesetController' => 'PhabricatorXHPASTViewController', 'PhabricatorXHPASTViewInputController' => 'PhabricatorXHPASTViewPanelController', 'PhabricatorXHPASTViewPanelController' => 'PhabricatorXHPASTViewController', 'PhabricatorXHPASTViewParseTree' => 'PhabricatorXHPASTViewDAO', 'PhabricatorXHPASTViewRunController' => 'PhabricatorXHPASTViewController', 'PhabricatorXHPASTViewStreamController' => 'PhabricatorXHPASTViewPanelController', 'PhabricatorXHPASTViewTreeController' => 'PhabricatorXHPASTViewPanelController', 'PhabricatorXHProfController' => 'PhabricatorController', 'PhabricatorXHProfProfileController' => 'PhabricatorXHProfController', 'PhabricatorXHProfProfileSymbolView' => 'AphrontView', 'PhabricatorXHProfProfileTopLevelView' => 'AphrontView', ), 'requires_interface' => array( ), )); diff --git a/src/applications/maniphest/controller/tasklist/ManiphestTaskListController.php b/src/applications/maniphest/controller/tasklist/ManiphestTaskListController.php index 72250e2d1a..0c0dee7d1f 100644 --- a/src/applications/maniphest/controller/tasklist/ManiphestTaskListController.php +++ b/src/applications/maniphest/controller/tasklist/ManiphestTaskListController.php @@ -1,510 +1,474 @@ view = idx($data, 'view'); } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $uri = $request->getRequestURI(); if ($request->isFormPost()) { - $phid_arr = $request->getArr('view_user'); - $view_target = head($phid_arr); - return id(new AphrontRedirectResponse()) - ->setURI($request->getRequestURI()->alter('phid', $view_target)); - } + // Redirect to GET so URIs can be copy/pasted. + + $user_phids = $request->getArr('set_users'); + $proj_phids = $request->getArr('set_projects'); + $user_phids = implode(',', $user_phids); + $proj_phids = implode(',', $proj_phids); + $user_phids = nonempty($user_phids, null); + $proj_phids = nonempty($proj_phids, null); + $uri = $request->getRequestURI() + ->alter('users', $user_phids) + ->alter('projects', $proj_phids); + + return id(new AphrontRedirectResponse())->setURI($uri); + } $views = array( 'User Tasks', 'action' => 'Assigned', 'created' => 'Created', 'triage' => 'Need Triage', -// 'touched' => 'Touched', '
', 'All Tasks', 'alltriage' => 'Need Triage', - 'unassigned' => 'Unassigned', 'all' => 'All Tasks', ); if (empty($views[$this->view])) { $this->view = 'action'; } $has_filter = array( 'action' => true, 'created' => true, 'triage' => true, ); $nav = new AphrontSideNavView(); foreach ($views as $view => $name) { if (is_integer($view)) { $nav->addNavItem( phutil_render_tag( 'span', array(), $name)); } else { $uri->setPath('/maniphest/view/'.$view.'/'); $nav->addNavItem( phutil_render_tag( 'a', array( - 'href' => $uri, + 'href' => $uri->alter('page', null), 'class' => ($this->view == $view) ? 'aphront-side-nav-selected' : null, ), phutil_escape_html($name))); } } list($status_map, $status_links) = $this->renderStatusLinks(); list($grouping, $group_links) = $this->renderGroupLinks(); list($order, $order_links) = $this->renderOrderLinks(); - $view_phid = nonempty($request->getStr('phid'), $user->getPHID()); + $user_phids = $request->getStr('users'); + if (strlen($user_phids)) { + $user_phids = explode(',', $user_phids); + } else { + $user_phids = array($user->getPHID()); + } + + $project_phids = $request->getStr('projects'); + if (strlen($project_phids)) { + $project_phids = explode(',', $project_phids); + } else { + $project_phids = array(); + } $page = $request->getInt('page'); $page_size = self::DEFAULT_PAGE_SIZE; list($tasks, $handles, $total_count) = $this->loadTasks( - $view_phid, + $user_phids, + $project_phids, array( 'status' => $status_map, 'group' => $grouping, 'order' => $order, 'offset' => $page, 'limit' => $page_size, )); - $form = id(new AphrontFormView()) - ->setUser($user); + ->setUser($user) + ->setAction($request->getRequestURI()); if (isset($has_filter[$this->view])) { + $tokens = array(); + foreach ($user_phids as $phid) { + $tokens[$phid] = $handles[$phid]->getFullName(); + } $form->appendChild( id(new AphrontFormTokenizerControl()) - ->setLimit(1) ->setDatasource('/typeahead/common/searchowner/') - ->setName('view_user') - ->setLabel('View User') - ->setCaption('Use "upforgrabs" to find unassigned tasks.') - ->setValue( - array( - $view_phid => $handles[$view_phid]->getFullName(), - ))); + ->setName('set_users') + ->setLabel('Users') + ->setValue($tokens)); + } + + $tokens = array(); + foreach ($project_phids as $phid) { + $tokens[$phid] = $handles[$phid]->getFullName(); } + $form->appendChild( + id(new AphrontFormTokenizerControl()) + ->setDatasource('/typeahead/common/projects/') + ->setName('set_projects') + ->setLabel('Projects') + ->setValue($tokens)); $form ->appendChild( id(new AphrontFormToggleButtonsControl()) ->setLabel('Status') ->setValue($status_links)) ->appendChild( id(new AphrontFormToggleButtonsControl()) ->setLabel('Group') ->setValue($group_links)) ->appendChild( id(new AphrontFormToggleButtonsControl()) ->setLabel('Order') ->setValue($order_links)); + $form->appendChild( + id(new AphrontFormSubmitControl()) + ->setValue('Filter Tasks')); + $filter = new AphrontListFilterView(); $filter->addButton( phutil_render_tag( 'a', array( 'href' => '/maniphest/task/create/', 'class' => 'green button', ), 'Create New Task')); $filter->appendChild($form); $nav->appendChild($filter); $have_tasks = false; foreach ($tasks as $group => $list) { if (count($list)) { $have_tasks = true; break; } } require_celerity_resource('maniphest-task-summary-css'); if (!$have_tasks) { $nav->appendChild( '

'. 'No matching tasks.'. '

'); } else { $pager = new AphrontPagerView(); $pager->setURI($request->getRequestURI(), 'page'); $pager->setPageSize($page_size); $pager->setOffset($page); $pager->setCount($total_count); $cur = ($pager->getOffset() + 1); $max = min($pager->getOffset() + $page_size, $total_count); $tot = $total_count; $cur = number_format($cur); $max = number_format($max); $tot = number_format($tot); $nav->appendChild( '
'. "Displaying tasks {$cur} - {$max} of {$tot}.". '
'); foreach ($tasks as $group => $list) { $task_list = new ManiphestTaskListView(); $task_list->setUser($user); $task_list->setTasks($list); $task_list->setHandles($handles); $count = number_format(count($list)); $nav->appendChild( '

'. phutil_escape_html($group).' ('.$count.')'. '

'); $nav->appendChild($task_list); } $nav->appendChild($pager); } return $this->buildStandardPageResponse( $nav, array( 'title' => 'Task List', )); } - private function loadTasks($view_phid, array $dict) { - $phids = array($view_phid); - - $include_upforgrabs = false; - foreach ($phids as $key => $phid) { - if ($phid == ManiphestTaskOwner::OWNER_UP_FOR_GRABS) { - unset($phids[$key]); - $include_upforgrabs = true; - } - } + private function loadTasks( + array $user_phids, + array $project_phids, + array $dict) { - $task = new ManiphestTask(); - - $argv = array(); + $query = new ManiphestTaskQuery(); + $query->withProjects($project_phids); $status = $dict['status']; if (!empty($status['open']) && !empty($status['closed'])) { - $status_clause = '1 = 1'; + $query->withStatus(ManiphestTaskQuery::STATUS_ANY); } else if (!empty($status['open'])) { - $status_clause = 'status = %d'; - $argv[] = 0; + $query->withStatus(ManiphestTaskQuery::STATUS_OPEN); } else { - $status_clause = 'status > %d'; - $argv[] = 0; + $query->withStatus(ManiphestTaskQuery::STATUS_CLOSED); } - $extra_clause = '1 = 1'; switch ($this->view) { case 'action': - $parts = array(); - if ($phids) { - $parts[] = 'ownerPHID in (%Ls)'; - $argv[] = $phids; - } - if ($include_upforgrabs) { - $parts[] = 'ownerPHID IS NULL'; - } - $extra_clause = '('.implode(' OR ', $parts).')'; + $query->withOwners($user_phids); break; case 'created': - $parts = array(); - if ($phids) { - $parts[] = 'authorPHID in (%Ls)'; - $argv[] = $phids; - } - if ($include_upforgrabs) { - // This should be impossible since every task is supposed to have a - // valid author, but we might as well run the query. - $parts[] = 'authorPHID IS NULL'; - } - $extra_clause = '('.implode(' OR ', $parts).')'; + $query->withAuthors($user_phids); break; case 'triage': - $parts = array(); - if ($phids) { - $parts[] = 'ownerPHID in (%Ls)'; - $argv[] = $phids; - } - if ($include_upforgrabs) { - $parts[] = 'ownerPHID IS NULL'; - } - $extra_clause = '('.implode(' OR ', $parts).') AND priority = %d'; - $argv[] = ManiphestTaskPriority::PRIORITY_TRIAGE; + $query->withOwners($user_phids); + $query->withPriority(ManiphestTaskPriority::PRIORITY_TRIAGE); break; case 'alltriage': - $extra_clause = 'priority = %d'; - $argv[] = ManiphestTaskPriority::PRIORITY_TRIAGE; - break; - case 'unassigned': - $extra_clause = 'ownerPHID is NULL'; + $query->withPriority(ManiphestTaskPriority::PRIORITY_TRIAGE); break; case 'all': break; } - $order = array(); - switch ($dict['group']) { - case 'priority': - $order[] = 'priority'; - break; - case 'owner': - $order[] = 'ownerOrdering'; - break; - case 'status': - $order[] = 'status'; - break; - } - - switch ($dict['order']) { - case 'priority': - $order[] = 'priority'; - $order[] = 'dateModified'; - break; - case 'created': - $order[] = 'id'; - break; - default: - $order[] = 'dateModified'; - break; - } - - $order = array_unique($order); - - foreach ($order as $k => $column) { - switch ($column) { - case 'ownerOrdering': - $order[$k] = "{$column} ASC"; - break; - default: - $order[$k] = "{$column} DESC"; - break; - } - } - - $order = implode(', ', $order); - - $offset = (int)idx($dict, 'offset', 0); - $limit = (int)idx($dict, 'limit', self::DEFAULT_PAGE_SIZE); - - $sql = "SELECT SQL_CALC_FOUND_ROWS * FROM %T WHERE ". - "({$status_clause}) AND ({$extra_clause}) ORDER BY {$order} ". - "LIMIT {$offset}, {$limit}"; - - array_unshift($argv, $task->getTableName()); - - $conn = $task->establishConnection('r'); - $data = vqueryfx_all($conn, $sql, $argv); + $order_map = array( + 'priority' => ManiphestTaskQuery::ORDER_PRIORITY, + 'created' => ManiphestTaskQuery::ORDER_CREATED, + ); + $query->setOrderBy( + idx( + $order_map, + $dict['order'], + ManiphestTaskQuery::ORDER_MODIFIED)); + + $group_map = array( + 'priority' => ManiphestTaskQuery::GROUP_PRIORITY, + 'owner' => ManiphestTaskQuery::GROUP_OWNER, + 'status' => ManiphestTaskQuery::GROUP_STATUS, + ); + $query->setGroupBy( + idx( + $group_map, + $dict['group'], + ManiphestTaskQuery::GROUP_NONE)); - $total_row_count = queryfx_one($conn, 'SELECT FOUND_ROWS() N'); - $total_row_count = $total_row_count['N']; + $query->setCalculateRows(true); + $query->setLimit($dict['limit']); + $query->setOffset($dict['offset']); - $data = $task->loadAllFromArray($data); + $data = $query->execute(); + $total_row_count = $query->getRowCount(); $handle_phids = mpull($data, 'getOwnerPHID'); - $handle_phids[] = $view_phid; + $handle_phids = array_merge($handle_phids, $project_phids, $user_phids); $handles = id(new PhabricatorObjectHandleData($handle_phids)) ->loadHandles(); switch ($dict['group']) { case 'priority': $data = mgroup($data, 'getPriority'); krsort($data); // If we have invalid priorities, they'll all map to "???". Merge // arrays to prevent them from overwriting each other. $out = array(); foreach ($data as $pri => $tasks) { $out[ManiphestTaskPriority::getTaskPriorityName($pri)][] = $tasks; } foreach ($out as $pri => $tasks) { $out[$pri] = array_mergev($tasks); } $data = $out; break; case 'status': $data = mgroup($data, 'getStatus'); ksort($data); $out = array(); foreach ($data as $status => $tasks) { $out[ManiphestTaskStatus::getTaskStatusFullName($status)] = $tasks; } $data = $out; break; case 'owner': $data = mgroup($data, 'getOwnerPHID'); $out = array(); foreach ($data as $phid => $tasks) { if ($phid) { $out[$handles[$phid]->getFullName()] = $tasks; } else { $out['Unassigned'] = $tasks; } } if (isset($out['Unassigned'])) { // If any tasks are unassigned, move them to the front of the list. $data = array('Unassigned' => $out['Unassigned']) + $out; } else { $data = $out; } ksort($data); break; default: $data = array( 'Tasks' => $data, ); break; } return array($data, $handles, $total_row_count); } public function renderStatusLinks() { $request = $this->getRequest(); $statuses = array( 'o' => array('open' => true), 'c' => array('closed' => true), 'oc' => array('open' => true, 'closed' => true), ); $status = $request->getStr('s'); if (empty($statuses[$status])) { $status = 'o'; } $button_names = array( 'Open' => 'o', 'Closed' => 'c', 'All' => 'oc', ); $status_links = $this->renderFilterLinks($button_names, $status, 's'); return array($statuses[$status], $status_links); } public function renderOrderLinks() { $request = $this->getRequest(); $order = $request->getStr('o'); $orders = array( 'u' => 'updated', 'c' => 'created', 'p' => 'priority', ); if (empty($orders[$order])) { $order = 'p'; } $order_by = $orders[$order]; $order_names = array( 'Priority' => 'p', 'Updated' => 'u', 'Created' => 'c', ); $order_links = $this->renderFilterLinks($order_names, $order, 'o'); return array($order_by, $order_links); } public function renderGroupLinks() { $request = $this->getRequest(); $group = $request->getStr('g'); $groups = array( 'n' => 'none', 'p' => 'priority', 's' => 'status', 'o' => 'owner', ); if (empty($groups[$group])) { $group = 'p'; } $group_by = $groups[$group]; $group_names = array( 'Priority' => 'p', 'Owner' => 'o', 'Status' => 's', 'None' => 'n', ); $group_links = $this->renderFilterLinks($group_names, $group, 'g'); return array($group_by, $group_links); } private function renderFilterLinks($filter_map, $selected, $uri_param) { $request = $this->getRequest(); $uri = $request->getRequestURI(); $links = array(); foreach ($filter_map as $name => $value) { if ($value == $selected) { $more = ' toggle-selected toggle-fixed'; $href = null; } else { $more = null; $href = $uri->alter($uri_param, $value); } $links[] = phutil_render_tag( 'a', array( 'class' => 'toggle'.$more, 'href' => $href, ), $name); } return implode("\n", $links); } } diff --git a/src/applications/maniphest/controller/tasklist/__init__.php b/src/applications/maniphest/controller/tasklist/__init__.php index 16e6296085..28c7ec5a4c 100644 --- a/src/applications/maniphest/controller/tasklist/__init__.php +++ b/src/applications/maniphest/controller/tasklist/__init__.php @@ -1,30 +1,29 @@ authorPHIDs = $authors; + return $this; + } + + public function withOwners(array $owners) { + $this->includeUnowned = false; + foreach ($owners as $k => $phid) { + if ($phid == ManiphestTaskOwner::OWNER_UP_FOR_GRABS) { + $this->includeUnowned = true; + unset($owners[$k]); + break; + } + } + $this->ownerPHIDs = $owners; + return $this; + } + + public function withProjects(array $projects) { + $this->projectPHIDs = $projects; + return $this; + } + + public function withStatus($status) { + $this->status = $status; + return $this; + } + + public function withPriority($priority) { + $this->priority = $priority; + return $this; + } + + public function setGroupBy($group) { + $this->groupBy = $group; + return $this; + } + + public function setOrderBy($order) { + $this->orderBy = $order; + return $this; + } + + public function setLimit($limit) { + $this->limit = $limit; + return $this; + } + + public function setOffset($offset) { + $this->offset = $offset; + return $this; + } + + public function setCalculateRows($calculate_rows) { + $this->calculateRows = $calculate_rows; + return $this; + } + + public function getRowCount() { + if ($this->rowCount === null) { + throw new Exception( + "You must execute a query with setCalculateRows() before you can ". + "retrieve a row count."); + } + return $this->rowCount; + } + + public function execute() { + + $task_dao = new ManiphestTask(); + $conn = $task_dao->establishConnection('r'); + + if ($this->calculateRows) { + $calc = 'SQL_CALC_FOUND_ROWS'; + } else { + $calc = ''; + } + + $where = array(); + $where[] = $this->buildStatusWhereClause($conn); + $where[] = $this->buildPriorityWhereClause($conn); + $where[] = $this->buildAuthorWhereClause($conn); + $where[] = $this->buildOwnerWhereClause($conn); + $where[] = $this->buildProjectWhereClause($conn); + + $where = array_filter($where); + if ($where) { + $where = 'WHERE ('.implode(') AND (', $where).')'; + } else { + $where = ''; + } + + $join = array(); + $join[] = $this->buildProjectJoinClause($conn); + + $join = array_filter($join); + if ($join) { + $join = implode(' ', $join); + } else { + $join = ''; + } + + $having = ''; + $count = ''; + $group = ''; + if (count($this->projectPHIDs) > 1) { + + // If we're searching for more than one project: + // - We'll get multiple rows for tasks when they join the project table + // multiple times. We use GROUP BY to make them distinct again. + // - We want to treat the query as an intersection query, not a union + // query. We sum the project count and require it be the same as the + // number of projects we're searching for. + + $group = 'GROUP BY task.id'; + $count = ', COUNT(1) projectCount'; + $having = qsprintf( + $conn, + 'HAVING projectCount = %d', + count($this->projectPHIDs)); + } + + $order = $this->buildOrderClause($conn); + + $offset = (int)nonempty($this->offset, 0); + $limit = (int)nonempty($this->limit, self::DEFAULT_PAGE_SIZE); + + $data = queryfx_all( + $conn, + 'SELECT %Q * %Q FROM %T task %Q %Q %Q %Q %Q LIMIT %d, %d', + $calc, + $count, + $task_dao->getTableName(), + $join, + $where, + $group, + $having, + $order, + $offset, + $limit); + + if ($this->calculateRows) { + $count = queryfx_one( + $conn, + 'SELECT FOUND_ROWS() N'); + $this->rowCount = $count['N']; + } else { + $this->rowCount = null; + } + + return $task_dao->loadAllFromArray($data); + } + + private function buildStatusWhereClause($conn) { + switch ($this->status) { + case self::STATUS_ANY: + return null; + case self::STATUS_OPEN: + return 'status = 0'; + case self::STATUS_CLOSED: + return 'status > 0'; + default: + throw new Exception("Unknown status query '{$this->status}'!"); + } + } + + private function buildPriorityWhereClause($conn) { + if ($this->priority === null) { + return null; + } + + return qsprintf( + $conn, + 'priority = %d', + $this->priority); + } + + private function buildAuthorWhereClause($conn) { + if (!$this->authorPHIDs) { + return null; + } + + return qsprintf( + $conn, + 'authorPHID in (%Ls)', + $this->authorPHIDs); + } + + private function buildOwnerWhereClause($conn) { + if (!$this->ownerPHIDs) { + if ($this->includeUnowned === null) { + return null; + } else if ($this->includeUnowned) { + return qsprintf( + $conn, + 'ownerPHID IS NULL'); + } else { + return qsprintf( + $conn, + 'ownerPHID IS NOT NULL'); + } + } + + if ($this->includeUnowned) { + return qsprintf( + $conn, + 'ownerPHID IN (%Ls) OR ownerPHID IS NULL', + $this->ownerPHIDs); + } else { + return qsprintf( + $conn, + 'ownerPHID IN (%Ls)', + $this->ownerPHIDs); + } + } + + private function buildProjectWhereClause($conn) { + if (!$this->projectPHIDs) { + return null; + } + + return qsprintf( + $conn, + 'project.projectPHID IN (%Ls)', + $this->projectPHIDs); + } + + private function buildProjectJoinClause($conn) { + if (!$this->projectPHIDs) { + return null; + } + + $project_dao = new ManiphestTaskProject(); + return qsprintf( + $conn, + 'JOIN %T project ON project.taskPHID = task.phid', + $project_dao->getTableName()); + } + + private function buildOrderClause($conn) { + $order = array(); + + switch ($this->groupBy) { + case self::GROUP_NONE: + break; + case self::GROUP_PRIORITY: + $order[] = 'priority'; + break; + case self::GROUP_OWNER: + $order[] = 'ownerOrdering'; + break; + case self::GROUP_STATUS: + $order[] = 'status'; + break; + default: + throw new Exception("Unknown group query '{$this->groupBy}'!"); + } + + switch ($this->orderBy) { + case self::ORDER_PRIORITY: + $order[] = 'priority'; + $order[] = 'dateModified'; + break; + case self::ORDER_CREATED: + $order[] = 'id'; + break; + case self::ORDER_MODIFIED: + $order[] = 'dateModified'; + break; + default: + throw new Exception("Unknown order query '{$this->orderBy}'!"); + } + + $order = array_unique($order); + + if (empty($order)) { + return null; + } + + foreach ($order as $k => $column) { + switch ($column) { + case 'ownerOrdering': + $order[$k] = "task.{$column} ASC"; + break; + default: + $order[$k] = "task.{$column} DESC"; + break; + } + } + + return 'ORDER BY '.implode(', ', $order); + } + + +} diff --git a/src/applications/maniphest/query/__init__.php b/src/applications/maniphest/query/__init__.php new file mode 100644 index 0000000000..19b4f9fc8c --- /dev/null +++ b/src/applications/maniphest/query/__init__.php @@ -0,0 +1,18 @@ + true, self::CONFIG_SERIALIZATION => array( 'ccPHIDs' => self::SERIALIZATION_JSON, 'attached' => self::SERIALIZATION_JSON, 'projectPHIDs' => self::SERIALIZATION_JSON, ), ) + parent::getConfiguration(); } public function getAttachedPHIDs($type) { return array_keys(idx($this->attached, $type, array())); } public function generatePHID() { return PhabricatorPHID::generateNewPHID( PhabricatorPHIDConstants::PHID_TYPE_TASK); } public function getCCPHIDs() { return nonempty($this->ccPHIDs, array()); } + public function setProjectPHIDs(array $phids) { + $this->projectPHIDs = $phids; + $this->projectsNeedUpdate = true; + return $this; + } + public function save() { if (!$this->mailKey) { $this->mailKey = sha1(Filesystem::readRandomBytes(20)); } - return parent::save(); + + $result = parent::save(); + + if ($this->projectsNeedUpdate) { + // If we've changed the project PHIDs for this task, update the link + // table. + ManiphestTaskProject::updateTaskProjects($this); + $this->projectsNeedUpdate = false; + } + + return $result; } } diff --git a/src/applications/maniphest/storage/task/__init__.php b/src/applications/maniphest/storage/task/__init__.php index 22a5346f8c..14ba412113 100644 --- a/src/applications/maniphest/storage/task/__init__.php +++ b/src/applications/maniphest/storage/task/__init__.php @@ -1,17 +1,18 @@ Project table, which denormalizes the + * relationship between tasks and projects into a link table so it can be + * efficiently queried. This table is not authoritative; the projectPHIDs field + * of ManiphestTask is. The rows in this table are regenerated when transactions + * are applied to tasks which affected their associated projects. + * + * @group maniphest + */ +final class ManiphestTaskProject extends ManiphestDAO { + + protected $taskPHID; + protected $projectPHID; + + public function getConfiguration() { + return array( + self::CONFIG_IDS => self::IDS_MANUAL, + self::CONFIG_TIMESTAMPS => false, + ); + } + + public static function updateTaskProjects(ManiphestTask $task) { + $dao = new ManiphestTaskProject(); + $conn = $dao->establishConnection('w'); + + $sql = array(); + foreach ($task->getProjectPHIDs() as $project_phid) { + $sql[] = qsprintf( + $conn, + '(%s, %s)', + $task->getPHID(), + $project_phid); + } + + queryfx( + $conn, + 'DELETE FROM %T WHERE taskPHID = %s', + $dao->getTableName(), + $task->getPHID()); + if ($sql) { + queryfx( + $conn, + 'INSERT INTO %T (taskPHID, projectPHID) VALUES %Q', + $dao->getTableName(), + implode(', ', $sql)); + } + } + +} diff --git a/src/applications/maniphest/storage/taskproject/__init__.php b/src/applications/maniphest/storage/taskproject/__init__.php new file mode 100644 index 0000000000..8a3c5b26af --- /dev/null +++ b/src/applications/maniphest/storage/taskproject/__init__.php @@ -0,0 +1,14 @@ +