Page MenuHomePhorge

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/scripts/repository/undo_commits.php b/scripts/repository/undo_commits.php
index e3b4a0694e..a68f9dab98 100755
--- a/scripts/repository/undo_commits.php
+++ b/scripts/repository/undo_commits.php
@@ -1,194 +1,194 @@
#!/usr/bin/env php
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
$root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/__init_script__.php';
$args = new PhutilArgumentParser($argv);
$args->setTagline('reopen reviews accidentally closed by a bad push');
$args->setSynopsis(<<<EOSYNOPSIS
**undo_commits.php** --repository __callsign__ < __commits__
Reopen code reviews accidentally closed by a bad push. If someone
pushes a bunch of commits to a tracked branch that they shouldn't
have, you can pipe in all the commit hashes to this script to
"undo" the damage in Differential after you revert the commits.
To use this script:
1. Identify the commits you want to undo the effects of.
2. Put all their identifiers (commit hashes in git/hg, revision
numbers in svn) into a file, one per line.
3. Pipe that file into this script with relevant arguments.
- 4. Revisions marked "committed" by those commits will be
+ 4. Revisions marked "closed" by those commits will be
restored to their previous state.
EOSYNOPSIS
);
$args->parseStandardArguments();
$args->parse(
array(
array(
'name' => 'repository',
'param' => 'callsign',
'help' => 'Callsign for the repository these commits appear in.',
),
));
$callsign = $args->getArg('repository');
if (!$callsign) {
$args->printHelpAndExit();
}
$repository = id(new PhabricatorRepository())->loadOneWhere(
'callsign = %s',
$callsign);
if (!$repository) {
throw new Exception("No repository with callsign '{$callsign}'!");
}
echo "Reading commit identifiers from stdin...\n";
$identifiers = @file_get_contents('php://stdin');
$identifiers = trim($identifiers);
$identifiers = explode("\n", $identifiers);
echo "Read ".count($identifiers)." commit identifiers.\n";
if (!$identifiers) {
throw new Exception("You must provide commmit identifiers on stdin!");
}
echo "Looking up commits...\n";
$commits = id(new PhabricatorRepositoryCommit())->loadAllWhere(
'repositoryID = %d AND commitIdentifier IN (%Ls)',
$repository->getID(),
$identifiers);
echo "Found ".count($commits)." matching commits.\n";
if (!$commits) {
throw new Exception("None of the commits could be found!");
}
$commit_phids = mpull($commits, 'getPHID', 'getPHID');
-echo "Looking up revisions marked 'committed' by these commits...\n";
+echo "Looking up revisions marked 'closed' by these commits...\n";
$revision_ids = queryfx_all(
id(new DifferentialRevision())->establishConnection('r'),
'SELECT DISTINCT revisionID from %T WHERE commitPHID IN (%Ls)',
DifferentialRevision::TABLE_COMMIT,
$commit_phids);
$revision_ids = ipull($revision_ids, 'revisionID');
echo "Found ".count($revision_ids)." associated revisions.\n";
if (!$revision_ids) {
echo "Done -- nothing to do.\n";
return;
}
-$status_committed = ArcanistDifferentialRevisionStatus::COMMITTED;
+$status_closed = ArcanistDifferentialRevisionStatus::CLOSED;
$revisions = array();
$map = array();
if ($revision_ids) {
foreach ($revision_ids as $revision_id) {
echo "Assessing revision D{$revision_id}...\n";
$revision = id(new DifferentialRevision())->load($revision_id);
- if ($revision->getStatus() != $status_committed) {
- echo "Revision is not 'committed', skipping.\n";
+ if ($revision->getStatus() != $status_closed) {
+ echo "Revision is not 'closed', skipping.\n";
}
$assoc_commits = queryfx_all(
$revision->establishConnection('r'),
'SELECT commitPHID FROM %T WHERE revisionID = %d',
DifferentialRevision::TABLE_COMMIT,
$revision_id);
$assoc_commits = ipull($assoc_commits, 'commitPHID', 'commitPHID');
if (array_diff_key($assoc_commits, $commit_phids)) {
echo "Revision is associated with other commits, skipping.\n";
}
$comments = id(new DifferentialComment())->loadAllWhere(
'revisionID = %d',
$revision_id);
$new_status = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW;
foreach ($comments as $comment) {
switch ($comment->getAction()) {
case DifferentialAction::ACTION_ACCEPT:
$new_status = ArcanistDifferentialRevisionStatus::ACCEPTED;
break;
case DifferentialAction::ACTION_REJECT:
case DifferentialAction::ACTION_RETHINK:
$new_status = ArcanistDifferentialRevisionStatus::NEEDS_REVISION;
break;
case DifferentialAction::ACTION_ABANDON:
$new_status = ArcanistDifferentialRevisionStatus::ABANDONED;
break;
case DifferentialAction::ACTION_RECLAIM:
case DifferentialAction::ACTION_UPDATE:
$new_status = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW;
break;
}
}
$revisions[$revision_id] = $revision;
$map[$revision_id] = $new_status;
}
}
if (!$revisions) {
echo "Done -- nothing to do.\n";
}
echo "Found ".count($revisions)." revisions to update:\n\n";
foreach ($revisions as $id => $revision) {
$old_status = ArcanistDifferentialRevisionStatus::getNameForRevisionStatus(
$revision->getStatus());
$new_status = ArcanistDifferentialRevisionStatus::getNameForRevisionStatus(
$map[$id]);
echo " - D{$id}: ".$revision->getTitle()."\n";
echo " Will update: {$old_status} -> {$new_status}\n\n";
}
$ok = phutil_console_confirm('Apply these changes?');
if (!$ok) {
echo "Aborted.\n";
exit(1);
}
echo "Saving changes...\n";
foreach ($revisions as $id => $revision) {
queryfx(
$revision->establishConnection('r'),
'UPDATE %T SET status = %d WHERE id = %d',
$revision->getTableName(),
$map[$id],
$id);
}
echo "Done.\n";
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index b0b7d09ba0..a289cea27f 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1,1855 +1,1857 @@
<?php
/**
* This file is automatically generated. Use 'phutil_mapper.php' to rebuild it.
* @generated
*/
phutil_register_library_map(array(
'class' =>
array(
'Aphront304Response' => 'aphront/response/304',
'Aphront400Response' => 'aphront/response/400',
'Aphront403Response' => 'aphront/response/403',
'Aphront404Response' => 'aphront/response/404',
'AphrontAjaxResponse' => 'aphront/response/ajax',
'AphrontApplicationConfiguration' => 'aphront/applicationconfiguration',
'AphrontAttachedFileView' => 'view/control/attachedfile',
'AphrontCSRFException' => 'aphront/exception/csrf',
'AphrontCalendarMonthView' => 'applications/calendar/view/month',
'AphrontContextBarView' => 'view/layout/contextbar',
'AphrontController' => 'aphront/controller',
'AphrontCrumbsView' => 'view/layout/crumbs',
'AphrontDatabaseConnection' => 'storage/connection/base',
'AphrontDatabaseTransactionState' => 'storage/transaction',
'AphrontDefaultApplicationConfiguration' => 'aphront/default/configuration',
'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',
'AphrontFormDateControl' => 'view/form/control/date',
'AphrontFormDividerControl' => 'view/form/control/divider',
'AphrontFormDragAndDropUploadControl' => 'view/form/control/draganddropupload',
'AphrontFormFileControl' => 'view/form/control/file',
'AphrontFormInsetView' => 'view/form/inset',
'AphrontFormLayoutView' => 'view/form/layout',
'AphrontFormMarkupControl' => 'view/form/control/markup',
'AphrontFormPasswordControl' => 'view/form/control/password',
'AphrontFormPolicyControl' => 'view/form/control/policy',
'AphrontFormRadioButtonControl' => 'view/form/control/radio',
'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',
'AphrontHTTPSink' => 'aphront/sink/base',
'AphrontHTTPSinkTestCase' => 'aphront/sink/base/__tests__',
'AphrontHeadsupActionListView' => 'view/layout/headsup/actionlist',
'AphrontHeadsupActionView' => 'view/layout/headsup/action',
'AphrontHeadsupView' => 'view/layout/headsup/panel',
'AphrontIDPagerView' => 'view/control/idpager',
'AphrontIsolatedDatabaseConnection' => 'storage/connection/isolated',
'AphrontIsolatedDatabaseConnectionTestCase' => 'storage/connection/isolated/__tests__',
'AphrontIsolatedHTTPSink' => 'aphront/sink/test',
'AphrontJSONResponse' => 'aphront/response/json',
'AphrontJavelinView' => 'view/javelin-view',
'AphrontKeyboardShortcutsAvailableView' => 'view/widget/keyboardshortcuts',
'AphrontListFilterView' => 'view/layout/listfilter',
'AphrontMiniPanelView' => 'view/layout/minipanel',
'AphrontMySQLDatabaseConnection' => 'storage/connection/mysql/mysql',
'AphrontMySQLDatabaseConnectionBase' => 'storage/connection/mysql/base',
'AphrontMySQLDatabaseConnectionTestCase' => 'storage/connection/mysql/__tests__',
'AphrontMySQLiDatabaseConnection' => 'storage/connection/mysql/mysqli',
'AphrontNullView' => 'view/null',
'AphrontPHPHTTPSink' => 'aphront/sink/php',
'AphrontPageView' => 'view/page/base',
'AphrontPagerView' => 'view/control/pager',
'AphrontPanelView' => 'view/layout/panel',
'AphrontPlainTextResponse' => 'aphront/response/plaintext',
'AphrontProxyResponse' => 'aphront/response/proxy',
'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',
'AphrontQuerySchemaException' => 'storage/exception/schema',
'AphrontRedirectException' => 'aphront/exception/redirect',
'AphrontRedirectResponse' => 'aphront/response/redirect',
'AphrontReloadResponse' => 'aphront/response/reload',
'AphrontRequest' => 'aphront/request',
'AphrontRequestFailureView' => 'view/page/failure',
'AphrontRequestTestCase' => 'aphront/request/__tests__',
'AphrontResponse' => 'aphront/response/base',
'AphrontScopedUnguardedWriteCapability' => 'aphront/writeguard/scopeguard',
'AphrontSideNavFilterView' => 'view/layout/sidenavfilter',
'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',
'AphrontWriteGuard' => 'aphront/writeguard',
'CelerityAPI' => 'infrastructure/celerity/api',
'CelerityResourceController' => 'infrastructure/celerity/controller',
'CelerityResourceGraph' => 'infrastructure/celerity/graph',
'CelerityResourceMap' => 'infrastructure/celerity/map',
'CelerityResourceTransformer' => 'infrastructure/celerity/transformer',
'CelerityResourceTransformerTestCase' => 'infrastructure/celerity/transformer/__tests__',
'CelerityStaticResourceResponse' => 'infrastructure/celerity/response',
'ConduitAPIMethod' => 'applications/conduit/method/base',
'ConduitAPIRequest' => 'applications/conduit/protocol/request',
'ConduitAPIResponse' => 'applications/conduit/protocol/response',
'ConduitAPI_arcanist_Method' => 'applications/conduit/method/arcanist/base',
'ConduitAPI_arcanist_projectinfo_Method' => 'applications/conduit/method/arcanist/projectinfo',
'ConduitAPI_audit_Method' => 'applications/conduit/method/audit/base',
'ConduitAPI_audit_query_Method' => 'applications/conduit/method/audit/query',
'ConduitAPI_chatlog_Method' => 'applications/conduit/method/chatlog/base',
'ConduitAPI_chatlog_query_Method' => 'applications/conduit/method/chatlog/query',
'ConduitAPI_chatlog_record_Method' => 'applications/conduit/method/chatlog/record',
'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_close_Method' => 'applications/conduit/method/differential/close',
'ConduitAPI_differential_createcomment_Method' => 'applications/conduit/method/differential/createcomment',
'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_getrevisioncomments_Method' => 'applications/conduit/method/differential/getrevisioncomments',
'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_query_Method' => 'applications/conduit/method/differential/query',
'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_findsymbols_Method' => 'applications/conduit/method/diffusion/findsymbols',
'ConduitAPI_diffusion_getcommits_Method' => 'applications/conduit/method/diffusion/getcommits',
'ConduitAPI_diffusion_getrecentcommitsbypath_Method' => 'applications/conduit/method/diffusion/getrecentcommitsbypath',
'ConduitAPI_feed_publish_Method' => 'applications/conduit/method/feed/publish',
'ConduitAPI_feed_query_Method' => 'applications/conduit/method/feed/query',
'ConduitAPI_file_download_Method' => 'applications/conduit/method/file/download',
'ConduitAPI_file_info_Method' => 'applications/conduit/method/file/info',
'ConduitAPI_file_upload_Method' => 'applications/conduit/method/file/upload',
'ConduitAPI_flag_Method' => 'applications/conduit/method/flag/base',
'ConduitAPI_flag_query_Method' => 'applications/conduit/method/flag/query',
'ConduitAPI_macro_Method' => 'applications/conduit/method/macro/base',
'ConduitAPI_macro_query_Method' => 'applications/conduit/method/macro/query',
'ConduitAPI_maniphest_Method' => 'applications/conduit/method/maniphest/base',
'ConduitAPI_maniphest_createtask_Method' => 'applications/conduit/method/maniphest/createtask',
'ConduitAPI_maniphest_find_Method' => 'applications/conduit/method/maniphest/find',
'ConduitAPI_maniphest_gettasktransactions_Method' => 'applications/conduit/method/maniphest/gettasktransactions',
'ConduitAPI_maniphest_info_Method' => 'applications/conduit/method/maniphest/info',
'ConduitAPI_maniphest_update_Method' => 'applications/conduit/method/maniphest/update',
'ConduitAPI_paste_Method' => 'applications/conduit/method/paste/base',
'ConduitAPI_paste_create_Method' => 'applications/conduit/method/paste/create',
'ConduitAPI_paste_info_Method' => 'applications/conduit/method/paste/info',
'ConduitAPI_path_getowners_Method' => 'applications/conduit/method/path/getowners',
'ConduitAPI_phid_Method' => 'applications/conduit/method/phid/base',
'ConduitAPI_phid_info_Method' => 'applications/conduit/method/phid/info',
'ConduitAPI_phid_query_Method' => 'applications/conduit/method/phid/query',
'ConduitAPI_phriction_Method' => 'applications/conduit/method/phriction/base',
'ConduitAPI_phriction_edit_Method' => 'applications/conduit/method/phriction/edit',
'ConduitAPI_phriction_history_Method' => 'applications/conduit/method/phriction/history',
'ConduitAPI_phriction_info_Method' => 'applications/conduit/method/phriction/info',
'ConduitAPI_project_Method' => 'applications/conduit/method/project/base',
'ConduitAPI_project_query_Method' => 'applications/conduit/method/project/query',
'ConduitAPI_remarkup_process_Method' => 'applications/conduit/method/remarkup/process',
'ConduitAPI_slowvote_info_Method' => 'applications/conduit/method/slowvote/info',
'ConduitAPI_user_Method' => 'applications/conduit/method/user/base',
'ConduitAPI_user_find_Method' => 'applications/conduit/method/user/find',
'ConduitAPI_user_info_Method' => 'applications/conduit/method/user/info',
'ConduitAPI_user_whoami_Method' => 'applications/conduit/method/user/whoami',
'ConduitException' => 'applications/conduit/protocol/exception',
'DarkConsoleConfigPlugin' => 'aphront/console/plugin/config',
'DarkConsoleController' => 'aphront/console/controller',
'DarkConsoleCore' => 'aphront/console/core',
'DarkConsoleErrorLogPlugin' => 'aphront/console/plugin/errorlog',
'DarkConsoleErrorLogPluginAPI' => 'aphront/console/plugin/errorlog/api',
'DarkConsoleEventPlugin' => 'aphront/console/plugin/event',
'DarkConsoleEventPluginAPI' => 'aphront/console/plugin/event/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/base',
'DefaultDatabaseConfigurationProvider' => 'applications/base/storage/configuration/default',
'DifferentialAction' => 'applications/differential/constants/action',
'DifferentialActionHasNoEffectException' => 'applications/differential/exception/noeffect',
'DifferentialAddCommentView' => 'applications/differential/view/addcomment',
'DifferentialAffectedPath' => 'applications/differential/storage/affectedpath',
'DifferentialApplyPatchFieldSpecification' => 'applications/differential/field/specification/applypatch',
'DifferentialArcanistProjectFieldSpecification' => 'applications/differential/field/specification/arcanistproject',
'DifferentialAuditorsFieldSpecification' => 'applications/differential/field/specification/auditors',
'DifferentialAuthorFieldSpecification' => 'applications/differential/field/specification/author',
'DifferentialAuxiliaryField' => 'applications/differential/storage/auxiliaryfield',
'DifferentialBlameRevisionFieldSpecification' => 'applications/differential/field/specification/blamerev',
'DifferentialBranchFieldSpecification' => 'applications/differential/field/specification/branch',
'DifferentialCCWelcomeMail' => 'applications/differential/mail/ccwelcome',
'DifferentialCCsFieldSpecification' => 'applications/differential/field/specification/ccs',
'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',
'DifferentialCommitsFieldSpecification' => 'applications/differential/field/specification/commits',
'DifferentialController' => 'applications/differential/controller/base',
'DifferentialDAO' => 'applications/differential/storage/base',
'DifferentialDateCreatedFieldSpecification' => 'applications/differential/field/specification/datecreated',
'DifferentialDateModifiedFieldSpecification' => 'applications/differential/field/specification/datemodified',
'DifferentialDefaultFieldSelector' => 'applications/differential/field/selector/default',
'DifferentialDependenciesFieldSpecification' => 'applications/differential/field/specification/dependencies',
'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',
'DifferentialException' => 'applications/differential/exception/base',
'DifferentialExceptionMail' => 'applications/differential/mail/exception',
'DifferentialExportPatchFieldSpecification' => 'applications/differential/field/specification/exportpatch',
'DifferentialFieldDataNotAvailableException' => 'applications/differential/field/exception/notavailable',
'DifferentialFieldParseException' => 'applications/differential/field/exception/parse',
'DifferentialFieldSelector' => 'applications/differential/field/selector/base',
'DifferentialFieldSpecification' => 'applications/differential/field/specification/base',
'DifferentialFieldSpecificationIncompleteException' => 'applications/differential/field/exception/incomplete',
'DifferentialFieldValidationException' => 'applications/differential/field/exception/validation',
'DifferentialGitSVNIDFieldSpecification' => 'applications/differential/field/specification/gitsvnid',
'DifferentialHostFieldSpecification' => 'applications/differential/field/specification/host',
'DifferentialHunk' => 'applications/differential/storage/hunk',
'DifferentialInlineComment' => 'applications/differential/storage/inlinecomment',
'DifferentialInlineCommentEditController' => 'applications/differential/controller/inlinecommentedit',
'DifferentialInlineCommentEditView' => 'applications/differential/view/inlinecommentedit',
'DifferentialInlineCommentPreviewController' => 'applications/differential/controller/inlinecommentpreview',
'DifferentialInlineCommentView' => 'applications/differential/view/inlinecomment',
'DifferentialLinesFieldSpecification' => 'applications/differential/field/specification/lines',
'DifferentialLintFieldSpecification' => 'applications/differential/field/specification/lint',
'DifferentialLintStatus' => 'applications/differential/constants/lintstatus',
'DifferentialLocalCommitsView' => 'applications/differential/view/localcommits',
'DifferentialMail' => 'applications/differential/mail/base',
'DifferentialManiphestTasksFieldSpecification' => 'applications/differential/field/specification/maniphesttasks',
'DifferentialNewDiffMail' => 'applications/differential/mail/newdiff',
'DifferentialPathFieldSpecification' => 'applications/differential/field/specification/path',
'DifferentialPrimaryPaneView' => 'applications/differential/view/primarypane',
'DifferentialReplyHandler' => 'applications/differential/replyhandler',
'DifferentialRevertPlanFieldSpecification' => 'applications/differential/field/specification/revertplan',
'DifferentialReviewRequestMail' => 'applications/differential/mail/reviewrequest',
'DifferentialReviewedByFieldSpecification' => 'applications/differential/field/specification/reviewedby',
'DifferentialReviewersFieldSpecification' => 'applications/differential/field/specification/reviewers',
'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',
'DifferentialRevisionIDFieldParserTestCase' => 'applications/differential/field/specification/revisionid/__tests__',
'DifferentialRevisionIDFieldSpecification' => 'applications/differential/field/specification/revisionid',
'DifferentialRevisionListController' => 'applications/differential/controller/revisionlist',
'DifferentialRevisionListData' => 'applications/differential/data/revisionlist',
'DifferentialRevisionListView' => 'applications/differential/view/revisionlist',
'DifferentialRevisionQuery' => 'applications/differential/query/revision',
'DifferentialRevisionStatsController' => 'applications/differential/controller/revisionstats',
'DifferentialRevisionStatsView' => 'applications/differential/view/revisionstats',
'DifferentialRevisionStatusFieldSpecification' => 'applications/differential/field/specification/revisionstatus',
'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/revisionupdatehistory',
'DifferentialRevisionViewController' => 'applications/differential/controller/revisionview',
'DifferentialSubscribeController' => 'applications/differential/controller/subscribe',
'DifferentialSummaryFieldSpecification' => 'applications/differential/field/specification/summary',
'DifferentialTasksAttacher' => 'applications/differential/tasks',
'DifferentialTestPlanFieldSpecification' => 'applications/differential/field/specification/testplan',
'DifferentialTitleFieldSpecification' => 'applications/differential/field/specification/title',
'DifferentialUnitFieldSpecification' => 'applications/differential/field/specification/unit',
'DifferentialUnitStatus' => 'applications/differential/constants/unitstatus',
'DifferentialUnitTestResult' => 'applications/differential/constants/unittestresult',
'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',
'DiffusionCommentListView' => 'applications/diffusion/view/commentlist',
'DiffusionCommentView' => 'applications/diffusion/view/comment',
'DiffusionCommitChangeTableView' => 'applications/diffusion/view/commitchangetable',
'DiffusionCommitController' => 'applications/diffusion/controller/commit',
'DiffusionCommitParentsQuery' => 'applications/diffusion/query/parents/base',
'DiffusionContainsQuery' => 'applications/diffusion/query/contains/base',
'DiffusionController' => 'applications/diffusion/controller/base',
'DiffusionDiffController' => 'applications/diffusion/controller/diff',
'DiffusionDiffQuery' => 'applications/diffusion/query/diff/base',
'DiffusionEmptyResultView' => 'applications/diffusion/view/emptyresult',
'DiffusionExternalController' => 'applications/diffusion/controller/external',
'DiffusionFileContent' => 'applications/diffusion/data/filecontent',
'DiffusionFileContentQuery' => 'applications/diffusion/query/filecontent/base',
'DiffusionGitBranchQuery' => 'applications/diffusion/query/branch/git',
'DiffusionGitBranchQueryTestCase' => 'applications/diffusion/query/branch/git/__tests__',
'DiffusionGitBrowseQuery' => 'applications/diffusion/query/browse/git',
'DiffusionGitCommitParentsQuery' => 'applications/diffusion/query/parents/git',
'DiffusionGitContainsQuery' => 'applications/diffusion/query/contains/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',
'DiffusionGitMergedCommitsQuery' => 'applications/diffusion/query/mergedcommits/git',
'DiffusionGitRequest' => 'applications/diffusion/request/git',
'DiffusionGitTagListQuery' => 'applications/diffusion/query/taglist/git',
'DiffusionHistoryController' => 'applications/diffusion/controller/history',
'DiffusionHistoryQuery' => 'applications/diffusion/query/history/base',
'DiffusionHistoryTableView' => 'applications/diffusion/view/historytable',
'DiffusionHomeController' => 'applications/diffusion/controller/home',
'DiffusionInlineCommentController' => 'applications/diffusion/controller/inline',
'DiffusionLastModifiedController' => 'applications/diffusion/controller/lastmodified',
'DiffusionLastModifiedQuery' => 'applications/diffusion/query/lastmodified/base',
'DiffusionMercurialBranchQuery' => 'applications/diffusion/query/branch/mercurial',
'DiffusionMercurialBrowseQuery' => 'applications/diffusion/query/browse/mercurial',
'DiffusionMercurialCommitParentsQuery' => 'applications/diffusion/query/parents/mercurial',
'DiffusionMercurialContainsQuery' => 'applications/diffusion/query/contains/mercurial',
'DiffusionMercurialDiffQuery' => 'applications/diffusion/query/diff/mercurial',
'DiffusionMercurialFileContentQuery' => 'applications/diffusion/query/filecontent/mercurial',
'DiffusionMercurialHistoryQuery' => 'applications/diffusion/query/history/mercurial',
'DiffusionMercurialLastModifiedQuery' => 'applications/diffusion/query/lastmodified/mercurial',
'DiffusionMercurialMergedCommitsQuery' => 'applications/diffusion/query/mergedcommits/mercurial',
'DiffusionMercurialRequest' => 'applications/diffusion/request/mercurial',
'DiffusionMercurialTagListQuery' => 'applications/diffusion/query/taglist/mercurial',
'DiffusionMergedCommitsQuery' => 'applications/diffusion/query/mergedcommits/base',
'DiffusionPathChange' => 'applications/diffusion/data/pathchange',
'DiffusionPathChangeQuery' => 'applications/diffusion/query/pathchange/base',
'DiffusionPathCompleteController' => 'applications/diffusion/controller/pathcomplete',
'DiffusionPathIDQuery' => 'applications/diffusion/query/pathid/base',
'DiffusionPathQuery' => 'applications/diffusion/query/path',
'DiffusionPathQueryTestCase' => 'applications/diffusion/query/pathid/base/__tests__',
'DiffusionPathValidateController' => 'applications/diffusion/controller/pathvalidate',
'DiffusionQuery' => 'applications/diffusion/query/base',
'DiffusionRepositoryController' => 'applications/diffusion/controller/repository',
'DiffusionRepositoryPath' => 'applications/diffusion/data/repositorypath',
'DiffusionRepositoryTag' => 'applications/diffusion/tag',
'DiffusionRequest' => 'applications/diffusion/request/base',
'DiffusionSvnBrowseQuery' => 'applications/diffusion/query/browse/svn',
'DiffusionSvnCommitParentsQuery' => 'applications/diffusion/query/parents/svn',
'DiffusionSvnContainsQuery' => 'applications/diffusion/query/contains/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',
'DiffusionSvnMergedCommitsQuery' => 'applications/diffusion/query/mergedcommits/svn',
'DiffusionSvnRequest' => 'applications/diffusion/request/svn',
'DiffusionSvnTagListQuery' => 'applications/diffusion/query/taglist/svn',
'DiffusionSymbolController' => 'applications/diffusion/controller/symbol',
'DiffusionSymbolQuery' => 'applications/diffusion/query/symbol',
'DiffusionTagListController' => 'applications/diffusion/controller/taglist',
'DiffusionTagListQuery' => 'applications/diffusion/query/taglist/base',
'DiffusionTagListView' => 'applications/diffusion/view/taglist',
'DiffusionURITestCase' => 'applications/diffusion/request/base/__tests__',
'DiffusionView' => 'applications/diffusion/view/base',
'DrydockAllocator' => 'applications/drydock/allocator/resource',
'DrydockAllocatorWorker' => 'applications/drydock/allocator/worker',
'DrydockApacheWebrootBlueprint' => 'applications/drydock/blueprint/webroot/apache',
'DrydockApacheWebrootInterface' => 'applications/drydock/interface/webroot/apache',
'DrydockBlueprint' => 'applications/drydock/blueprint/base',
'DrydockCommandInterface' => 'applications/drydock/interface/command/base',
'DrydockConstants' => 'applications/drydock/constants/base',
'DrydockController' => 'applications/drydock/controller/base',
'DrydockDAO' => 'applications/drydock/storage/base',
'DrydockEC2HostBlueprint' => 'applications/drydock/blueprint/ec2host',
'DrydockInterface' => 'applications/drydock/interface/base',
'DrydockLease' => 'applications/drydock/storage/lease',
'DrydockLeaseListController' => 'applications/drydock/controller/leaselist',
'DrydockLeaseStatus' => 'applications/drydock/constants/leasestatus',
'DrydockLocalCommandInterface' => 'applications/drydock/interface/command/local',
'DrydockLocalHostBlueprint' => 'applications/drydock/blueprint/localhost',
'DrydockLog' => 'applications/drydock/storage/log',
'DrydockLogController' => 'applications/drydock/controller/log',
'DrydockLogQuery' => 'applications/drydock/query/log',
'DrydockPhabricatorApplicationBlueprint' => 'applications/drydock/blueprint/application/phabricator',
'DrydockRemoteHostBlueprint' => 'applications/drydock/blueprint/remotehost',
'DrydockResource' => 'applications/drydock/storage/resource',
'DrydockResourceAllocateController' => 'applications/drydock/controller/resourceallocate',
'DrydockResourceListController' => 'applications/drydock/controller/resourcelist',
'DrydockResourceStatus' => 'applications/drydock/constants/resourcestatus',
'DrydockSSHCommandInterface' => 'applications/drydock/interface/command/ssh',
'DrydockWebrootInterface' => 'applications/drydock/interface/webroot/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',
'HeraldEditLogQuery' => 'applications/herald/query/log',
'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',
'HeraldRuleEdit' => 'applications/herald/storage/edithistory',
'HeraldRuleEditHistoryController' => 'applications/herald/controller/edithistory',
'HeraldRuleEditHistoryView' => 'applications/herald/view/edithistory',
'HeraldRuleListView' => 'applications/herald/view/rulelist',
'HeraldRuleQuery' => 'applications/herald/query/rule',
'HeraldRuleTranscript' => 'applications/herald/storage/transcript/rule',
'HeraldRuleTypeConfig' => 'applications/herald/config/ruletype',
'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',
'JavelinReactorExample' => 'applications/uiexample/examples/reactor',
'JavelinViewExample' => 'applications/uiexample/examples/client',
'JavelinViewExampleServerView' => 'applications/uiexample/examples/client',
'LiskDAO' => 'storage/lisk/dao',
'LiskEphemeralObjectException' => 'storage/lisk/dao',
'LiskIsolationTestCase' => 'storage/lisk/dao/__tests__',
'LiskIsolationTestDAO' => 'storage/lisk/dao/__tests__',
'LiskIsolationTestDAOException' => 'storage/lisk/dao/__tests__',
'ManiphestAction' => 'applications/maniphest/constants/action',
'ManiphestAuxiliaryFieldDefaultSpecification' => 'applications/maniphest/auxiliaryfield/default',
'ManiphestAuxiliaryFieldSpecification' => 'applications/maniphest/auxiliaryfield/base',
'ManiphestAuxiliaryFieldTypeException' => 'applications/maniphest/auxiliaryfield/typeexception',
'ManiphestAuxiliaryFieldValidationException' => 'applications/maniphest/auxiliaryfield/validationexception',
'ManiphestBatchEditController' => 'applications/maniphest/controller/batch',
'ManiphestConstants' => 'applications/maniphest/constants/base',
'ManiphestController' => 'applications/maniphest/controller/base',
'ManiphestDAO' => 'applications/maniphest/storage/base',
'ManiphestDefaultTaskExtensions' => 'applications/maniphest/extensions/task',
'ManiphestExportController' => 'applications/maniphest/controller/export',
'ManiphestReplyHandler' => 'applications/maniphest/replyhandler',
'ManiphestReportController' => 'applications/maniphest/controller/report',
'ManiphestSavedQuery' => 'applications/maniphest/storage/savedquery',
'ManiphestSavedQueryDeleteController' => 'applications/maniphest/controller/savedquerydelete',
'ManiphestSavedQueryEditController' => 'applications/maniphest/controller/savedqueryedit',
'ManiphestSavedQueryListController' => 'applications/maniphest/controller/savedquerylist',
'ManiphestSubpriorityController' => 'applications/maniphest/controller/subpriority',
'ManiphestTask' => 'applications/maniphest/storage/task',
'ManiphestTaskAuxiliaryStorage' => 'applications/maniphest/storage/auxiliary',
'ManiphestTaskDescriptionChangeController' => 'applications/maniphest/controller/descriptionchange',
'ManiphestTaskDescriptionPreviewController' => 'applications/maniphest/controller/descriptionpreview',
'ManiphestTaskDetailController' => 'applications/maniphest/controller/taskdetail',
'ManiphestTaskEditController' => 'applications/maniphest/controller/taskedit',
'ManiphestTaskExtensions' => 'applications/maniphest/extensions/base',
'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',
'ManiphestTaskProjectsView' => 'applications/maniphest/view/taskprojects',
'ManiphestTaskQuery' => 'applications/maniphest/query',
'ManiphestTaskStatus' => 'applications/maniphest/constants/status',
'ManiphestTaskSubscriber' => 'applications/maniphest/storage/subscriber',
'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',
'ManiphestView' => 'applications/maniphest/view/base',
'MetaMTAConstants' => 'applications/metamta/constants/base',
'MetaMTANotificationType' => 'applications/metamta/constants/notificationtype',
'Phabricator404Controller' => 'applications/base/controller/404',
'PhabricatorAphlictTestPageController' => 'applications/notifications/controller/test',
'PhabricatorAuditActionConstants' => 'applications/audit/constants/action',
'PhabricatorAuditAddCommentController' => 'applications/audit/controller/addcomment',
'PhabricatorAuditComment' => 'applications/audit/storage/auditcomment',
'PhabricatorAuditCommentEditor' => 'applications/audit/editor/comment',
'PhabricatorAuditCommitListView' => 'applications/audit/view/commitlist',
'PhabricatorAuditCommitQuery' => 'applications/audit/query/commit',
'PhabricatorAuditCommitStatusConstants' => 'applications/audit/constants/commitstatus',
'PhabricatorAuditController' => 'applications/audit/controller/base',
'PhabricatorAuditDAO' => 'applications/audit/storage/base',
'PhabricatorAuditInlineComment' => 'applications/audit/storage/inlinecommment',
'PhabricatorAuditListController' => 'applications/audit/controller/list',
'PhabricatorAuditListView' => 'applications/audit/view/list',
'PhabricatorAuditPreviewController' => 'applications/audit/controller/preview',
'PhabricatorAuditQuery' => 'applications/audit/query/audit',
'PhabricatorAuditReplyHandler' => 'applications/audit/replyhandler',
'PhabricatorAuditStatusConstants' => 'applications/audit/constants/status',
'PhabricatorAuthController' => 'applications/auth/controller/base',
'PhabricatorCalendarBrowseController' => 'applications/calendar/controller/browse',
'PhabricatorCalendarController' => 'applications/calendar/controller/base',
'PhabricatorChangesetResponse' => 'infrastructure/diff/response',
'PhabricatorChatLogChannelListController' => 'applications/chatlog/controller/channellist',
'PhabricatorChatLogChannelLogController' => 'applications/chatlog/controller/channellog',
'PhabricatorChatLogConstants' => 'applications/chatlog/constants/base',
'PhabricatorChatLogController' => 'applications/chatlog/controller/base',
'PhabricatorChatLogDAO' => 'applications/chatlog/storage/base',
'PhabricatorChatLogEvent' => 'applications/chatlog/storage/event',
'PhabricatorChatLogEventType' => 'applications/chatlog/constants/eventtype',
'PhabricatorChatLogQuery' => 'applications/chatlog/query',
'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',
'PhabricatorContentSource' => 'applications/metamta/contentsource/source',
'PhabricatorContentSourceView' => 'applications/metamta/contentsource/view',
'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',
'PhabricatorDefaultFileStorageEngineSelector' => 'applications/files/engineselector/default',
'PhabricatorDefaultSearchEngineSelector' => 'applications/search/selector/default',
'PhabricatorDifferenceEngine' => 'infrastructure/diff/engine',
'PhabricatorDirectoryCategory' => 'applications/directory/storage/category',
'PhabricatorDirectoryCategoryDeleteController' => 'applications/directory/controller/categorydelete',
'PhabricatorDirectoryCategoryEditController' => 'applications/directory/controller/categoryedit',
'PhabricatorDirectoryCategoryViewController' => 'applications/directory/controller/categoryview',
'PhabricatorDirectoryController' => 'applications/directory/controller/base',
'PhabricatorDirectoryDAO' => 'applications/directory/storage/base',
'PhabricatorDirectoryEditController' => 'applications/directory/controller/edit',
'PhabricatorDirectoryItem' => 'applications/directory/storage/item',
'PhabricatorDirectoryItemDeleteController' => 'applications/directory/controller/itemdelete',
'PhabricatorDirectoryItemEditController' => 'applications/directory/controller/itemedit',
'PhabricatorDirectoryMainController' => 'applications/directory/controller/main',
'PhabricatorDisabledUserController' => 'applications/auth/controller/disabled',
'PhabricatorDraft' => 'applications/draft/storage/draft',
'PhabricatorDraftDAO' => 'applications/draft/storage/base',
'PhabricatorEdgeConfig' => 'infrastructure/edges/constants/config',
'PhabricatorEdgeConstants' => 'infrastructure/edges/constants/base',
'PhabricatorEdgeEditor' => 'infrastructure/edges/editor/edge',
'PhabricatorEdgeQuery' => 'infrastructure/edges/query/edge',
'PhabricatorEmailLoginController' => 'applications/auth/controller/email',
'PhabricatorEmailTokenController' => 'applications/auth/controller/emailtoken',
'PhabricatorEnv' => 'infrastructure/env',
'PhabricatorEnvTestCase' => 'infrastructure/env/__tests__',
'PhabricatorErrorExample' => 'applications/uiexample/examples/error',
'PhabricatorEvent' => 'infrastructure/events/event',
'PhabricatorEventEngine' => 'infrastructure/events/engine',
'PhabricatorEventType' => 'infrastructure/events/constant/type',
'PhabricatorFeedBuilder' => 'applications/feed/builder/feed',
'PhabricatorFeedConstants' => 'applications/feed/constants/base',
'PhabricatorFeedController' => 'applications/feed/controller/base',
'PhabricatorFeedDAO' => 'applications/feed/storage/base',
'PhabricatorFeedPublicStreamController' => 'applications/feed/controller/publicstream',
'PhabricatorFeedQuery' => 'applications/feed/query',
'PhabricatorFeedStory' => 'applications/feed/story/base',
'PhabricatorFeedStoryAudit' => 'applications/feed/story/audit',
'PhabricatorFeedStoryData' => 'applications/feed/storage/story',
'PhabricatorFeedStoryDifferential' => 'applications/feed/story/differential',
'PhabricatorFeedStoryManiphest' => 'applications/feed/story/maniphest',
'PhabricatorFeedStoryPhriction' => 'applications/feed/story/phriction',
'PhabricatorFeedStoryProject' => 'applications/feed/story/project',
'PhabricatorFeedStoryPublisher' => 'applications/feed/publisher',
'PhabricatorFeedStoryReference' => 'applications/feed/storage/storyreference',
'PhabricatorFeedStoryStatus' => 'applications/feed/story/status',
'PhabricatorFeedStoryTypeConstants' => 'applications/feed/constants/story',
'PhabricatorFeedStoryUnknown' => 'applications/feed/story/unknown',
'PhabricatorFeedStoryView' => 'applications/feed/view/story',
'PhabricatorFeedView' => 'applications/feed/view/base',
'PhabricatorFile' => 'applications/files/storage/file',
'PhabricatorFileController' => 'applications/files/controller/base',
'PhabricatorFileDAO' => 'applications/files/storage/base',
'PhabricatorFileDataController' => 'applications/files/controller/data',
'PhabricatorFileDeleteController' => 'applications/files/controller/delete',
'PhabricatorFileDropUploadController' => 'applications/files/controller/dropupload',
'PhabricatorFileImageMacro' => 'applications/files/storage/imagemacro',
'PhabricatorFileInfoController' => 'applications/files/controller/info',
'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',
'PhabricatorFileSideNavView' => 'applications/files/view/sidenav',
'PhabricatorFileStorageBlob' => 'applications/files/storage/storageblob',
'PhabricatorFileStorageConfigurationException' => 'applications/files/exception/configuration',
'PhabricatorFileStorageEngine' => 'applications/files/engine/base',
'PhabricatorFileStorageEngineSelector' => 'applications/files/engineselector/base',
'PhabricatorFileTransformController' => 'applications/files/controller/transform',
'PhabricatorFileUploadController' => 'applications/files/controller/upload',
'PhabricatorFileUploadException' => 'applications/files/exception/upload',
'PhabricatorFileUploadView' => 'applications/files/view/upload',
'PhabricatorFlag' => 'applications/flag/storage/flag',
'PhabricatorFlagColor' => 'applications/flag/constants/color',
'PhabricatorFlagConstants' => 'applications/flag/constants/base',
'PhabricatorFlagController' => 'applications/flag/controller/base',
'PhabricatorFlagDAO' => 'applications/flag/storage/base',
'PhabricatorFlagDeleteController' => 'applications/flag/controller/delete',
'PhabricatorFlagEditController' => 'applications/flag/controller/edit',
'PhabricatorFlagListController' => 'applications/flag/controller/list',
'PhabricatorFlagListView' => 'applications/flag/view/list',
'PhabricatorFlagQuery' => 'applications/flag/query/flag',
'PhabricatorFormExample' => 'applications/uiexample/examples/form',
'PhabricatorGarbageCollectorDaemon' => 'infrastructure/daemon/garbagecollector',
'PhabricatorGoodForNothingWorker' => 'infrastructure/daemon/workers/worker/goodfornothing',
'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/selector',
'PhabricatorHash' => 'infrastructure/util/hash',
'PhabricatorHelpController' => 'applications/help/controller/base',
'PhabricatorHelpKeyboardShortcutController' => 'applications/help/controller/keyboardshortcut',
'PhabricatorIDPagedPolicyQuery' => 'infrastructure/query/policy/idpaged',
'PhabricatorIRCBot' => 'infrastructure/daemon/irc/bot',
'PhabricatorIRCDifferentialNotificationHandler' => 'infrastructure/daemon/irc/handler/differentialnotification',
'PhabricatorIRCHandler' => 'infrastructure/daemon/irc/handler/base',
'PhabricatorIRCLogHandler' => 'infrastructure/daemon/irc/handler/log',
'PhabricatorIRCMacroHandler' => 'infrastructure/daemon/irc/handler/macro',
'PhabricatorIRCMessage' => 'infrastructure/daemon/irc/message',
'PhabricatorIRCObjectNameHandler' => 'infrastructure/daemon/irc/handler/objectname',
'PhabricatorIRCProtocolHandler' => 'infrastructure/daemon/irc/handler/protocol',
'PhabricatorIRCWhatsNewHandler' => 'infrastructure/daemon/irc/handler/whatsnew',
'PhabricatorImageTransformer' => 'applications/files/transform',
'PhabricatorInfrastructureTestCase' => 'infrastructure/__tests__',
'PhabricatorInlineCommentController' => 'infrastructure/diff/controller',
'PhabricatorInlineCommentInterface' => 'infrastructure/diff/interface/inline',
'PhabricatorInlineSummaryView' => 'infrastructure/diff/view/inline',
'PhabricatorJavelinLinter' => 'infrastructure/lint/linter/javelin',
'PhabricatorJumpNavHandler' => 'applications/search/engine/jumpnav',
'PhabricatorLintEngine' => 'infrastructure/lint/engine',
'PhabricatorLiskDAO' => 'applications/base/storage/lisk',
'PhabricatorLocalDiskFileStorageEngine' => 'applications/files/engine/localdisk',
'PhabricatorLocalTimeTestCase' => 'view/utils/__tests__',
'PhabricatorLoginController' => 'applications/auth/controller/login',
'PhabricatorLoginValidateController' => 'applications/auth/controller/validate',
'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',
'PhabricatorMarkupEngine' => 'applications/markup/engine',
'PhabricatorMetaMTAAttachment' => 'applications/metamta/storage/mail',
'PhabricatorMetaMTAController' => 'applications/metamta/controller/base',
'PhabricatorMetaMTADAO' => 'applications/metamta/storage/base',
'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',
'PhabricatorMetaMTAWorker' => 'applications/metamta/worker',
'PhabricatorMySQLFileStorageEngine' => 'applications/files/engine/mysql',
'PhabricatorNotificationsController' => 'applications/notifications/controller/base',
'PhabricatorOAuthClientAuthorization' => 'applications/oauthserver/storage/clientauthorization',
'PhabricatorOAuthClientAuthorizationBaseController' => 'applications/oauthserver/controller/clientauthorization/base',
'PhabricatorOAuthClientAuthorizationDeleteController' => 'applications/oauthserver/controller/clientauthorization/delete',
'PhabricatorOAuthClientAuthorizationEditController' => 'applications/oauthserver/controller/clientauthorization/edit',
'PhabricatorOAuthClientAuthorizationListController' => 'applications/oauthserver/controller/clientauthorization/list',
'PhabricatorOAuthClientAuthorizationQuery' => 'applications/oauthserver/query/clientauthorization',
'PhabricatorOAuthClientBaseController' => 'applications/oauthserver/controller/client/base',
'PhabricatorOAuthClientDeleteController' => 'applications/oauthserver/controller/client/delete',
'PhabricatorOAuthClientEditController' => 'applications/oauthserver/controller/client/edit',
'PhabricatorOAuthClientListController' => 'applications/oauthserver/controller/client/list',
'PhabricatorOAuthClientViewController' => 'applications/oauthserver/controller/client/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',
'PhabricatorOAuthProviderGoogle' => 'applications/auth/oauth/provider/google',
'PhabricatorOAuthProviderPhabricator' => 'applications/auth/oauth/provider/phabricator',
'PhabricatorOAuthRegistrationController' => 'applications/auth/controller/oauthregistration/base',
'PhabricatorOAuthResponse' => 'applications/oauthserver/response',
'PhabricatorOAuthServer' => 'applications/oauthserver/server',
'PhabricatorOAuthServerAccessToken' => 'applications/oauthserver/storage/accesstoken',
'PhabricatorOAuthServerAuthController' => 'applications/oauthserver/controller/auth',
'PhabricatorOAuthServerAuthorizationCode' => 'applications/oauthserver/storage/authorizationcode',
'PhabricatorOAuthServerClient' => 'applications/oauthserver/storage/client',
'PhabricatorOAuthServerClientQuery' => 'applications/oauthserver/query/client',
'PhabricatorOAuthServerController' => 'applications/oauthserver/controller/base',
'PhabricatorOAuthServerDAO' => 'applications/oauthserver/storage/base',
'PhabricatorOAuthServerScope' => 'applications/oauthserver/scope',
'PhabricatorOAuthServerTestCase' => 'applications/oauthserver/server/__tests__',
'PhabricatorOAuthServerTestController' => 'applications/oauthserver/controller/test',
'PhabricatorOAuthServerTokenController' => 'applications/oauthserver/controller/token',
'PhabricatorOAuthUnlinkController' => 'applications/auth/controller/unlink',
'PhabricatorObjectAttachmentEditor' => 'applications/search/editor/attach',
'PhabricatorObjectGraph' => 'applications/phid/graph',
'PhabricatorObjectHandle' => 'applications/phid/handle',
'PhabricatorObjectHandleConstants' => 'applications/phid/handle/const/base',
'PhabricatorObjectHandleData' => 'applications/phid/handle/data',
'PhabricatorObjectHandleStatus' => 'applications/phid/handle/const/status',
'PhabricatorObjectSelectorDialog' => 'view/control/objectselector',
'PhabricatorOffsetPagedQuery' => 'infrastructure/query/offsetpaged',
'PhabricatorOwnerPathQuery' => 'applications/owners/query/path',
'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',
'PhabricatorPHIDConstants' => 'applications/phid/constants',
'PhabricatorPHIDController' => 'applications/phid/controller/base',
'PhabricatorPHIDDAO' => 'applications/phid/storage/base',
'PhabricatorPHIDLookupController' => 'applications/phid/controller/lookup',
'PhabricatorPaste' => 'applications/paste/storage/paste',
'PhabricatorPasteController' => 'applications/paste/controller/base',
'PhabricatorPasteDAO' => 'applications/paste/storage/base',
'PhabricatorPasteListController' => 'applications/paste/controller/list',
'PhabricatorPasteQuery' => 'applications/paste/query/paste',
'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',
'PhabricatorPolicies' => 'applications/policy/constants/policy',
'PhabricatorPolicyCapability' => 'applications/policy/constants/capability',
'PhabricatorPolicyConstants' => 'applications/policy/constants/base',
'PhabricatorPolicyException' => 'applications/policy/exception/base',
'PhabricatorPolicyFilter' => 'applications/policy/filter/policy',
'PhabricatorPolicyInterface' => 'applications/policy/interface/policy',
'PhabricatorPolicyQuery' => 'infrastructure/query/policy/base',
'PhabricatorPolicyTestCase' => 'applications/policy/__tests__',
'PhabricatorPolicyTestObject' => 'applications/policy/__tests__',
'PhabricatorPolicyTestQuery' => 'applications/policy/__tests__',
'PhabricatorProfileHeaderView' => 'view/layout/profileheader',
'PhabricatorProject' => 'applications/project/storage/project',
'PhabricatorProjectAffiliation' => 'applications/project/storage/affiliation',
'PhabricatorProjectConstants' => 'applications/project/constants/base',
'PhabricatorProjectController' => 'applications/project/controller/base',
'PhabricatorProjectCreateController' => 'applications/project/controller/create',
'PhabricatorProjectDAO' => 'applications/project/storage/base',
'PhabricatorProjectEditor' => 'applications/project/editor/project',
'PhabricatorProjectListController' => 'applications/project/controller/list',
'PhabricatorProjectNameCollisionException' => 'applications/project/exception/namecollison',
'PhabricatorProjectProfile' => 'applications/project/storage/profile',
'PhabricatorProjectProfileController' => 'applications/project/controller/profile',
'PhabricatorProjectProfileEditController' => 'applications/project/controller/profileedit',
'PhabricatorProjectQuery' => 'applications/project/query/project',
'PhabricatorProjectStatus' => 'applications/project/constants/status',
'PhabricatorProjectSubproject' => 'applications/project/storage/subproject',
'PhabricatorProjectTransaction' => 'applications/project/storage/transaction',
'PhabricatorProjectTransactionType' => 'applications/project/constants/transaction',
'PhabricatorProjectUpdateController' => 'applications/project/controller/update',
'PhabricatorQuery' => 'infrastructure/query/base',
'PhabricatorRedirectController' => 'applications/base/controller/redirect',
'PhabricatorRefreshCSRFController' => 'applications/auth/controller/refresh',
'PhabricatorRemarkupRuleDifferential' => 'infrastructure/markup/remarkup/markuprule/differential',
'PhabricatorRemarkupRuleDifferentialHandle' => 'infrastructure/markup/remarkup/markuprule/handle/differential',
'PhabricatorRemarkupRuleDiffusion' => 'infrastructure/markup/remarkup/markuprule/diffusion',
'PhabricatorRemarkupRuleEmbedFile' => 'infrastructure/markup/remarkup/markuprule/embedobject',
'PhabricatorRemarkupRuleImageMacro' => 'infrastructure/markup/remarkup/markuprule/imagemacro',
'PhabricatorRemarkupRuleManiphest' => 'infrastructure/markup/remarkup/markuprule/maniphest',
'PhabricatorRemarkupRuleManiphestHandle' => 'infrastructure/markup/remarkup/markuprule/handle/maniphest',
'PhabricatorRemarkupRuleMention' => 'infrastructure/markup/remarkup/markuprule/mention',
'PhabricatorRemarkupRuleObjectHandle' => 'infrastructure/markup/remarkup/markuprule/handle',
'PhabricatorRemarkupRuleObjectName' => 'infrastructure/markup/remarkup/markuprule/objectname',
'PhabricatorRemarkupRulePaste' => 'infrastructure/markup/remarkup/markuprule/paste',
'PhabricatorRemarkupRulePhriction' => 'infrastructure/markup/remarkup/markuprule/phriction',
'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',
'PhabricatorRepositoryAuditRequest' => 'applications/repository/storage/auditrequest',
'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',
'PhabricatorRepositoryCommitOwnersWorker' => 'applications/repository/worker/owner',
'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',
'PhabricatorRepositoryGitCommitDiscoveryDaemonTestCase' => 'applications/repository/daemon/commitdiscovery/git/__tests__',
'PhabricatorRepositoryGitCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/git',
'PhabricatorRepositoryGitFetchDaemon' => 'applications/repository/daemon/gitfetch',
'PhabricatorRepositoryListController' => 'applications/repository/controller/list',
'PhabricatorRepositoryMercurialCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/mercurial',
'PhabricatorRepositoryMercurialCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/mercurial',
'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/mercurial',
'PhabricatorRepositoryMercurialPullDaemon' => 'applications/repository/daemon/mercurialpull',
'PhabricatorRepositoryPullLocalDaemon' => 'applications/repository/daemon/pulllocal',
'PhabricatorRepositoryShortcut' => 'applications/repository/storage/shortcut',
'PhabricatorRepositorySvnCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/svn',
'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/svn',
'PhabricatorRepositorySvnCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/svn',
'PhabricatorRepositorySymbol' => 'applications/repository/storage/symbol',
'PhabricatorRepositoryTestCase' => 'applications/repository/storage/repository/__tests__',
'PhabricatorRepositoryType' => 'applications/repository/constants/repositorytype',
'PhabricatorS3FileStorageEngine' => 'applications/files/engine/s3',
'PhabricatorSQLPatchList' => 'infrastructure/setup/sql',
'PhabricatorScopedEnv' => 'infrastructure/env',
'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',
'PhabricatorSearchEngine' => 'applications/search/engine/base',
'PhabricatorSearchEngineElastic' => 'applications/search/engine/elastic',
'PhabricatorSearchEngineMySQL' => 'applications/search/engine/mysql',
'PhabricatorSearchEngineSelector' => 'applications/search/selector/base',
'PhabricatorSearchField' => 'applications/search/constants/field',
'PhabricatorSearchIndexController' => 'applications/search/controller/index',
'PhabricatorSearchManiphestIndexer' => 'applications/search/index/indexer/maniphest',
'PhabricatorSearchPhrictionIndexer' => 'applications/search/index/indexer/phriction',
'PhabricatorSearchQuery' => 'applications/search/storage/query',
'PhabricatorSearchRelationship' => 'applications/search/constants/relationship',
'PhabricatorSearchResultView' => 'applications/search/view/searchresult',
'PhabricatorSearchScope' => 'applications/search/constants/scope',
'PhabricatorSearchSelectController' => 'applications/search/controller/select',
'PhabricatorSearchUserIndexer' => 'applications/search/index/indexer/user',
'PhabricatorSetup' => 'infrastructure/setup',
'PhabricatorSlowvoteChoice' => 'applications/slowvote/storage/choice',
'PhabricatorSlowvoteComment' => 'applications/slowvote/storage/comment',
'PhabricatorSlowvoteController' => 'applications/slowvote/controller/base',
'PhabricatorSlowvoteCreateController' => 'applications/slowvote/controller/create',
'PhabricatorSlowvoteDAO' => 'applications/slowvote/storage/base',
'PhabricatorSlowvoteListController' => 'applications/slowvote/controller/list',
'PhabricatorSlowvoteOption' => 'applications/slowvote/storage/option',
'PhabricatorSlowvotePoll' => 'applications/slowvote/storage/poll',
'PhabricatorSlowvotePollController' => 'applications/slowvote/controller/poll',
'PhabricatorSlug' => 'infrastructure/util/slug',
'PhabricatorSlugTestCase' => 'infrastructure/util/slug/__tests__',
'PhabricatorSortTableExample' => 'applications/uiexample/examples/sorttable',
'PhabricatorStandardPageView' => 'view/page/standard',
'PhabricatorStatusController' => 'applications/status/base',
'PhabricatorSymbolNameLinter' => 'infrastructure/lint/hook/xhpastsymbolname',
'PhabricatorSyntaxHighlighter' => 'applications/markup/syntax',
'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',
'PhabricatorTransactionView' => 'view/layout/transaction',
'PhabricatorTransformedFile' => 'applications/files/storage/transformed',
'PhabricatorTrivialTestCase' => 'infrastructure/testing/testcase/__tests__',
'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',
'PhabricatorUITooltipExample' => 'applications/uiexample/examples/tooltip',
'PhabricatorUser' => 'applications/people/storage/user',
'PhabricatorUserAccountSettingsPanelController' => 'applications/people/controller/settings/panels/account',
'PhabricatorUserConduitSettingsPanelController' => 'applications/people/controller/settings/panels/conduit',
'PhabricatorUserDAO' => 'applications/people/storage/base',
'PhabricatorUserEmailPreferenceSettingsPanelController' => 'applications/people/controller/settings/panels/emailpref',
'PhabricatorUserEmailSettingsPanelController' => 'applications/people/controller/settings/panels/email',
'PhabricatorUserLog' => 'applications/people/storage/log',
'PhabricatorUserOAuthInfo' => 'applications/people/storage/useroauthinfo',
'PhabricatorUserOAuthSettingsPanelController' => 'applications/people/controller/settings/panels/oauth',
'PhabricatorUserPasswordSettingsPanelController' => 'applications/people/controller/settings/panels/password',
'PhabricatorUserPreferenceSettingsPanelController' => 'applications/people/controller/settings/panels/preferences',
'PhabricatorUserPreferences' => 'applications/people/storage/preferences',
'PhabricatorUserProfile' => 'applications/people/storage/profile',
'PhabricatorUserProfileSettingsPanelController' => 'applications/people/controller/settings/panels/profile',
'PhabricatorUserSSHKey' => 'applications/people/storage/usersshkey',
'PhabricatorUserSSHKeysSettingsPanelController' => 'applications/people/controller/settings/panels/sshkeys',
'PhabricatorUserSearchSettingsPanelController' => 'applications/people/controller/settings/panels/search',
'PhabricatorUserSettingsController' => 'applications/people/controller/settings',
'PhabricatorUserSettingsPanelController' => 'applications/people/controller/settings/panels/base',
'PhabricatorUserTestCase' => 'applications/people/storage/user/__tests__',
'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',
'PhabricatorWorkerTaskUpdateController' => 'applications/daemon/controller/workertaskupdate',
'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',
'PhabricatorXHProfProfileView' => 'applications/xhprof/view/base',
'PhameController' => 'applications/phame/controller/base',
'PhameDAO' => 'applications/phame/storage/base',
'PhameDraftListController' => 'applications/phame/controller/post/list/drafts',
'PhamePost' => 'applications/phame/storage/post',
'PhamePostDeleteController' => 'applications/phame/controller/post/delete',
'PhamePostDetailView' => 'applications/phame/view/postdetail',
'PhamePostEditController' => 'applications/phame/controller/post/edit',
'PhamePostListBaseController' => 'applications/phame/controller/post/list/base',
'PhamePostListController' => 'applications/phame/controller/post/list/posts',
'PhamePostListView' => 'applications/phame/view/postlist',
'PhamePostPreviewController' => 'applications/phame/controller/post/preview',
'PhamePostQuery' => 'applications/phame/query/post',
'PhamePostViewController' => 'applications/phame/controller/post/view',
'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/monthyearexpiry',
'PhortuneStripeBaseController' => 'applications/phortune/stripe/controller/base',
'PhortuneStripePaymentFormView' => 'applications/phortune/stripe/view/paymentform',
'PhortuneStripeTestPaymentFormController' => 'applications/phortune/stripe/controller/testpaymentform',
'PhrictionActionConstants' => 'applications/phriction/constants/action',
'PhrictionChangeType' => 'applications/phriction/constants/changetype',
'PhrictionConstants' => 'applications/phriction/constants/base',
'PhrictionContent' => 'applications/phriction/storage/content',
'PhrictionController' => 'applications/phriction/controller/base',
'PhrictionDAO' => 'applications/phriction/storage/base',
'PhrictionDeleteController' => 'applications/phriction/controller/delete',
'PhrictionDiffController' => 'applications/phriction/controller/diff',
'PhrictionDocument' => 'applications/phriction/storage/document',
'PhrictionDocumentController' => 'applications/phriction/controller/document',
'PhrictionDocumentEditor' => 'applications/phriction/editor/document',
'PhrictionDocumentPreviewController' => 'applications/phriction/controller/documentpreview',
'PhrictionDocumentStatus' => 'applications/phriction/constants/documentstatus',
'PhrictionDocumentTestCase' => 'applications/phriction/storage/document/__tests__',
'PhrictionEditController' => 'applications/phriction/controller/edit',
'PhrictionHistoryController' => 'applications/phriction/controller/history',
'PhrictionListController' => 'applications/phriction/controller/list',
'QueryFormattingTestCase' => 'storage/qsprintf/__tests__',
),
'function' =>
array(
'_phabricator_date_format' => 'view/utils',
'_qsprintf_check_scalar_type' => 'storage/qsprintf',
'_qsprintf_check_type' => 'storage/qsprintf',
'celerity_generate_unique_node_id' => 'infrastructure/celerity/api',
'celerity_get_resource_uri' => '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_local_time' => 'view/utils',
'phabricator_format_relative_time' => 'view/utils',
'phabricator_format_units_generic' => 'view/utils',
'phabricator_on_relative_date' => 'view/utils',
'phabricator_relative_date' => 'view/utils',
'phabricator_render_form' => 'infrastructure/javelin/markup',
'phabricator_time' => 'view/utils',
'phid_get_type' => 'applications/phid/utils',
'phid_group_by_type' => 'applications/phid/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',
'Aphront403Response' => 'AphrontWebpageResponse',
'Aphront404Response' => 'AphrontWebpageResponse',
'AphrontAjaxResponse' => 'AphrontResponse',
'AphrontAttachedFileView' => 'AphrontView',
'AphrontCSRFException' => 'AphrontException',
'AphrontCalendarMonthView' => 'AphrontView',
'AphrontContextBarView' => 'AphrontView',
'AphrontCrumbsView' => 'AphrontView',
'AphrontDefaultApplicationConfiguration' => 'AphrontApplicationConfiguration',
'AphrontDialogResponse' => 'AphrontResponse',
'AphrontDialogView' => 'AphrontView',
'AphrontErrorView' => 'AphrontView',
'AphrontFilePreviewView' => 'AphrontView',
'AphrontFileResponse' => 'AphrontResponse',
'AphrontFormCheckboxControl' => 'AphrontFormControl',
'AphrontFormControl' => 'AphrontView',
'AphrontFormDateControl' => 'AphrontFormControl',
'AphrontFormDividerControl' => 'AphrontFormControl',
'AphrontFormDragAndDropUploadControl' => 'AphrontFormControl',
'AphrontFormFileControl' => 'AphrontFormControl',
'AphrontFormInsetView' => 'AphrontView',
'AphrontFormLayoutView' => 'AphrontView',
'AphrontFormMarkupControl' => 'AphrontFormControl',
'AphrontFormPasswordControl' => 'AphrontFormControl',
'AphrontFormPolicyControl' => 'AphrontFormControl',
'AphrontFormRadioButtonControl' => 'AphrontFormControl',
'AphrontFormRecaptchaControl' => 'AphrontFormControl',
'AphrontFormSelectControl' => 'AphrontFormControl',
'AphrontFormStaticControl' => 'AphrontFormControl',
'AphrontFormSubmitControl' => 'AphrontFormControl',
'AphrontFormTextAreaControl' => 'AphrontFormControl',
'AphrontFormTextControl' => 'AphrontFormControl',
'AphrontFormToggleButtonsControl' => 'AphrontFormControl',
'AphrontFormTokenizerControl' => 'AphrontFormControl',
'AphrontFormView' => 'AphrontView',
'AphrontHTTPSinkTestCase' => 'PhabricatorTestCase',
'AphrontHeadsupActionListView' => 'AphrontView',
'AphrontHeadsupActionView' => 'AphrontView',
'AphrontHeadsupView' => 'AphrontView',
'AphrontIDPagerView' => 'AphrontView',
'AphrontIsolatedDatabaseConnection' => 'AphrontDatabaseConnection',
'AphrontIsolatedDatabaseConnectionTestCase' => 'PhabricatorTestCase',
'AphrontIsolatedHTTPSink' => 'AphrontHTTPSink',
'AphrontJSONResponse' => 'AphrontResponse',
'AphrontJavelinView' => 'AphrontView',
'AphrontKeyboardShortcutsAvailableView' => 'AphrontView',
'AphrontListFilterView' => 'AphrontView',
'AphrontMiniPanelView' => 'AphrontView',
'AphrontMySQLDatabaseConnection' => 'AphrontMySQLDatabaseConnectionBase',
'AphrontMySQLDatabaseConnectionBase' => 'AphrontDatabaseConnection',
'AphrontMySQLDatabaseConnectionTestCase' => 'PhabricatorTestCase',
'AphrontMySQLiDatabaseConnection' => 'AphrontMySQLDatabaseConnectionBase',
'AphrontNullView' => 'AphrontView',
'AphrontPHPHTTPSink' => 'AphrontHTTPSink',
'AphrontPageView' => 'AphrontView',
'AphrontPagerView' => 'AphrontView',
'AphrontPanelView' => 'AphrontView',
'AphrontPlainTextResponse' => 'AphrontResponse',
'AphrontProxyResponse' => 'AphrontResponse',
'AphrontQueryAccessDeniedException' => 'AphrontQueryRecoverableException',
'AphrontQueryConnectionException' => 'AphrontQueryException',
'AphrontQueryConnectionLostException' => 'AphrontQueryRecoverableException',
'AphrontQueryCountException' => 'AphrontQueryException',
'AphrontQueryDuplicateKeyException' => 'AphrontQueryException',
'AphrontQueryObjectMissingException' => 'AphrontQueryException',
'AphrontQueryParameterException' => 'AphrontQueryException',
'AphrontQueryRecoverableException' => 'AphrontQueryException',
'AphrontQuerySchemaException' => 'AphrontQueryException',
'AphrontRedirectException' => 'AphrontException',
'AphrontRedirectResponse' => 'AphrontResponse',
'AphrontReloadResponse' => 'AphrontRedirectResponse',
'AphrontRequestFailureView' => 'AphrontView',
'AphrontRequestTestCase' => 'PhabricatorTestCase',
'AphrontSideNavFilterView' => 'AphrontView',
'AphrontSideNavView' => 'AphrontView',
'AphrontTableView' => 'AphrontView',
'AphrontTokenizerTemplateView' => 'AphrontView',
'AphrontTypeaheadTemplateView' => 'AphrontView',
'AphrontWebpageResponse' => 'AphrontResponse',
'CelerityResourceController' => 'AphrontController',
'CelerityResourceGraph' => 'AbstractDirectedGraph',
'CelerityResourceTransformerTestCase' => 'PhabricatorTestCase',
'ConduitAPI_arcanist_Method' => 'ConduitAPIMethod',
'ConduitAPI_arcanist_projectinfo_Method' => 'ConduitAPI_arcanist_Method',
'ConduitAPI_audit_Method' => 'ConduitAPIMethod',
'ConduitAPI_audit_query_Method' => 'ConduitAPI_audit_Method',
'ConduitAPI_chatlog_Method' => 'ConduitAPIMethod',
'ConduitAPI_chatlog_query_Method' => 'ConduitAPI_chatlog_Method',
'ConduitAPI_chatlog_record_Method' => 'ConduitAPI_chatlog_Method',
'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_close_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_createcomment_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_getrevisioncomments_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_getrevisionfeedback_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_markcommitted_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_parsecommitmessage_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_query_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_setdiffproperty_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_updaterevision_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_updatetaskrevisionassoc_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_updateunitresults_Method' => 'ConduitAPIMethod',
'ConduitAPI_diffusion_findsymbols_Method' => 'ConduitAPIMethod',
'ConduitAPI_diffusion_getcommits_Method' => 'ConduitAPIMethod',
'ConduitAPI_diffusion_getrecentcommitsbypath_Method' => 'ConduitAPIMethod',
'ConduitAPI_feed_publish_Method' => 'ConduitAPIMethod',
'ConduitAPI_feed_query_Method' => 'ConduitAPIMethod',
'ConduitAPI_file_download_Method' => 'ConduitAPIMethod',
'ConduitAPI_file_info_Method' => 'ConduitAPIMethod',
'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod',
'ConduitAPI_flag_Method' => 'ConduitAPIMethod',
'ConduitAPI_flag_query_Method' => 'ConduitAPI_flag_Method',
'ConduitAPI_macro_Method' => 'ConduitAPIMethod',
'ConduitAPI_macro_query_Method' => 'ConduitAPI_macro_Method',
'ConduitAPI_maniphest_Method' => 'ConduitAPIMethod',
'ConduitAPI_maniphest_createtask_Method' => 'ConduitAPI_maniphest_Method',
'ConduitAPI_maniphest_find_Method' => 'ConduitAPI_maniphest_Method',
'ConduitAPI_maniphest_gettasktransactions_Method' => 'ConduitAPI_maniphest_Method',
'ConduitAPI_maniphest_info_Method' => 'ConduitAPI_maniphest_Method',
'ConduitAPI_maniphest_update_Method' => 'ConduitAPI_maniphest_Method',
'ConduitAPI_paste_Method' => 'ConduitAPIMethod',
'ConduitAPI_paste_create_Method' => 'ConduitAPI_paste_Method',
'ConduitAPI_paste_info_Method' => 'ConduitAPI_paste_Method',
'ConduitAPI_path_getowners_Method' => 'ConduitAPIMethod',
'ConduitAPI_phid_Method' => 'ConduitAPIMethod',
'ConduitAPI_phid_info_Method' => 'ConduitAPI_phid_Method',
'ConduitAPI_phid_query_Method' => 'ConduitAPI_phid_Method',
'ConduitAPI_phriction_Method' => 'ConduitAPIMethod',
'ConduitAPI_phriction_edit_Method' => 'ConduitAPI_phriction_Method',
'ConduitAPI_phriction_history_Method' => 'ConduitAPI_phriction_Method',
'ConduitAPI_phriction_info_Method' => 'ConduitAPI_phriction_Method',
'ConduitAPI_project_Method' => 'ConduitAPIMethod',
'ConduitAPI_project_query_Method' => 'ConduitAPI_project_Method',
'ConduitAPI_remarkup_process_Method' => 'ConduitAPIMethod',
'ConduitAPI_slowvote_info_Method' => 'ConduitAPIMethod',
'ConduitAPI_user_Method' => 'ConduitAPIMethod',
'ConduitAPI_user_find_Method' => 'ConduitAPI_user_Method',
'ConduitAPI_user_info_Method' => 'ConduitAPI_user_Method',
'ConduitAPI_user_whoami_Method' => 'ConduitAPI_user_Method',
'DarkConsoleConfigPlugin' => 'DarkConsolePlugin',
'DarkConsoleController' => 'PhabricatorController',
'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin',
'DarkConsoleEventPlugin' => 'DarkConsolePlugin',
'DarkConsoleEventPluginAPI' => 'PhutilEventListener',
'DarkConsoleRequestPlugin' => 'DarkConsolePlugin',
'DarkConsoleServicesPlugin' => 'DarkConsolePlugin',
'DarkConsoleXHProfPlugin' => 'DarkConsolePlugin',
'DifferentialActionHasNoEffectException' => 'DifferentialException',
'DifferentialAddCommentView' => 'AphrontView',
'DifferentialAffectedPath' => 'DifferentialDAO',
'DifferentialApplyPatchFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialArcanistProjectFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialAuditorsFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialAuthorFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialAuxiliaryField' => 'DifferentialDAO',
'DifferentialBlameRevisionFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialBranchFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail',
'DifferentialCCsFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialChangeset' => 'DifferentialDAO',
'DifferentialChangesetDetailView' => 'AphrontView',
'DifferentialChangesetListView' => 'AphrontView',
'DifferentialChangesetViewController' => 'DifferentialController',
'DifferentialComment' => 'DifferentialDAO',
'DifferentialCommentMail' => 'DifferentialMail',
'DifferentialCommentPreviewController' => 'DifferentialController',
'DifferentialCommentSaveController' => 'DifferentialController',
'DifferentialCommitsFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialController' => 'PhabricatorController',
'DifferentialDAO' => 'PhabricatorLiskDAO',
'DifferentialDateCreatedFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialDateModifiedFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialDefaultFieldSelector' => 'DifferentialFieldSelector',
'DifferentialDependenciesFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialDiff' => 'DifferentialDAO',
'DifferentialDiffContentMail' => 'DifferentialMail',
'DifferentialDiffCreateController' => 'DifferentialController',
'DifferentialDiffProperty' => 'DifferentialDAO',
'DifferentialDiffTableOfContentsView' => 'AphrontView',
'DifferentialDiffViewController' => 'DifferentialController',
'DifferentialExceptionMail' => 'DifferentialMail',
'DifferentialExportPatchFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialGitSVNIDFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialHostFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialHunk' => 'DifferentialDAO',
'DifferentialInlineComment' => 'DifferentialDAO',
'DifferentialInlineCommentEditController' => 'PhabricatorInlineCommentController',
'DifferentialInlineCommentEditView' => 'AphrontView',
'DifferentialInlineCommentPreviewController' => 'DifferentialController',
'DifferentialInlineCommentView' => 'AphrontView',
'DifferentialLinesFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialLintFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialLocalCommitsView' => 'AphrontView',
'DifferentialManiphestTasksFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialNewDiffMail' => 'DifferentialReviewRequestMail',
'DifferentialPathFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialPrimaryPaneView' => 'AphrontView',
'DifferentialReplyHandler' => 'PhabricatorMailReplyHandler',
'DifferentialRevertPlanFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialReviewRequestMail' => 'DifferentialMail',
'DifferentialReviewedByFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialReviewersFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialRevision' => 'DifferentialDAO',
'DifferentialRevisionCommentListView' => 'AphrontView',
'DifferentialRevisionCommentView' => 'AphrontView',
'DifferentialRevisionDetailView' => 'AphrontView',
'DifferentialRevisionEditController' => 'DifferentialController',
'DifferentialRevisionIDFieldParserTestCase' => 'PhabricatorTestCase',
'DifferentialRevisionIDFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialRevisionListController' => 'DifferentialController',
'DifferentialRevisionListView' => 'AphrontView',
'DifferentialRevisionStatsController' => 'DifferentialController',
'DifferentialRevisionStatsView' => 'AphrontView',
'DifferentialRevisionStatusFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialRevisionUpdateHistoryView' => 'AphrontView',
'DifferentialRevisionViewController' => 'DifferentialController',
'DifferentialSubscribeController' => 'DifferentialController',
'DifferentialSummaryFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialTestPlanFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialTitleFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialUnitFieldSpecification' => 'DifferentialFieldSpecification',
'DiffusionBranchTableView' => 'DiffusionView',
'DiffusionBrowseController' => 'DiffusionController',
'DiffusionBrowseFileController' => 'DiffusionController',
'DiffusionBrowseTableView' => 'DiffusionView',
'DiffusionChangeController' => 'DiffusionController',
'DiffusionCommentListView' => 'AphrontView',
'DiffusionCommentView' => 'AphrontView',
'DiffusionCommitChangeTableView' => 'DiffusionView',
'DiffusionCommitController' => 'DiffusionController',
'DiffusionCommitParentsQuery' => 'DiffusionQuery',
'DiffusionContainsQuery' => 'DiffusionQuery',
'DiffusionController' => 'PhabricatorController',
'DiffusionDiffController' => 'DiffusionController',
'DiffusionDiffQuery' => 'DiffusionQuery',
'DiffusionEmptyResultView' => 'DiffusionView',
'DiffusionExternalController' => 'DiffusionController',
'DiffusionFileContentQuery' => 'DiffusionQuery',
'DiffusionGitBranchQuery' => 'DiffusionBranchQuery',
'DiffusionGitBranchQueryTestCase' => 'PhabricatorTestCase',
'DiffusionGitBrowseQuery' => 'DiffusionBrowseQuery',
'DiffusionGitCommitParentsQuery' => 'DiffusionCommitParentsQuery',
'DiffusionGitContainsQuery' => 'DiffusionContainsQuery',
'DiffusionGitDiffQuery' => 'DiffusionDiffQuery',
'DiffusionGitFileContentQuery' => 'DiffusionFileContentQuery',
'DiffusionGitHistoryQuery' => 'DiffusionHistoryQuery',
'DiffusionGitLastModifiedQuery' => 'DiffusionLastModifiedQuery',
'DiffusionGitMergedCommitsQuery' => 'DiffusionMergedCommitsQuery',
'DiffusionGitRequest' => 'DiffusionRequest',
'DiffusionGitTagListQuery' => 'DiffusionTagListQuery',
'DiffusionHistoryController' => 'DiffusionController',
'DiffusionHistoryQuery' => 'DiffusionQuery',
'DiffusionHistoryTableView' => 'DiffusionView',
'DiffusionHomeController' => 'DiffusionController',
'DiffusionInlineCommentController' => 'PhabricatorInlineCommentController',
'DiffusionLastModifiedController' => 'DiffusionController',
'DiffusionLastModifiedQuery' => 'DiffusionQuery',
'DiffusionMercurialBranchQuery' => 'DiffusionBranchQuery',
'DiffusionMercurialBrowseQuery' => 'DiffusionBrowseQuery',
'DiffusionMercurialCommitParentsQuery' => 'DiffusionCommitParentsQuery',
'DiffusionMercurialContainsQuery' => 'DiffusionContainsQuery',
'DiffusionMercurialDiffQuery' => 'DiffusionDiffQuery',
'DiffusionMercurialFileContentQuery' => 'DiffusionFileContentQuery',
'DiffusionMercurialHistoryQuery' => 'DiffusionHistoryQuery',
'DiffusionMercurialLastModifiedQuery' => 'DiffusionLastModifiedQuery',
'DiffusionMercurialMergedCommitsQuery' => 'DiffusionMergedCommitsQuery',
'DiffusionMercurialRequest' => 'DiffusionRequest',
'DiffusionMercurialTagListQuery' => 'DiffusionTagListQuery',
'DiffusionMergedCommitsQuery' => 'DiffusionQuery',
'DiffusionPathCompleteController' => 'DiffusionController',
'DiffusionPathQueryTestCase' => 'PhabricatorTestCase',
'DiffusionPathValidateController' => 'DiffusionController',
'DiffusionRepositoryController' => 'DiffusionController',
'DiffusionSvnBrowseQuery' => 'DiffusionBrowseQuery',
'DiffusionSvnCommitParentsQuery' => 'DiffusionCommitParentsQuery',
'DiffusionSvnContainsQuery' => 'DiffusionContainsQuery',
'DiffusionSvnDiffQuery' => 'DiffusionDiffQuery',
'DiffusionSvnFileContentQuery' => 'DiffusionFileContentQuery',
'DiffusionSvnHistoryQuery' => 'DiffusionHistoryQuery',
'DiffusionSvnLastModifiedQuery' => 'DiffusionLastModifiedQuery',
'DiffusionSvnMergedCommitsQuery' => 'DiffusionMergedCommitsQuery',
'DiffusionSvnRequest' => 'DiffusionRequest',
'DiffusionSvnTagListQuery' => 'DiffusionTagListQuery',
'DiffusionSymbolController' => 'DiffusionController',
'DiffusionTagListController' => 'DiffusionController',
'DiffusionTagListQuery' => 'DiffusionQuery',
'DiffusionTagListView' => 'DiffusionView',
'DiffusionURITestCase' => 'ArcanistPhutilTestCase',
'DiffusionView' => 'AphrontView',
'DrydockAllocatorWorker' => 'PhabricatorWorker',
'DrydockApacheWebrootBlueprint' => 'DrydockBlueprint',
'DrydockApacheWebrootInterface' => 'DrydockWebrootInterface',
'DrydockCommandInterface' => 'DrydockInterface',
'DrydockController' => 'PhabricatorController',
'DrydockDAO' => 'PhabricatorLiskDAO',
'DrydockEC2HostBlueprint' => 'DrydockRemoteHostBlueprint',
'DrydockLease' => 'DrydockDAO',
'DrydockLeaseListController' => 'DrydockController',
'DrydockLeaseStatus' => 'DrydockConstants',
'DrydockLocalCommandInterface' => 'DrydockCommandInterface',
'DrydockLocalHostBlueprint' => 'DrydockBlueprint',
'DrydockLog' => 'DrydockDAO',
'DrydockLogController' => 'DrydockController',
'DrydockLogQuery' => 'PhabricatorOffsetPagedQuery',
'DrydockPhabricatorApplicationBlueprint' => 'DrydockBlueprint',
'DrydockRemoteHostBlueprint' => 'DrydockBlueprint',
'DrydockResource' => 'DrydockDAO',
'DrydockResourceAllocateController' => 'DrydockController',
'DrydockResourceListController' => 'DrydockController',
'DrydockResourceStatus' => 'DrydockConstants',
'DrydockSSHCommandInterface' => 'DrydockCommandInterface',
'DrydockWebrootInterface' => 'DrydockInterface',
'HeraldAction' => 'HeraldDAO',
'HeraldApplyTranscript' => 'HeraldDAO',
'HeraldCommitAdapter' => 'HeraldObjectAdapter',
'HeraldCondition' => 'HeraldDAO',
'HeraldController' => 'PhabricatorController',
'HeraldDAO' => 'PhabricatorLiskDAO',
'HeraldDeleteController' => 'HeraldController',
'HeraldDifferentialRevisionAdapter' => 'HeraldObjectAdapter',
'HeraldDryRunAdapter' => 'HeraldObjectAdapter',
'HeraldEditLogQuery' => 'PhabricatorOffsetPagedQuery',
'HeraldHomeController' => 'HeraldController',
'HeraldNewController' => 'HeraldController',
'HeraldRule' => 'HeraldDAO',
'HeraldRuleController' => 'HeraldController',
'HeraldRuleEdit' => 'HeraldDAO',
'HeraldRuleEditHistoryController' => 'HeraldController',
'HeraldRuleEditHistoryView' => 'AphrontView',
'HeraldRuleListView' => 'AphrontView',
'HeraldRuleQuery' => 'PhabricatorOffsetPagedQuery',
'HeraldTestConsoleController' => 'HeraldController',
'HeraldTranscript' => 'HeraldDAO',
'HeraldTranscriptController' => 'HeraldController',
'HeraldTranscriptListController' => 'HeraldController',
'JavelinReactorExample' => 'PhabricatorUIExample',
'JavelinViewExample' => 'PhabricatorUIExample',
'JavelinViewExampleServerView' => 'AphrontView',
'LiskIsolationTestCase' => 'PhabricatorTestCase',
'LiskIsolationTestDAO' => 'LiskDAO',
'ManiphestAction' => 'PhrictionConstants',
'ManiphestAuxiliaryFieldDefaultSpecification' => 'ManiphestAuxiliaryFieldSpecification',
'ManiphestBatchEditController' => 'ManiphestController',
'ManiphestController' => 'PhabricatorController',
'ManiphestDAO' => 'PhabricatorLiskDAO',
'ManiphestDefaultTaskExtensions' => 'ManiphestTaskExtensions',
'ManiphestExportController' => 'ManiphestController',
'ManiphestReplyHandler' => 'PhabricatorMailReplyHandler',
'ManiphestReportController' => 'ManiphestController',
'ManiphestSavedQuery' => 'ManiphestDAO',
'ManiphestSavedQueryDeleteController' => 'ManiphestController',
'ManiphestSavedQueryEditController' => 'ManiphestController',
'ManiphestSavedQueryListController' => 'ManiphestController',
'ManiphestSubpriorityController' => 'ManiphestController',
'ManiphestTask' => 'ManiphestDAO',
'ManiphestTaskAuxiliaryStorage' => 'ManiphestDAO',
'ManiphestTaskDescriptionChangeController' => 'ManiphestController',
'ManiphestTaskDescriptionPreviewController' => 'ManiphestController',
'ManiphestTaskDetailController' => 'ManiphestController',
'ManiphestTaskEditController' => 'ManiphestController',
'ManiphestTaskListController' => 'ManiphestController',
'ManiphestTaskListView' => 'ManiphestView',
'ManiphestTaskOwner' => 'ManiphestConstants',
'ManiphestTaskPriority' => 'ManiphestConstants',
'ManiphestTaskProject' => 'ManiphestDAO',
'ManiphestTaskProjectsView' => 'ManiphestView',
'ManiphestTaskStatus' => 'ManiphestConstants',
'ManiphestTaskSubscriber' => 'ManiphestDAO',
'ManiphestTaskSummaryView' => 'ManiphestView',
'ManiphestTransaction' => 'ManiphestDAO',
'ManiphestTransactionDetailView' => 'ManiphestView',
'ManiphestTransactionListView' => 'ManiphestView',
'ManiphestTransactionPreviewController' => 'ManiphestController',
'ManiphestTransactionSaveController' => 'ManiphestController',
'ManiphestTransactionType' => 'ManiphestConstants',
'ManiphestView' => 'AphrontView',
'MetaMTANotificationType' => 'MetaMTAConstants',
'Phabricator404Controller' => 'PhabricatorController',
'PhabricatorAphlictTestPageController' => 'PhabricatorNotificationsController',
'PhabricatorAuditAddCommentController' => 'PhabricatorAuditController',
'PhabricatorAuditComment' => 'PhabricatorAuditDAO',
'PhabricatorAuditCommitListView' => 'AphrontView',
'PhabricatorAuditController' => 'PhabricatorController',
'PhabricatorAuditDAO' => 'PhabricatorLiskDAO',
'PhabricatorAuditInlineComment' => 'PhabricatorAuditDAO',
'PhabricatorAuditListController' => 'PhabricatorAuditController',
'PhabricatorAuditListView' => 'AphrontView',
'PhabricatorAuditPreviewController' => 'PhabricatorAuditController',
'PhabricatorAuditReplyHandler' => 'PhabricatorMailReplyHandler',
'PhabricatorAuthController' => 'PhabricatorController',
'PhabricatorCalendarBrowseController' => 'PhabricatorCalendarController',
'PhabricatorCalendarController' => 'PhabricatorController',
'PhabricatorChangesetResponse' => 'AphrontProxyResponse',
'PhabricatorChatLogChannelListController' => 'PhabricatorChatLogController',
'PhabricatorChatLogChannelLogController' => 'PhabricatorChatLogController',
'PhabricatorChatLogController' => 'PhabricatorController',
'PhabricatorChatLogDAO' => 'PhabricatorLiskDAO',
'PhabricatorChatLogEvent' => 'PhabricatorChatLogDAO',
'PhabricatorChatLogEventType' => 'PhabricatorChatLogConstants',
'PhabricatorChatLogQuery' => 'PhabricatorOffsetPagedQuery',
'PhabricatorConduitAPIController' => 'PhabricatorConduitController',
'PhabricatorConduitCertificateToken' => 'PhabricatorConduitDAO',
'PhabricatorConduitConnectionLog' => 'PhabricatorConduitDAO',
'PhabricatorConduitConsoleController' => 'PhabricatorConduitController',
'PhabricatorConduitController' => 'PhabricatorController',
'PhabricatorConduitDAO' => 'PhabricatorLiskDAO',
'PhabricatorConduitLogController' => 'PhabricatorConduitController',
'PhabricatorConduitMethodCallLog' => 'PhabricatorConduitDAO',
'PhabricatorConduitTokenController' => 'PhabricatorConduitController',
'PhabricatorContentSourceView' => 'AphrontView',
'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',
'PhabricatorDefaultFileStorageEngineSelector' => 'PhabricatorFileStorageEngineSelector',
'PhabricatorDefaultSearchEngineSelector' => 'PhabricatorSearchEngineSelector',
'PhabricatorDirectoryCategory' => 'PhabricatorDirectoryDAO',
'PhabricatorDirectoryCategoryDeleteController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryCategoryEditController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryCategoryViewController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryController' => 'PhabricatorController',
'PhabricatorDirectoryDAO' => 'PhabricatorLiskDAO',
'PhabricatorDirectoryEditController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryItem' => 'PhabricatorDirectoryDAO',
'PhabricatorDirectoryItemDeleteController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryItemEditController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryMainController' => 'PhabricatorDirectoryController',
'PhabricatorDisabledUserController' => 'PhabricatorAuthController',
'PhabricatorDraft' => 'PhabricatorDraftDAO',
'PhabricatorDraftDAO' => 'PhabricatorLiskDAO',
'PhabricatorEdgeConfig' => 'PhabricatorEdgeConstants',
'PhabricatorEdgeQuery' => 'PhabricatorQuery',
'PhabricatorEmailLoginController' => 'PhabricatorAuthController',
'PhabricatorEmailTokenController' => 'PhabricatorAuthController',
'PhabricatorEnvTestCase' => 'PhabricatorTestCase',
'PhabricatorErrorExample' => 'PhabricatorUIExample',
'PhabricatorEvent' => 'PhutilEvent',
'PhabricatorEventType' => 'PhutilEventType',
'PhabricatorFeedController' => 'PhabricatorController',
'PhabricatorFeedDAO' => 'PhabricatorLiskDAO',
'PhabricatorFeedPublicStreamController' => 'PhabricatorFeedController',
'PhabricatorFeedStoryAudit' => 'PhabricatorFeedStory',
'PhabricatorFeedStoryData' => 'PhabricatorFeedDAO',
'PhabricatorFeedStoryDifferential' => 'PhabricatorFeedStory',
'PhabricatorFeedStoryManiphest' => 'PhabricatorFeedStory',
'PhabricatorFeedStoryPhriction' => 'PhabricatorFeedStory',
'PhabricatorFeedStoryProject' => 'PhabricatorFeedStory',
'PhabricatorFeedStoryReference' => 'PhabricatorFeedDAO',
'PhabricatorFeedStoryStatus' => 'PhabricatorFeedStory',
'PhabricatorFeedStoryTypeConstants' => 'PhabricatorFeedConstants',
'PhabricatorFeedStoryUnknown' => 'PhabricatorFeedStory',
'PhabricatorFeedStoryView' => 'PhabricatorFeedView',
'PhabricatorFeedView' => 'AphrontView',
'PhabricatorFile' => 'PhabricatorFileDAO',
'PhabricatorFileController' => 'PhabricatorController',
'PhabricatorFileDAO' => 'PhabricatorLiskDAO',
'PhabricatorFileDataController' => 'PhabricatorFileController',
'PhabricatorFileDeleteController' => 'PhabricatorFileController',
'PhabricatorFileDropUploadController' => 'PhabricatorFileController',
'PhabricatorFileImageMacro' => 'PhabricatorFileDAO',
'PhabricatorFileInfoController' => 'PhabricatorFileController',
'PhabricatorFileListController' => 'PhabricatorFileController',
'PhabricatorFileMacroDeleteController' => 'PhabricatorFileController',
'PhabricatorFileMacroEditController' => 'PhabricatorFileController',
'PhabricatorFileMacroListController' => 'PhabricatorFileController',
'PhabricatorFileProxyController' => 'PhabricatorFileController',
'PhabricatorFileProxyImage' => 'PhabricatorFileDAO',
'PhabricatorFileSideNavView' => 'AphrontView',
'PhabricatorFileStorageBlob' => 'PhabricatorFileDAO',
'PhabricatorFileTransformController' => 'PhabricatorFileController',
'PhabricatorFileUploadController' => 'PhabricatorFileController',
'PhabricatorFileUploadView' => 'AphrontView',
'PhabricatorFlag' => 'PhabricatorFlagDAO',
'PhabricatorFlagColor' => 'PhabricatorFlagConstants',
'PhabricatorFlagController' => 'PhabricatorController',
'PhabricatorFlagDAO' => 'PhabricatorLiskDAO',
'PhabricatorFlagDeleteController' => 'PhabricatorFlagController',
'PhabricatorFlagEditController' => 'PhabricatorFlagController',
'PhabricatorFlagListController' => 'PhabricatorFlagController',
'PhabricatorFlagListView' => 'AphrontView',
'PhabricatorFormExample' => 'PhabricatorUIExample',
'PhabricatorGarbageCollectorDaemon' => 'PhabricatorDaemon',
'PhabricatorGoodForNothingWorker' => 'PhabricatorWorker',
'PhabricatorHelpController' => 'PhabricatorController',
'PhabricatorHelpKeyboardShortcutController' => 'PhabricatorHelpController',
'PhabricatorIDPagedPolicyQuery' => 'PhabricatorPolicyQuery',
'PhabricatorIRCBot' => 'PhabricatorDaemon',
'PhabricatorIRCDifferentialNotificationHandler' => 'PhabricatorIRCHandler',
'PhabricatorIRCLogHandler' => 'PhabricatorIRCHandler',
'PhabricatorIRCMacroHandler' => 'PhabricatorIRCHandler',
'PhabricatorIRCObjectNameHandler' => 'PhabricatorIRCHandler',
'PhabricatorIRCProtocolHandler' => 'PhabricatorIRCHandler',
'PhabricatorIRCWhatsNewHandler' => 'PhabricatorIRCHandler',
'PhabricatorInfrastructureTestCase' => 'PhabricatorTestCase',
'PhabricatorInlineCommentController' => 'PhabricatorController',
'PhabricatorInlineSummaryView' => 'AphrontView',
'PhabricatorJavelinLinter' => 'ArcanistLinter',
'PhabricatorLintEngine' => 'PhutilLintEngine',
'PhabricatorLiskDAO' => 'LiskDAO',
'PhabricatorLocalDiskFileStorageEngine' => 'PhabricatorFileStorageEngine',
'PhabricatorLocalTimeTestCase' => 'PhabricatorTestCase',
'PhabricatorLoginController' => 'PhabricatorAuthController',
'PhabricatorLoginValidateController' => 'PhabricatorAuthController',
'PhabricatorLogoutController' => 'PhabricatorAuthController',
'PhabricatorMailImplementationAmazonSESAdapter' => 'PhabricatorMailImplementationPHPMailerLiteAdapter',
'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'PhabricatorMailImplementationAdapter',
'PhabricatorMailImplementationSendGridAdapter' => 'PhabricatorMailImplementationAdapter',
'PhabricatorMailImplementationTestAdapter' => 'PhabricatorMailImplementationAdapter',
'PhabricatorMetaMTAController' => 'PhabricatorController',
'PhabricatorMetaMTADAO' => 'PhabricatorLiskDAO',
'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',
'PhabricatorMetaMTAWorker' => 'PhabricatorWorker',
'PhabricatorMySQLFileStorageEngine' => 'PhabricatorFileStorageEngine',
'PhabricatorNotificationsController' => 'PhabricatorController',
'PhabricatorOAuthClientAuthorization' => 'PhabricatorOAuthServerDAO',
'PhabricatorOAuthClientAuthorizationBaseController' => 'PhabricatorOAuthServerController',
'PhabricatorOAuthClientAuthorizationDeleteController' => 'PhabricatorOAuthClientAuthorizationBaseController',
'PhabricatorOAuthClientAuthorizationEditController' => 'PhabricatorOAuthClientAuthorizationBaseController',
'PhabricatorOAuthClientAuthorizationListController' => 'PhabricatorOAuthClientAuthorizationBaseController',
'PhabricatorOAuthClientAuthorizationQuery' => 'PhabricatorOffsetPagedQuery',
'PhabricatorOAuthClientBaseController' => 'PhabricatorOAuthServerController',
'PhabricatorOAuthClientDeleteController' => 'PhabricatorOAuthClientBaseController',
'PhabricatorOAuthClientEditController' => 'PhabricatorOAuthClientBaseController',
'PhabricatorOAuthClientListController' => 'PhabricatorOAuthClientBaseController',
'PhabricatorOAuthClientViewController' => 'PhabricatorOAuthClientBaseController',
'PhabricatorOAuthDefaultRegistrationController' => 'PhabricatorOAuthRegistrationController',
'PhabricatorOAuthDiagnosticsController' => 'PhabricatorAuthController',
'PhabricatorOAuthFailureView' => 'AphrontView',
'PhabricatorOAuthLoginController' => 'PhabricatorAuthController',
'PhabricatorOAuthProviderFacebook' => 'PhabricatorOAuthProvider',
'PhabricatorOAuthProviderGitHub' => 'PhabricatorOAuthProvider',
'PhabricatorOAuthProviderGoogle' => 'PhabricatorOAuthProvider',
'PhabricatorOAuthProviderPhabricator' => 'PhabricatorOAuthProvider',
'PhabricatorOAuthRegistrationController' => 'PhabricatorAuthController',
'PhabricatorOAuthResponse' => 'AphrontResponse',
'PhabricatorOAuthServerAccessToken' => 'PhabricatorOAuthServerDAO',
'PhabricatorOAuthServerAuthController' => 'PhabricatorAuthController',
'PhabricatorOAuthServerAuthorizationCode' => 'PhabricatorOAuthServerDAO',
'PhabricatorOAuthServerClient' => 'PhabricatorOAuthServerDAO',
'PhabricatorOAuthServerClientQuery' => 'PhabricatorOffsetPagedQuery',
'PhabricatorOAuthServerController' => 'PhabricatorController',
'PhabricatorOAuthServerDAO' => 'PhabricatorLiskDAO',
'PhabricatorOAuthServerTestCase' => 'PhabricatorTestCase',
'PhabricatorOAuthServerTestController' => 'PhabricatorOAuthServerController',
'PhabricatorOAuthServerTokenController' => 'PhabricatorAuthController',
'PhabricatorOAuthUnlinkController' => 'PhabricatorAuthController',
'PhabricatorObjectGraph' => 'AbstractDirectedGraph',
'PhabricatorObjectHandleStatus' => 'PhabricatorObjectHandleConstants',
'PhabricatorOffsetPagedQuery' => 'PhabricatorQuery',
'PhabricatorOwnersController' => 'PhabricatorController',
'PhabricatorOwnersDAO' => 'PhabricatorLiskDAO',
'PhabricatorOwnersDeleteController' => 'PhabricatorOwnersController',
'PhabricatorOwnersDetailController' => 'PhabricatorOwnersController',
'PhabricatorOwnersEditController' => 'PhabricatorOwnersController',
'PhabricatorOwnersListController' => 'PhabricatorOwnersController',
'PhabricatorOwnersOwner' => 'PhabricatorOwnersDAO',
'PhabricatorOwnersPackage' => 'PhabricatorOwnersDAO',
'PhabricatorOwnersPath' => 'PhabricatorOwnersDAO',
'PhabricatorPHID' => 'PhabricatorPHIDDAO',
'PhabricatorPHIDController' => 'PhabricatorController',
'PhabricatorPHIDDAO' => 'PhabricatorLiskDAO',
'PhabricatorPHIDLookupController' => 'PhabricatorPHIDController',
'PhabricatorPaste' => 'PhabricatorPasteDAO',
'PhabricatorPasteController' => 'PhabricatorController',
'PhabricatorPasteDAO' => 'PhabricatorLiskDAO',
'PhabricatorPasteListController' => 'PhabricatorPasteController',
'PhabricatorPasteQuery' => 'PhabricatorIDPagedPolicyQuery',
'PhabricatorPasteViewController' => 'PhabricatorPasteController',
'PhabricatorPeopleController' => 'PhabricatorController',
'PhabricatorPeopleEditController' => 'PhabricatorPeopleController',
'PhabricatorPeopleListController' => 'PhabricatorPeopleController',
'PhabricatorPeopleLogsController' => 'PhabricatorPeopleController',
'PhabricatorPeopleProfileController' => 'PhabricatorPeopleController',
'PhabricatorPolicies' => 'PhabricatorPolicyConstants',
'PhabricatorPolicyCapability' => 'PhabricatorPolicyConstants',
'PhabricatorPolicyQuery' => 'PhabricatorQuery',
'PhabricatorPolicyTestCase' => 'PhabricatorTestCase',
'PhabricatorPolicyTestQuery' => 'PhabricatorPolicyQuery',
'PhabricatorProfileHeaderView' => 'AphrontView',
'PhabricatorProject' => 'PhabricatorProjectDAO',
'PhabricatorProjectAffiliation' => 'PhabricatorProjectDAO',
'PhabricatorProjectController' => 'PhabricatorController',
'PhabricatorProjectCreateController' => 'PhabricatorProjectController',
'PhabricatorProjectDAO' => 'PhabricatorLiskDAO',
'PhabricatorProjectListController' => 'PhabricatorProjectController',
'PhabricatorProjectProfile' => 'PhabricatorProjectDAO',
'PhabricatorProjectProfileController' => 'PhabricatorProjectController',
'PhabricatorProjectProfileEditController' => 'PhabricatorProjectController',
'PhabricatorProjectSubproject' => 'PhabricatorProjectDAO',
'PhabricatorProjectTransaction' => 'PhabricatorProjectDAO',
'PhabricatorProjectTransactionType' => 'PhabricatorProjectConstants',
'PhabricatorProjectUpdateController' => 'PhabricatorProjectController',
'PhabricatorRedirectController' => 'PhabricatorController',
'PhabricatorRefreshCSRFController' => 'PhabricatorAuthController',
'PhabricatorRemarkupRuleDifferential' => 'PhabricatorRemarkupRuleObjectName',
'PhabricatorRemarkupRuleDifferentialHandle' => 'PhabricatorRemarkupRuleObjectHandle',
'PhabricatorRemarkupRuleDiffusion' => 'PhutilRemarkupRule',
'PhabricatorRemarkupRuleEmbedFile' => 'PhutilRemarkupRule',
'PhabricatorRemarkupRuleImageMacro' => 'PhutilRemarkupRule',
'PhabricatorRemarkupRuleManiphest' => 'PhabricatorRemarkupRuleObjectName',
'PhabricatorRemarkupRuleManiphestHandle' => 'PhabricatorRemarkupRuleObjectHandle',
'PhabricatorRemarkupRuleMention' => 'PhutilRemarkupRule',
'PhabricatorRemarkupRuleObjectHandle' => 'PhutilRemarkupRule',
'PhabricatorRemarkupRuleObjectName' => 'PhutilRemarkupRule',
'PhabricatorRemarkupRulePaste' => 'PhabricatorRemarkupRuleObjectName',
'PhabricatorRemarkupRulePhriction' => 'PhutilRemarkupRule',
'PhabricatorRemarkupRuleProxyImage' => 'PhutilRemarkupRule',
'PhabricatorRemarkupRuleYoutube' => 'PhutilRemarkupRule',
'PhabricatorRepository' => 'PhabricatorRepositoryDAO',
'PhabricatorRepositoryArcanistProject' => 'PhabricatorRepositoryDAO',
'PhabricatorRepositoryArcanistProjectEditController' => 'PhabricatorRepositoryController',
'PhabricatorRepositoryAuditRequest' => 'PhabricatorRepositoryDAO',
'PhabricatorRepositoryCommit' => 'PhabricatorRepositoryDAO',
'PhabricatorRepositoryCommitChangeParserWorker' => 'PhabricatorRepositoryCommitParserWorker',
'PhabricatorRepositoryCommitData' => 'PhabricatorRepositoryDAO',
'PhabricatorRepositoryCommitDiscoveryDaemon' => 'PhabricatorRepositoryDaemon',
'PhabricatorRepositoryCommitHeraldWorker' => 'PhabricatorRepositoryCommitParserWorker',
'PhabricatorRepositoryCommitMessageParserWorker' => 'PhabricatorRepositoryCommitParserWorker',
'PhabricatorRepositoryCommitOwnersWorker' => 'PhabricatorRepositoryCommitParserWorker',
'PhabricatorRepositoryCommitParserWorker' => 'PhabricatorWorker',
'PhabricatorRepositoryCommitTaskDaemon' => 'PhabricatorRepositoryDaemon',
'PhabricatorRepositoryController' => 'PhabricatorController',
'PhabricatorRepositoryCreateController' => 'PhabricatorRepositoryController',
'PhabricatorRepositoryDAO' => 'PhabricatorLiskDAO',
'PhabricatorRepositoryDaemon' => 'PhabricatorDaemon',
'PhabricatorRepositoryDefaultCommitMessageDetailParser' => 'PhabricatorRepositoryCommitMessageDetailParser',
'PhabricatorRepositoryDeleteController' => 'PhabricatorRepositoryController',
'PhabricatorRepositoryEditController' => 'PhabricatorRepositoryController',
'PhabricatorRepositoryGitCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
'PhabricatorRepositoryGitCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',
'PhabricatorRepositoryGitCommitDiscoveryDaemonTestCase' => 'PhabricatorTestCase',
'PhabricatorRepositoryGitCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
'PhabricatorRepositoryGitFetchDaemon' => 'PhabricatorRepositoryPullLocalDaemon',
'PhabricatorRepositoryListController' => 'PhabricatorRepositoryController',
'PhabricatorRepositoryMercurialCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
'PhabricatorRepositoryMercurialCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',
'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
'PhabricatorRepositoryMercurialPullDaemon' => 'PhabricatorRepositoryPullLocalDaemon',
'PhabricatorRepositoryPullLocalDaemon' => 'PhabricatorRepositoryDaemon',
'PhabricatorRepositoryShortcut' => 'PhabricatorRepositoryDAO',
'PhabricatorRepositorySvnCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',
'PhabricatorRepositorySvnCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
'PhabricatorRepositorySymbol' => 'PhabricatorRepositoryDAO',
'PhabricatorRepositoryTestCase' => 'PhabricatorTestCase',
'PhabricatorS3FileStorageEngine' => 'PhabricatorFileStorageEngine',
'PhabricatorSearchAttachController' => 'PhabricatorSearchBaseController',
'PhabricatorSearchBaseController' => 'PhabricatorController',
'PhabricatorSearchCommitIndexer' => 'PhabricatorSearchDocumentIndexer',
'PhabricatorSearchController' => 'PhabricatorSearchBaseController',
'PhabricatorSearchDAO' => 'PhabricatorLiskDAO',
'PhabricatorSearchDifferentialIndexer' => 'PhabricatorSearchDocumentIndexer',
'PhabricatorSearchDocument' => 'PhabricatorSearchDAO',
'PhabricatorSearchDocumentField' => 'PhabricatorSearchDAO',
'PhabricatorSearchDocumentRelationship' => 'PhabricatorSearchDAO',
'PhabricatorSearchEngineElastic' => 'PhabricatorSearchEngine',
'PhabricatorSearchEngineMySQL' => 'PhabricatorSearchEngine',
'PhabricatorSearchIndexController' => 'PhabricatorSearchBaseController',
'PhabricatorSearchManiphestIndexer' => 'PhabricatorSearchDocumentIndexer',
'PhabricatorSearchPhrictionIndexer' => 'PhabricatorSearchDocumentIndexer',
'PhabricatorSearchQuery' => 'PhabricatorSearchDAO',
'PhabricatorSearchResultView' => 'AphrontView',
'PhabricatorSearchSelectController' => 'PhabricatorSearchBaseController',
'PhabricatorSearchUserIndexer' => 'PhabricatorSearchDocumentIndexer',
'PhabricatorSlowvoteChoice' => 'PhabricatorSlowvoteDAO',
'PhabricatorSlowvoteComment' => 'PhabricatorSlowvoteDAO',
'PhabricatorSlowvoteController' => 'PhabricatorController',
'PhabricatorSlowvoteCreateController' => 'PhabricatorSlowvoteController',
'PhabricatorSlowvoteDAO' => 'PhabricatorLiskDAO',
'PhabricatorSlowvoteListController' => 'PhabricatorSlowvoteController',
'PhabricatorSlowvoteOption' => 'PhabricatorSlowvoteDAO',
'PhabricatorSlowvotePoll' => 'PhabricatorSlowvoteDAO',
'PhabricatorSlowvotePollController' => 'PhabricatorSlowvoteController',
'PhabricatorSlugTestCase' => 'PhabricatorTestCase',
'PhabricatorSortTableExample' => 'PhabricatorUIExample',
'PhabricatorStandardPageView' => 'AphrontPageView',
'PhabricatorStatusController' => 'PhabricatorController',
'PhabricatorSymbolNameLinter' => 'ArcanistXHPASTLintNamingHook',
'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon',
'PhabricatorTestCase' => 'ArcanistPhutilTestCase',
'PhabricatorTimelineCursor' => 'PhabricatorTimelineDAO',
'PhabricatorTimelineDAO' => 'PhabricatorLiskDAO',
'PhabricatorTimelineEvent' => 'PhabricatorTimelineDAO',
'PhabricatorTimelineEventData' => 'PhabricatorTimelineDAO',
'PhabricatorTimer' => 'PhabricatorCountdownDAO',
'PhabricatorTransactionView' => 'AphrontView',
'PhabricatorTransformedFile' => 'PhabricatorFileDAO',
'PhabricatorTrivialTestCase' => 'PhabricatorTestCase',
'PhabricatorTypeaheadCommonDatasourceController' => 'PhabricatorTypeaheadDatasourceController',
'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController',
'PhabricatorUIExampleController' => 'PhabricatorController',
'PhabricatorUIExampleRenderController' => 'PhabricatorUIExampleController',
'PhabricatorUIListFilterExample' => 'PhabricatorUIExample',
'PhabricatorUIPagerExample' => 'PhabricatorUIExample',
'PhabricatorUITooltipExample' => 'PhabricatorUIExample',
'PhabricatorUser' => 'PhabricatorUserDAO',
'PhabricatorUserAccountSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
'PhabricatorUserConduitSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
'PhabricatorUserDAO' => 'PhabricatorLiskDAO',
'PhabricatorUserEmailPreferenceSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
'PhabricatorUserEmailSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
'PhabricatorUserLog' => 'PhabricatorUserDAO',
'PhabricatorUserOAuthInfo' => 'PhabricatorUserDAO',
'PhabricatorUserOAuthSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
'PhabricatorUserPasswordSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
'PhabricatorUserPreferenceSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
'PhabricatorUserPreferences' => 'PhabricatorUserDAO',
'PhabricatorUserProfile' => 'PhabricatorUserDAO',
'PhabricatorUserProfileSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
'PhabricatorUserSSHKey' => 'PhabricatorUserDAO',
'PhabricatorUserSSHKeysSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
'PhabricatorUserSearchSettingsPanelController' => 'PhabricatorUserSettingsPanelController',
'PhabricatorUserSettingsController' => 'PhabricatorPeopleController',
'PhabricatorUserSettingsPanelController' => 'PhabricatorPeopleController',
'PhabricatorUserTestCase' => 'PhabricatorTestCase',
'PhabricatorWorkerDAO' => 'PhabricatorLiskDAO',
'PhabricatorWorkerTask' => 'PhabricatorWorkerDAO',
'PhabricatorWorkerTaskData' => 'PhabricatorWorkerDAO',
'PhabricatorWorkerTaskDetailController' => 'PhabricatorDaemonController',
'PhabricatorWorkerTaskUpdateController' => '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' => 'PhabricatorXHProfProfileView',
'PhabricatorXHProfProfileTopLevelView' => 'PhabricatorXHProfProfileView',
'PhabricatorXHProfProfileView' => 'AphrontView',
'PhameController' => 'PhabricatorController',
'PhameDAO' => 'PhabricatorLiskDAO',
'PhameDraftListController' => 'PhamePostListBaseController',
'PhamePost' => 'PhameDAO',
'PhamePostDeleteController' => 'PhameController',
'PhamePostDetailView' => 'AphrontView',
'PhamePostEditController' => 'PhameController',
'PhamePostListBaseController' => 'PhameController',
'PhamePostListController' => 'PhamePostListBaseController',
'PhamePostListView' => 'AphrontView',
'PhamePostPreviewController' => 'PhameController',
'PhamePostQuery' => 'PhabricatorOffsetPagedQuery',
'PhamePostViewController' => 'PhameController',
'PhortuneMonthYearExpiryControl' => 'AphrontFormControl',
'PhortuneStripeBaseController' => 'PhabricatorController',
'PhortuneStripePaymentFormView' => 'AphrontView',
'PhortuneStripeTestPaymentFormController' => 'PhortuneStripeBaseController',
'PhrictionActionConstants' => 'PhrictionConstants',
'PhrictionChangeType' => 'PhrictionConstants',
'PhrictionContent' => 'PhrictionDAO',
'PhrictionController' => 'PhabricatorController',
'PhrictionDAO' => 'PhabricatorLiskDAO',
'PhrictionDeleteController' => 'PhrictionController',
'PhrictionDiffController' => 'PhrictionController',
'PhrictionDocument' => 'PhrictionDAO',
'PhrictionDocumentController' => 'PhrictionController',
'PhrictionDocumentPreviewController' => 'PhrictionController',
'PhrictionDocumentStatus' => 'PhrictionConstants',
'PhrictionDocumentTestCase' => 'PhabricatorTestCase',
'PhrictionEditController' => 'PhrictionController',
'PhrictionHistoryController' => 'PhrictionController',
'PhrictionListController' => 'PhrictionController',
'QueryFormattingTestCase' => 'PhabricatorTestCase',
),
'requires_interface' =>
array(
'DefaultDatabaseConfigurationProvider' =>
array(
0 => 'DatabaseConfigurationProvider',
),
'DifferentialInlineComment' =>
array(
0 => 'PhabricatorInlineCommentInterface',
),
'PhabricatorAuditInlineComment' =>
array(
0 => 'PhabricatorInlineCommentInterface',
),
'PhabricatorPaste' =>
array(
0 => 'PhabricatorPolicyInterface',
),
'PhabricatorPolicyTestObject' =>
array(
0 => 'PhabricatorPolicyInterface',
),
),
));
diff --git a/src/applications/conduit/method/differential/markcommitted/ConduitAPI_differential_markcommitted_Method.php b/src/applications/conduit/method/differential/close/ConduitAPI_differential_close_Method.php
similarity index 73%
copy from src/applications/conduit/method/differential/markcommitted/ConduitAPI_differential_markcommitted_Method.php
copy to src/applications/conduit/method/differential/close/ConduitAPI_differential_close_Method.php
index bdbe365bd3..bcfe5a67f0 100644
--- a/src/applications/conduit/method/differential/markcommitted/ConduitAPI_differential_markcommitted_Method.php
+++ b/src/applications/conduit/method/differential/close/ConduitAPI_differential_close_Method.php
@@ -1,74 +1,77 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group conduit
*/
-final class ConduitAPI_differential_markcommitted_Method
+final class ConduitAPI_differential_close_Method
extends ConduitAPIMethod {
public function getMethodDescription() {
- return "Mark Differential revisions as committed.";
+ return "Close a Differential revision.";
}
public function defineParamTypes() {
return array(
- 'revision_id' => 'required revision_id',
+ 'revisionID' => 'required int',
);
}
public function defineReturnType() {
return 'void';
}
public function defineErrorTypes() {
return array(
'ERR_NOT_FOUND' => 'Revision was not found.',
);
}
protected function execute(ConduitAPIRequest $request) {
- $id = $request->getValue('revision_id');
+ $id = $request->getValue('revisionID');
$revision = id(new DifferentialRevision())->load($id);
if (!$revision) {
throw new ConduitException('ERR_NOT_FOUND');
}
- if ($revision->getStatus() ==
- ArcanistDifferentialRevisionStatus::COMMITTED) {
- // This can occur if someone runs 'mark-committed' and hits a race, or
+ if ($revision->getStatus() == ArcanistDifferentialRevisionStatus::CLOSED) {
+ // This can occur if someone runs 'close-revision' and hits a race, or
// they have a remote hook installed but don't have the
// 'remote_hook_installed' flag set, or similar. In any case, just treat
- // it as a no-op rather than adding another "X committed this revision"
+ // it as a no-op rather than adding another "X closed this revision"
// message to the revision comments.
return;
}
$revision->loadRelationships();
$editor = new DifferentialCommentEditor(
$revision,
$request->getUser()->getPHID(),
- DifferentialAction::ACTION_COMMIT);
+ DifferentialAction::ACTION_CLOSE);
$editor->save();
+ $revision->setStatus(ArcanistDifferentialRevisionStatus::CLOSED);
+ $revision->setDateCommitted(time());
+ $revision->save();
+
return;
}
}
diff --git a/src/applications/differential/query/revision/__init__.php b/src/applications/conduit/method/differential/close/__init__.php
similarity index 53%
copy from src/applications/differential/query/revision/__init__.php
copy to src/applications/conduit/method/differential/close/__init__.php
index 04b88010cc..a594223b52 100644
--- a/src/applications/differential/query/revision/__init__.php
+++ b/src/applications/conduit/method/differential/close/__init__.php
@@ -1,22 +1,20 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
-phutil_require_module('arcanist', 'differential/constants/revisionhash');
phutil_require_module('arcanist', 'differential/constants/revisionstatus');
-phutil_require_module('phabricator', 'applications/differential/storage/affectedpath');
-phutil_require_module('phabricator', 'applications/differential/storage/diff');
-phutil_require_module('phabricator', 'applications/differential/storage/inlinecomment');
+phutil_require_module('phabricator', 'applications/conduit/method/base');
+phutil_require_module('phabricator', 'applications/conduit/protocol/exception');
+phutil_require_module('phabricator', 'applications/differential/constants/action');
+phutil_require_module('phabricator', 'applications/differential/editor/comment');
phutil_require_module('phabricator', 'applications/differential/storage/revision');
-phutil_require_module('phabricator', 'storage/qsprintf');
-phutil_require_module('phabricator', 'storage/queryfx');
phutil_require_module('phutil', 'utils');
-phutil_require_source('DifferentialRevisionQuery.php');
+phutil_require_source('ConduitAPI_differential_close_Method.php');
diff --git a/src/applications/conduit/method/differential/creatediff/ConduitAPI_differential_creatediff_Method.php b/src/applications/conduit/method/differential/creatediff/ConduitAPI_differential_creatediff_Method.php
index 1713d6ff26..5f0557e9c0 100644
--- a/src/applications/conduit/method/differential/creatediff/ConduitAPI_differential_creatediff_Method.php
+++ b/src/applications/conduit/method/differential/creatediff/ConduitAPI_differential_creatediff_Method.php
@@ -1,161 +1,161 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group conduit
*/
final class ConduitAPI_differential_creatediff_Method extends ConduitAPIMethod {
public function getMethodDescription() {
return "Create a new Differential diff.";
}
public function defineParamTypes() {
return array(
'changes' => 'required list<dict>',
'sourceMachine' => 'required string',
'sourcePath' => 'required string',
'branch' => 'required string',
'sourceControlSystem' => 'required enum<svn, git>',
'sourceControlPath' => 'required string',
'sourceControlBaseRevision' => 'required string',
'parentRevisionID' => 'optional revisionid',
'creationMethod' => 'optional string',
'authorPHID' => 'optional phid',
'arcanistProject' => 'optional string',
'repositoryUUID' => 'optional string',
'lintStatus' =>
'required enum<none, skip, okay, warn, fail>',
'unitStatus' =>
'required enum<none, skip, okay, warn, fail>',
);
}
public function defineReturnType() {
return 'nonempty dict';
}
public function defineErrorTypes() {
return array(
);
}
protected function execute(ConduitAPIRequest $request) {
$change_data = $request->getValue('changes');
$changes = array();
foreach ($change_data as $dict) {
$changes[] = ArcanistDiffChange::newFromDictionary($dict);
}
$diff = DifferentialDiff::newFromRawChanges($changes);
$diff->setSourcePath($request->getValue('sourcePath'));
$diff->setSourceMachine($request->getValue('sourceMachine'));
$diff->setBranch($request->getValue('branch'));
$diff->setCreationMethod($request->getValue('creationMethod'));
$diff->setAuthorPHID($request->getValue('authorPHID'));
$parent_id = $request->getValue('parentRevisionID');
if ($parent_id) {
$parent_rev = id(new DifferentialRevision())->load($parent_id);
if ($parent_rev) {
if ($parent_rev->getStatus() !=
- ArcanistDifferentialRevisionStatus::COMMITTED) {
+ ArcanistDifferentialRevisionStatus::CLOSED) {
$diff->setParentRevisionID($parent_id);
}
}
}
$system = $request->getValue('sourceControlSystem');
$diff->setSourceControlSystem($system);
$diff->setSourceControlPath($request->getValue('sourceControlPath'));
$diff->setSourceControlBaseRevision(
$request->getValue('sourceControlBaseRevision'));
$project_name = $request->getValue('arcanistProject');
$project_phid = null;
if ($project_name) {
$arcanist_project = id(new PhabricatorRepositoryArcanistProject())
->loadOneWhere(
'name = %s',
$project_name);
if (!$arcanist_project) {
$arcanist_project = new PhabricatorRepositoryArcanistProject();
$arcanist_project->setName($project_name);
$arcanist_project->save();
}
$project_phid = $arcanist_project->getPHID();
}
$diff->setArcanistProjectPHID($project_phid);
$diff->setRepositoryUUID($request->getValue('repositoryUUID'));
switch ($request->getValue('lintStatus')) {
case 'skip':
$diff->setLintStatus(DifferentialLintStatus::LINT_SKIP);
break;
case 'okay':
$diff->setLintStatus(DifferentialLintStatus::LINT_OKAY);
break;
case 'warn':
$diff->setLintStatus(DifferentialLintStatus::LINT_WARN);
break;
case 'fail':
$diff->setLintStatus(DifferentialLintStatus::LINT_FAIL);
break;
case 'none':
default:
$diff->setLintStatus(DifferentialLintStatus::LINT_NONE);
break;
}
switch ($request->getValue('unitStatus')) {
case 'skip':
$diff->setUnitStatus(DifferentialUnitStatus::UNIT_SKIP);
break;
case 'okay':
$diff->setUnitStatus(DifferentialUnitStatus::UNIT_OKAY);
break;
case 'warn':
$diff->setUnitStatus(DifferentialUnitStatus::UNIT_WARN);
break;
case 'fail':
$diff->setUnitStatus(DifferentialUnitStatus::UNIT_FAIL);
break;
case 'postponed':
$diff->setUnitStatus(DifferentialUnitStatus::UNIT_POSTPONED);
break;
case 'none':
default:
$diff->setUnitStatus(DifferentialUnitStatus::UNIT_NONE);
break;
}
$diff->save();
$path = '/differential/diff/'.$diff->getID().'/';
$uri = PhabricatorEnv::getURI($path);
return array(
'diffid' => $diff->getID(),
'uri' => $uri,
);
}
}
diff --git a/src/applications/conduit/method/differential/markcommitted/ConduitAPI_differential_markcommitted_Method.php b/src/applications/conduit/method/differential/markcommitted/ConduitAPI_differential_markcommitted_Method.php
index bdbe365bd3..47163a3561 100644
--- a/src/applications/conduit/method/differential/markcommitted/ConduitAPI_differential_markcommitted_Method.php
+++ b/src/applications/conduit/method/differential/markcommitted/ConduitAPI_differential_markcommitted_Method.php
@@ -1,74 +1,75 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group conduit
+ * @deprecated
*/
final class ConduitAPI_differential_markcommitted_Method
extends ConduitAPIMethod {
+ public function getMethodStatus() {
+ return self::METHOD_STATUS_DEPRECATED;
+ }
+
+ public function getMethodStatusDescription() {
+ return "Replaced by 'differential.close'.";
+ }
+
public function getMethodDescription() {
- return "Mark Differential revisions as committed.";
+ return "Mark a revision closed.";
}
public function defineParamTypes() {
return array(
'revision_id' => 'required revision_id',
);
}
public function defineReturnType() {
return 'void';
}
public function defineErrorTypes() {
return array(
'ERR_NOT_FOUND' => 'Revision was not found.',
);
}
protected function execute(ConduitAPIRequest $request) {
$id = $request->getValue('revision_id');
$revision = id(new DifferentialRevision())->load($id);
if (!$revision) {
throw new ConduitException('ERR_NOT_FOUND');
}
- if ($revision->getStatus() ==
- ArcanistDifferentialRevisionStatus::COMMITTED) {
- // This can occur if someone runs 'mark-committed' and hits a race, or
- // they have a remote hook installed but don't have the
- // 'remote_hook_installed' flag set, or similar. In any case, just treat
- // it as a no-op rather than adding another "X committed this revision"
- // message to the revision comments.
+ if ($revision->getStatus() == ArcanistDifferentialRevisionStatus::CLOSED) {
return;
}
$revision->loadRelationships();
$editor = new DifferentialCommentEditor(
$revision,
$request->getUser()->getPHID(),
- DifferentialAction::ACTION_COMMIT);
+ DifferentialAction::ACTION_CLOSE);
$editor->save();
-
- return;
}
}
diff --git a/src/applications/conduit/method/differential/query/ConduitAPI_differential_query_Method.php b/src/applications/conduit/method/differential/query/ConduitAPI_differential_query_Method.php
index 983a4c0e2d..2c2c4d52a0 100644
--- a/src/applications/conduit/method/differential/query/ConduitAPI_differential_query_Method.php
+++ b/src/applications/conduit/method/differential/query/ConduitAPI_differential_query_Method.php
@@ -1,218 +1,218 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group conduit
*/
final class ConduitAPI_differential_query_Method
extends ConduitAPIMethod {
public function getMethodDescription() {
return "Query Differential revisions which match certain criteria.";
}
public function defineParamTypes() {
$hash_types = ArcanistDifferentialRevisionHash::getTypes();
$hash_types = implode(', ', $hash_types);
$status_types = array(
DifferentialRevisionQuery::STATUS_ANY,
DifferentialRevisionQuery::STATUS_OPEN,
DifferentialRevisionQuery::STATUS_ACCEPTED,
- DifferentialRevisionQuery::STATUS_COMMITTED,
+ DifferentialRevisionQuery::STATUS_CLOSED,
);
$status_types = implode(', ', $status_types);
$order_types = array(
DifferentialRevisionQuery::ORDER_MODIFIED,
DifferentialRevisionQuery::ORDER_CREATED,
);
$order_types = implode(', ', $order_types);
return array(
'authors' => 'optional list<phid>',
'ccs' => 'optional list<phid>',
'reviewers' => 'optional list<phid>',
// TODO: Implement this, it needs to accept a repository ID in addition
// to a path so the signature needs to be a little more complicated.
// 'paths' => 'optional list<pair<...>>',
'commitHashes' => 'optional list<pair<enum<'.
$hash_types.'>, string>>',
'status' => 'optional enum<'.$status_types.'>',
'order' => 'optional enum<'.$order_types.'>',
'limit' => 'optional uint',
'offset' => 'optional uint',
'ids' => 'optional list<uint>',
'phids' => 'optional list<phid>',
'subscribers' => 'optional list<phid>',
'responsibleUsers' => 'optional list<phid>',
'branches' => 'optional list<string>',
'arcanistProjects' => 'optional list<string>',
);
}
public function defineReturnType() {
return 'list<dict>';
}
public function defineErrorTypes() {
return array(
'ERR-INVALID-PARAMETER' => 'Missing or malformed parameter.',
);
}
protected function execute(ConduitAPIRequest $request) {
$authors = $request->getValue('authors');
$ccs = $request->getValue('ccs');
$reviewers = $request->getValue('reviewers');
$status = $request->getValue('status');
$order = $request->getValue('order');
$commit_hashes = $request->getValue('commitHashes');
$limit = $request->getValue('limit');
$offset = $request->getValue('offset');
$ids = $request->getValue('ids');
$phids = $request->getValue('phids');
$subscribers = $request->getValue('subscribers');
$responsible_users = $request->getValue('responsibleUsers');
$branches = $request->getValue('branches');
$arc_projects = $request->getValue('arcanistProjects');
$query = new DifferentialRevisionQuery();
if ($authors) {
$query->withAuthors($authors);
}
if ($ccs) {
$query->withCCs($ccs);
}
if ($reviewers) {
$query->withReviewers($reviewers);
}
/* TODO: Implement.
$paths = $request->getValue('paths');
if ($paths) {
foreach ($paths as $path) {
// (Lookup the repository IDs.)
$query->withPath($repository_id, $path);
}
}
*/
if ($commit_hashes) {
$hash_types = ArcanistDifferentialRevisionHash::getTypes();
foreach ($commit_hashes as $info) {
list($type, $hash) = $info;
if (empty($type) ||
!in_array($type, $hash_types) ||
empty($hash)) {
throw new ConduitException('ERR-INVALID-PARAMETER');
}
}
$query->withCommitHashes($commit_hashes);
}
if ($status) {
$query->withStatus($status);
}
if ($order) {
$query->setOrder($order);
}
if ($limit) {
$query->setLimit($limit);
}
if ($offset) {
$query->setOffset($offset);
}
if ($ids) {
$query->withIDs($ids);
}
if ($phids) {
$query->withPHIDs($phids);
}
if ($responsible_users) {
$query->withResponsibleUsers($responsible_users);
}
if ($subscribers) {
$query->withSubscribers($subscribers);
}
if ($branches) {
$query->withBranches($branches);
}
if ($arc_projects) {
// This is sort of special-cased, but don't make arc do an extra round
// trip.
$projects = id(new PhabricatorRepositoryArcanistProject())
->loadAllWhere(
'name in (%Ls)',
$arc_projects);
if (!$projects) {
return array();
}
$query->withArcanistProjectPHIDs(mpull($projects, 'getPHID'));
}
$query->needRelationships(true);
$query->needCommitPHIDs(true);
$query->needDiffIDs(true);
$query->needActiveDiffs(true);
$revisions = $query->execute();
$results = array();
foreach ($revisions as $revision) {
$diff = $revision->getActiveDiff();
if (!$diff) {
continue;
}
$id = $revision->getID();
$result = array(
'id' => $id,
'phid' => $revision->getPHID(),
'title' => $revision->getTitle(),
'uri' => PhabricatorEnv::getProductionURI('/D'.$id),
'dateCreated' => $revision->getDateCreated(),
'dateModified' => $revision->getDateModified(),
'authorPHID' => $revision->getAuthorPHID(),
'status' => $revision->getStatus(),
'statusName' =>
ArcanistDifferentialRevisionStatus::getNameForRevisionStatus(
$revision->getStatus()),
'branch' => $diff->getBranch(),
'summary' => $revision->getSummary(),
'testPlan' => $revision->getTestPlan(),
'lineCount' => $revision->getLineCount(),
'diffs' => $revision->getDiffIDs(),
'commits' => $revision->getCommitPHIDs(),
'reviewers' => array_values($revision->getReviewers()),
'ccs' => array_values($revision->getCCPHIDs()),
);
// TODO: This is a hacky way to put permissions on this field until we
// have first-class support, see T838.
if ($revision->getAuthorPHID() == $request->getUser()->getPHID()) {
$result['sourcePath'] = $diff->getSourcePath();
}
$results[] = $result;
}
return $results;
}
}
diff --git a/src/applications/conduit/method/differential/updaterevision/ConduitAPI_differential_updaterevision_Method.php b/src/applications/conduit/method/differential/updaterevision/ConduitAPI_differential_updaterevision_Method.php
index 6584b39a1c..19b9338af0 100644
--- a/src/applications/conduit/method/differential/updaterevision/ConduitAPI_differential_updaterevision_Method.php
+++ b/src/applications/conduit/method/differential/updaterevision/ConduitAPI_differential_updaterevision_Method.php
@@ -1,91 +1,90 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group conduit
*/
final class ConduitAPI_differential_updaterevision_Method
extends ConduitAPIMethod {
public function getMethodDescription() {
return "Update a Differential revision.";
}
public function defineParamTypes() {
return array(
'id' => 'required revisionid',
'diffid' => 'required diffid',
'fields' => 'required dict',
'message' => 'required string',
);
}
public function defineReturnType() {
return 'nonempty dict';
}
public function defineErrorTypes() {
return array(
'ERR_BAD_DIFF' => 'Bad diff ID.',
'ERR_BAD_REVISION' => 'Bad revision ID.',
'ERR_WRONG_USER' => 'You are not the author of this revision.',
- 'ERR_COMMITTED' => 'This revision has already been committed.',
+ 'ERR_CLOSED' => 'This revision has already been closed.',
);
}
protected function execute(ConduitAPIRequest $request) {
$diff = id(new DifferentialDiff())->load($request->getValue('diffid'));
if (!$diff) {
throw new ConduitException('ERR_BAD_DIFF');
}
$revision = id(new DifferentialRevision())->load($request->getValue('id'));
if (!$revision) {
throw new ConduitException('ERR_BAD_REVISION');
}
if ($request->getUser()->getPHID() !== $revision->getAuthorPHID()) {
throw new ConduitException('ERR_WRONG_USER');
}
- if ($revision->getStatus() ==
- ArcanistDifferentialRevisionStatus::COMMITTED) {
- throw new ConduitException('ERR_COMMITTED');
+ if ($revision->getStatus() == ArcanistDifferentialRevisionStatus::CLOSED) {
+ throw new ConduitException('ERR_CLOSED');
}
$content_source = PhabricatorContentSource::newForSource(
PhabricatorContentSource::SOURCE_CONDUIT,
array());
$editor = new DifferentialRevisionEditor(
$revision,
$revision->getAuthorPHID());
$editor->setContentSource($content_source);
$fields = $request->getValue('fields');
$editor->copyFieldsFromConduit($fields);
$editor->addDiff($diff, $request->getValue('message'));
$editor->save();
return array(
'revisionid' => $revision->getID(),
'uri' => PhabricatorEnv::getURI('/D'.$revision->getID()),
);
}
}
diff --git a/src/applications/differential/constants/action/DifferentialAction.php b/src/applications/differential/constants/action/DifferentialAction.php
index b9948cca32..25c1e2e152 100644
--- a/src/applications/differential/constants/action/DifferentialAction.php
+++ b/src/applications/differential/constants/action/DifferentialAction.php
@@ -1,88 +1,88 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class DifferentialAction {
- const ACTION_COMMIT = 'commit';
+ const ACTION_CLOSE = 'commit';
const ACTION_COMMENT = 'none';
const ACTION_ACCEPT = 'accept';
const ACTION_REJECT = 'reject';
const ACTION_RETHINK = 'rethink';
const ACTION_ABANDON = 'abandon';
const ACTION_REQUEST = 'request_review';
const ACTION_RECLAIM = 'reclaim';
const ACTION_UPDATE = 'update';
const ACTION_RESIGN = 'resign';
const ACTION_SUMMARIZE = 'summarize';
const ACTION_TESTPLAN = 'testplan';
const ACTION_CREATE = 'create';
const ACTION_ADDREVIEWERS = 'add_reviewers';
const ACTION_ADDCCS = 'add_ccs';
const ACTION_CLAIM = 'claim';
public static function getActionPastTenseVerb($action) {
static $verbs = array(
self::ACTION_COMMENT => 'commented on',
self::ACTION_ACCEPT => 'accepted',
self::ACTION_REJECT => 'requested changes to',
self::ACTION_RETHINK => 'planned changes to',
self::ACTION_ABANDON => 'abandoned',
- self::ACTION_COMMIT => 'committed',
+ self::ACTION_CLOSE => 'closed',
self::ACTION_REQUEST => 'requested a review of',
self::ACTION_RECLAIM => 'reclaimed',
self::ACTION_UPDATE => 'updated',
self::ACTION_RESIGN => 'resigned from',
self::ACTION_SUMMARIZE => 'summarized',
self::ACTION_TESTPLAN => 'explained the test plan for',
self::ACTION_CREATE => 'created',
self::ACTION_ADDREVIEWERS => 'added reviewers to',
self::ACTION_ADDCCS => 'added CCs to',
self::ACTION_CLAIM => 'commandeered',
);
if (!empty($verbs[$action])) {
return $verbs[$action];
} else {
return 'brazenly "'.$action.'ed"';
}
}
public static function getActionVerb($action) {
static $verbs = array(
self::ACTION_COMMENT => 'Comment',
self::ACTION_ACCEPT => "Accept Revision \xE2\x9C\x94",
self::ACTION_REJECT => "Request Changes \xE2\x9C\x98",
self::ACTION_RETHINK => "Plan Changes \xE2\x9C\x98",
self::ACTION_ABANDON => 'Abandon Revision',
self::ACTION_REQUEST => 'Request Review',
self::ACTION_RECLAIM => 'Reclaim Revision',
self::ACTION_RESIGN => 'Resign as Reviewer',
self::ACTION_ADDREVIEWERS => 'Add Reviewers',
self::ACTION_ADDCCS => 'Add CCs',
- self::ACTION_COMMIT => 'Mark Committed',
+ self::ACTION_CLOSE => 'Close Revision',
self::ACTION_CLAIM => 'Commandeer Revision',
);
if (!empty($verbs[$action])) {
return $verbs[$action];
} else {
return 'brazenly '.$action;
}
}
}
diff --git a/src/applications/differential/controller/revisionlist/DifferentialRevisionListController.php b/src/applications/differential/controller/revisionlist/DifferentialRevisionListController.php
index 5525beb009..391c762dbc 100644
--- a/src/applications/differential/controller/revisionlist/DifferentialRevisionListController.php
+++ b/src/applications/differential/controller/revisionlist/DifferentialRevisionListController.php
@@ -1,505 +1,505 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class DifferentialRevisionListController extends DifferentialController {
private $filter;
private $username;
public function shouldRequireLogin() {
return !$this->allowsAnonymousAccess();
}
public function willProcessRequest(array $data) {
$this->filter = idx($data, 'filter');
$this->username = idx($data, 'username');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$viewer_is_anonymous = !$user->isLoggedIn();
$params = array_filter(
array(
'status' => $request->getStr('status'),
'order' => $request->getStr('order'),
));
$default_filter = ($viewer_is_anonymous ? 'all' : 'active');
$filters = $this->getFilters();
$this->filter = $this->selectFilter(
$filters,
$this->filter,
$default_filter);
// Redirect from search to canonical URL.
$phid_arr = $request->getArr('view_user');
if ($phid_arr) {
$view_user = id(new PhabricatorUser())
->loadOneWhere('phid = %s', head($phid_arr));
$base_uri = '/differential/filter/'.$this->filter.'/';
if ($view_user) {
// This is a user, so generate a pretty URI.
$uri = $base_uri.phutil_escape_uri($view_user->getUserName()).'/';
} else {
// We're assuming this is a mailing list, generate an ugly URI.
$uri = $base_uri;
$params['phid'] = head($phid_arr);
}
$uri = new PhutilURI($uri);
$uri->setQueryParams($params);
return id(new AphrontRedirectResponse())->setURI($uri);
}
$uri = new PhutilURI('/differential/filter/'.$this->filter.'/');
$uri->setQueryParams($params);
$username = '';
if ($this->username) {
$view_user = id(new PhabricatorUser())
->loadOneWhere('userName = %s', $this->username);
if (!$view_user) {
return new Aphront404Response();
}
$username = phutil_escape_uri($this->username).'/';
$uri->setPath('/differential/filter/'.$this->filter.'/'.$username);
$params['phid'] = $view_user->getPHID();
} else {
$phid = $request->getStr('phid');
if (strlen($phid)) {
$params['phid'] = $phid;
}
}
// Fill in the defaults we'll actually use for calculations if any
// parameters are missing.
$params += array(
'phid' => $user->getPHID(),
'status' => 'all',
'order' => 'modified',
);
$side_nav = new AphrontSideNavView();
foreach ($filters as $filter) {
list($filter_name, $display_name) = $filter;
if ($filter_name) {
$href = clone $uri;
$href->setPath('/differential/filter/'.$filter_name.'/'.$username);
if ($filter_name == $this->filter) {
$class = 'aphront-side-nav-selected';
} else {
$class = null;
}
$item = phutil_render_tag(
'a',
array(
'href' => (string)$href,
'class' => $class,
),
phutil_escape_html($display_name));
} else {
$item = phutil_render_tag(
'span',
array(),
phutil_escape_html($display_name));
}
$side_nav->addNavItem($item);
}
$panels = array();
$handles = array();
$controls = $this->getFilterControls($this->filter);
if ($this->getFilterRequiresUser($this->filter) && !$params['phid']) {
// In the anonymous case, we still want to let you see some user's
// list, but we don't have a default PHID to provide (normally, we use
// the viewing user's). Show a warning instead.
$warning = new AphrontErrorView();
$warning->setSeverity(AphrontErrorView::SEVERITY_WARNING);
$warning->setTitle('User Required');
$warning->appendChild(
'This filter requires that a user be specified above.');
$panels[] = $warning;
} else {
$query = $this->buildQuery($this->filter, $params['phid']);
$pager = null;
if ($this->getFilterAllowsPaging($this->filter)) {
$pager = new AphrontPagerView();
$pager->setOffset($request->getInt('page'));
$pager->setPageSize(1000);
$pager->setURI($uri, 'page');
$query->setOffset($pager->getOffset());
$query->setLimit($pager->getPageSize() + 1);
}
foreach ($controls as $control) {
$this->applyControlToQuery($control, $query, $params);
}
$revisions = $query->execute();
if ($pager) {
$revisions = $pager->sliceResults($revisions);
}
$views = $this->buildViews($this->filter, $params['phid'], $revisions);
$view_objects = array();
foreach ($views as $view) {
if (empty($view['special'])) {
$view_objects[] = $view['view'];
}
}
$phids = array_mergev(mpull($view_objects, 'getRequiredHandlePHIDs'));
$phids[] = $params['phid'];
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
foreach ($views as $view) {
if (empty($view['special'])) {
$view['view']->setHandles($handles);
}
$panel = new AphrontPanelView();
$panel->setHeader($view['title']);
$panel->appendChild($view['view']);
if ($pager) {
$panel->appendChild($pager);
}
$panels[] = $panel;
}
}
$filter_form = id(new AphrontFormView())
->setMethod('GET')
->setAction('/differential/filter/'.$this->filter.'/')
->setUser($user);
foreach ($controls as $control) {
$control_view = $this->renderControl($control, $handles, $uri, $params);
$filter_form->appendChild($control_view);
}
$filter_form
->addHiddenInput('status', $params['status'])
->addHiddenInput('order', $params['order'])
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Filter Revisions'));
$filter_view = new AphrontListFilterView();
$filter_view->appendChild($filter_form);
if (!$viewer_is_anonymous) {
$create_uri = new PhutilURI('/differential/diff/create/');
$filter_view->addButton(
phutil_render_tag(
'a',
array(
'href' => (string)$create_uri,
'class' => 'green button',
),
'Create Revision'));
}
$side_nav->appendChild($filter_view);
foreach ($panels as $panel) {
$side_nav->appendChild($panel);
}
return $this->buildStandardPageResponse(
$side_nav,
array(
'title' => 'Differential Home',
'tab' => 'revisions',
));
}
private function getFilters() {
return array(
array(null, 'User Revisions'),
array('active', 'Active'),
array('revisions', 'Revisions'),
array('reviews', 'Reviews'),
array('subscribed', 'Subscribed'),
array('drafts', 'Draft Reviews'),
array(null, 'All Revisions'),
array('all', 'All'),
);
}
private function selectFilter(
array $filters,
$requested_filter,
$default_filter) {
// If the user requested a filter, make sure it actually exists.
if ($requested_filter) {
foreach ($filters as $filter) {
if ($filter[0] === $requested_filter) {
return $requested_filter;
}
}
}
// If not, return the default filter.
return $default_filter;
}
private function getFilterRequiresUser($filter) {
static $requires = array(
'active' => true,
'revisions' => true,
'reviews' => true,
'subscribed' => true,
'drafts' => true,
'all' => false,
);
if (!isset($requires[$filter])) {
throw new Exception("Unknown filter '{$filter}'!");
}
return $requires[$filter];
}
private function getFilterAllowsPaging($filter) {
static $allows = array(
'active' => false,
'revisions' => true,
'reviews' => true,
'subscribed' => true,
'drafts' => true,
'all' => true,
);
if (!isset($allows[$filter])) {
throw new Exception("Unknown filter '{$filter}'!");
}
return $allows[$filter];
}
private function getFilterControls($filter) {
static $controls = array(
'active' => array('phid'),
'revisions' => array('phid', 'status', 'order'),
'reviews' => array('phid', 'status', 'order'),
'subscribed' => array('subscriber', 'status', 'order'),
'drafts' => array('phid', 'status', 'order'),
'all' => array('status', 'order'),
);
if (!isset($controls[$filter])) {
throw new Exception("Unknown filter '{$filter}'!");
}
return $controls[$filter];
}
private function buildQuery($filter, $user_phid) {
$query = new DifferentialRevisionQuery();
$query->needRelationships(true);
switch ($filter) {
case 'active':
$query->withResponsibleUsers(array($user_phid));
$query->withStatus(DifferentialRevisionQuery::STATUS_OPEN);
$query->setLimit(null);
break;
case 'revisions':
$query->withAuthors(array($user_phid));
break;
case 'reviews':
$query->withReviewers(array($user_phid));
break;
case 'subscribed':
$query->withSubscribers(array($user_phid));
break;
case 'drafts':
$query->withDraftRepliesByAuthors(array($user_phid));
break;
case 'all':
break;
default:
throw new Exception("Unknown filter '{$filter}'!");
}
return $query;
}
private function renderControl(
$control,
array $handles,
PhutilURI $uri,
array $params) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
switch ($control) {
case 'subscriber':
case 'phid':
$view_phid = $params['phid'];
$value = array();
if ($view_phid) {
$value = array(
$view_phid => $handles[$view_phid]->getFullName(),
);
}
if ($control == 'subscriber') {
$source = '/typeahead/common/allmailable/';
$label = 'View Subscriber';
} else {
$source = '/typeahead/common/accounts/';
$label = 'View User';
}
return id(new AphrontFormTokenizerControl())
->setDatasource($source)
->setLabel($label)
->setName('view_user')
->setValue($value)
->setLimit(1);
case 'status':
return id(new AphrontFormToggleButtonsControl())
->setLabel('Status')
->setValue($params['status'])
->setBaseURI($uri, 'status')
->setButtons(
array(
'all' => 'All',
'open' => 'Open',
- 'committed' => 'Committed',
+ 'closed' => 'Closed',
'abandoned' => 'Abandoned',
));
case 'order':
return id(new AphrontFormToggleButtonsControl())
->setLabel('Order')
->setValue($params['order'])
->setBaseURI($uri, 'order')
->setButtons(
array(
'modified' => 'Updated',
'created' => 'Created',
));
default:
throw new Exception("Unknown control '{$control}'!");
}
}
private function applyControlToQuery($control, $query, array $params) {
switch ($control) {
case 'phid':
case 'subscriber':
// Already applied by query construction.
break;
case 'status':
if ($params['status'] == 'open') {
$query->withStatus(DifferentialRevisionQuery::STATUS_OPEN);
- } else if ($params['status'] == 'committed') {
- $query->withStatus(DifferentialRevisionQuery::STATUS_COMMITTED);
+ } else if ($params['status'] == 'closed') {
+ $query->withStatus(DifferentialRevisionQuery::STATUS_CLOSED);
} else if ($params['status'] == 'abandoned') {
$query->withStatus(DifferentialRevisionQuery::STATUS_ABANDONED);
}
break;
case 'order':
if ($params['order'] == 'created') {
$query->setOrder(DifferentialRevisionQuery::ORDER_CREATED);
}
break;
default:
throw new Exception("Unknown control '{$control}'!");
}
}
private function buildViews($filter, $user_phid, array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$user = $this->getRequest()->getUser();
$template = id(new DifferentialRevisionListView())
->setUser($user)
->setFields(DifferentialRevisionListView::getDefaultFields());
$views = array();
switch ($filter) {
case 'active':
list($active, $waiting) = DifferentialRevisionQuery::splitResponsible(
$revisions,
$user_phid);
$view = id(clone $template)
->setRevisions($active)
->setNoDataString("You have no active revisions requiring action.");
$views[] = array(
'title' => 'Action Required',
'view' => $view,
);
// Flags are sort of private, so only show the flag panel if you're
// looking at your own requests.
if ($user_phid == $user->getPHID()) {
$flags = id(new PhabricatorFlagQuery())
->withOwnerPHIDs(array($user_phid))
->withTypes(array(PhabricatorPHIDConstants::PHID_TYPE_DREV))
->needHandles(true)
->execute();
if ($flags) {
$view = id(new PhabricatorFlagListView())
->setFlags($flags)
->setUser($user);
$views[] = array(
'title' => 'Flagged Revisions',
'view' => $view,
'special' => true,
);
}
}
$view = id(clone $template)
->setRevisions($waiting)
->setNoDataString("You have no active revisions waiting on others.");
$views[] = array(
'title' => 'Waiting On Others',
'view' => $view,
);
break;
case 'revisions':
case 'reviews':
case 'subscribed':
case 'drafts':
case 'all':
$titles = array(
'revisions' => 'Revisions by Author',
'reviews' => 'Revisions by Reviewer',
'subscribed' => 'Revisions by Subscriber',
'all' => 'Revisions',
);
$view = id(clone $template)
->setRevisions($revisions);
$views[] = array(
'title' => idx($titles, $filter),
'view' => $view,
);
break;
default:
throw new Exception("Unknown filter '{$filter}'!");
}
return $views;
}
}
diff --git a/src/applications/differential/controller/revisionstats/DifferentialRevisionStatsController.php b/src/applications/differential/controller/revisionstats/DifferentialRevisionStatsController.php
index 07580a0872..8c2ae1e2ba 100644
--- a/src/applications/differential/controller/revisionstats/DifferentialRevisionStatsController.php
+++ b/src/applications/differential/controller/revisionstats/DifferentialRevisionStatsController.php
@@ -1,170 +1,170 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class DifferentialRevisionStatsController extends DifferentialController {
private $filter;
public function shouldRequireLogin() {
return true;
}
private function loadRevisions($phid) {
$table = new DifferentialRevision();
$conn_r = $table->establishConnection('r');
$rows = queryfx_all(
$conn_r,
'SELECT revisions.* FROM %T revisions ' .
'JOIN %T comments ON comments.revisionID = revisions.id ' .
'JOIN (' .
' SELECT revisionID FROM %T WHERE objectPHID = %s ' .
' UNION ALL ' .
' SELECT id from differential_revision WHERE authorPHID = %s) rel ' .
'ON (comments.revisionID = rel.revisionID)' .
'WHERE comments.action = %s' .
'AND comments.authorPHID = %s',
$table->getTableName(),
id(new DifferentialComment())->getTableName(),
DifferentialRevision::RELATIONSHIP_TABLE,
$phid,
$phid,
$this->filter,
$phid
);
return $table->loadAllFromArray($rows);
}
private function loadComments($phid) {
$table = new DifferentialComment();
$conn_r = $table->establishConnection('r');
$rows = queryfx_all(
$conn_r,
'SELECT comments.* FROM %T comments ' .
'JOIN (' .
' SELECT revisionID FROM %T WHERE objectPHID = %s ' .
' UNION ALL ' .
' SELECT id from differential_revision WHERE authorPHID = %s) rel ' .
'ON (comments.revisionID = rel.revisionID)' .
'WHERE comments.action = %s' .
'AND comments.authorPHID = %s',
$table->getTableName(),
DifferentialRevision::RELATIONSHIP_TABLE,
$phid,
$phid,
$this->filter,
$phid
);
return $table->loadAllFromArray($rows);
}
public function willProcessRequest(array $data) {
$this->filter = idx($data, 'filter');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
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));
}
$params = array_filter(
array(
'phid' => $request->getStr('phid'),
));
// Fill in the defaults we'll actually use for calculations if any
// parameters are missing.
$params += array(
'phid' => $user->getPHID(),
);
$side_nav = new AphrontSideNavFilterView();
$side_nav->setBaseURI(id(new PhutilURI('/differential/stats/'))
->alter('phid', $params['phid']));
foreach (array(
- DifferentialAction::ACTION_COMMIT,
+ DifferentialAction::ACTION_CLOSE,
DifferentialAction::ACTION_ACCEPT,
DifferentialAction::ACTION_REJECT,
DifferentialAction::ACTION_UPDATE,
DifferentialAction::ACTION_COMMENT,
) as $action) {
$verb = ucfirst(DifferentialAction::getActionPastTenseVerb($action));
$side_nav->addFilter($action, $verb);
}
$this->filter =
$side_nav->selectFilter($this->filter,
- DifferentialAction::ACTION_COMMIT);
+ DifferentialAction::ACTION_CLOSE);
$panels = array();
$handles = id(new PhabricatorObjectHandleData(array($params['phid'])))
->loadHandles();
$filter_form = id(new AphrontFormView())
->setAction('/differential/stats/'.$this->filter.'/')
->setUser($user);
$filter_form->appendChild(
$this->renderControl($params['phid'], $handles));
$filter_form->appendChild(id(new AphrontFormSubmitControl())
->setValue('Filter Revisions'));
$side_nav->appendChild($filter_form);
$comments = $this->loadComments($params['phid']);
$revisions = $this->loadRevisions($params['phid']);
$panel = new AphrontPanelView();
$panel->setHeader('Differential rate analysis');
$panel->appendChild(
id(new DifferentialRevisionStatsView())
->setComments($comments)
->setRevisions($revisions)
->setUser($user));
$panels[] = $panel;
foreach ($panels as $panel) {
$side_nav->appendChild($panel);
}
return $this->buildStandardPageResponse(
$side_nav,
array(
'title' => 'Differential statistics',
));
}
private function renderControl($view_phid, $handles) {
$value = array();
if ($view_phid) {
$value = array(
$view_phid => $handles[$view_phid]->getFullName(),
);
}
return id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/users/')
->setLabel('View User')
->setName('view_user')
->setValue($value)
->setLimit(1);
}
}
diff --git a/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php b/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
index c23cbd4c58..90fe60bc1e 100644
--- a/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
+++ b/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
@@ -1,799 +1,799 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class DifferentialRevisionViewController extends DifferentialController {
private $revisionID;
public function shouldRequireLogin() {
return !$this->allowsAnonymousAccess();
}
public function willProcessRequest(array $data) {
$this->revisionID = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$viewer_is_anonymous = !$user->isLoggedIn();
$revision = id(new DifferentialRevision())->load($this->revisionID);
if (!$revision) {
return new Aphront404Response();
}
$revision->loadRelationships();
$diffs = $revision->loadDiffs();
if (!$diffs) {
throw new Exception(
"This revision has no diffs. Something has gone quite wrong.");
}
$diff_vs = $request->getInt('vs');
$target = end($diffs);
$target_id = $request->getInt('id');
if ($target_id) {
if (isset($diffs[$target_id])) {
$target = $diffs[$target_id];
}
}
$diffs = mpull($diffs, null, 'getID');
if (empty($diffs[$diff_vs])) {
$diff_vs = null;
}
list($aux_fields, $props) = $this->loadAuxiliaryFieldsAndProperties(
$revision,
$target,
array(
'local:commits',
'arc:unit',
));
list($changesets, $vs_map, $rendering_references) =
$this->loadChangesetsAndVsMap($diffs, $diff_vs, $target);
$comments = $revision->loadComments();
$comments = array_merge(
$this->getImplicitComments($revision),
$comments);
$all_changesets = $changesets;
$inlines = $this->loadInlineComments($comments, $all_changesets);
$object_phids = array_merge(
$revision->getReviewers(),
$revision->getCCPHIDs(),
$revision->loadCommitPHIDs(),
array(
$revision->getAuthorPHID(),
$user->getPHID(),
),
mpull($comments, 'getAuthorPHID'));
foreach ($comments as $comment) {
$metadata = $comment->getMetadata();
$added_reviewers = idx(
$metadata,
DifferentialComment::METADATA_ADDED_REVIEWERS);
if ($added_reviewers) {
foreach ($added_reviewers as $phid) {
$object_phids[] = $phid;
}
}
$added_ccs = idx(
$metadata,
DifferentialComment::METADATA_ADDED_CCS);
if ($added_ccs) {
foreach ($added_ccs as $phid) {
$object_phids[] = $phid;
}
}
}
foreach ($revision->getAttached() as $type => $phids) {
foreach ($phids as $phid => $info) {
$object_phids[] = $phid;
}
}
$aux_phids = array();
foreach ($aux_fields as $key => $aux_field) {
$aux_phids[$key] = $aux_field->getRequiredHandlePHIDsForRevisionView();
}
$object_phids = array_merge($object_phids, array_mergev($aux_phids));
$object_phids = array_unique($object_phids);
$handles = id(new PhabricatorObjectHandleData($object_phids))
->loadHandles();
foreach ($aux_fields as $key => $aux_field) {
// Make sure each field only has access to handles it specifically
// requested, not all handles. Otherwise you can get a field which works
// only in the presence of other fields.
$aux_field->setHandles(array_select_keys($handles, $aux_phids[$key]));
}
$reviewer_warning = null;
$has_live_reviewer = false;
foreach ($revision->getReviewers() as $reviewer) {
if (!$handles[$reviewer]->isDisabled()) {
$has_live_reviewer = true;
}
}
if (!$has_live_reviewer) {
$reviewer_warning = new AphrontErrorView();
$reviewer_warning->setSeverity(AphrontErrorView::SEVERITY_WARNING);
$reviewer_warning->setTitle('No Active Reviewers');
if ($revision->getReviewers()) {
$reviewer_warning->appendChild(
'<p>All specified reviewers are disabled. You may want to add '.
'some new reviewers.</p>');
} else {
$reviewer_warning->appendChild(
'<p>This revision has no specified reviewers. You may want to '.
'add some.</p>');
}
}
$request_uri = $request->getRequestURI();
$limit = 100;
$large = $request->getStr('large');
if (count($changesets) > $limit && !$large) {
$count = number_format(count($changesets));
$warning = new AphrontErrorView();
$warning->setTitle('Very Large Diff');
$warning->setSeverity(AphrontErrorView::SEVERITY_WARNING);
$warning->setWidth(AphrontErrorView::WIDTH_WIDE);
$warning->appendChild(
"<p>This diff is very large and affects {$count} files. Use ".
"Table of Contents to open files in a standalone view. ".
"<strong>".
phutil_render_tag(
'a',
array(
'href' => $request_uri
->alter('large', 'true')
->setFragment('differential-review-toc'),
),
'Show All Files Inline').
"</strong>");
$warning = $warning->render();
$visible_changesets = array();
} else {
$warning = null;
$visible_changesets = $changesets;
}
$revision_detail = new DifferentialRevisionDetailView();
$revision_detail->setRevision($revision);
$revision_detail->setAuxiliaryFields($aux_fields);
$actions = $this->getRevisionActions($revision);
$custom_renderer_class = PhabricatorEnv::getEnvConfig(
'differential.revision-custom-detail-renderer');
if ($custom_renderer_class) {
// TODO: build a better version of the action links and deprecate the
// whole DifferentialRevisionDetailRenderer class.
PhutilSymbolLoader::loadClass($custom_renderer_class);
$custom_renderer =
newv($custom_renderer_class, array());
$actions = array_merge(
$actions,
$custom_renderer->generateActionLinks($revision, $target));
}
$whitespace = $request->getStr(
'whitespace',
DifferentialChangesetParser::WHITESPACE_IGNORE_ALL);
$arc_project = $target->loadArcanistProject();
if ($arc_project) {
$symbol_indexes = $this->buildSymbolIndexes(
$target,
$arc_project,
$visible_changesets);
$repository = $arc_project->loadRepository();
} else {
$symbol_indexes = array();
$repository = null;
}
$revision_detail->setActions($actions);
$revision_detail->setUser($user);
$comment_view = new DifferentialRevisionCommentListView();
$comment_view->setComments($comments);
$comment_view->setHandles($handles);
$comment_view->setInlineComments($inlines);
$comment_view->setChangesets($all_changesets);
$comment_view->setUser($user);
$comment_view->setTargetDiff($target);
$comment_view->setVersusDiffID($diff_vs);
$changeset_view = new DifferentialChangesetListView();
$changeset_view->setChangesets($visible_changesets);
if (!$viewer_is_anonymous) {
$changeset_view->setInlineCommentControllerURI(
'/differential/comment/inline/edit/'.$revision->getID().'/');
}
$changeset_view->setStandaloneURI('/differential/changeset/');
$changeset_view->setRawFileURIs(
'/differential/changeset/?view=old',
'/differential/changeset/?view=new');
$changeset_view->setUser($user);
$changeset_view->setDiff($target);
$changeset_view->setRenderingReferences($rendering_references);
$changeset_view->setVsMap($vs_map);
$changeset_view->setWhitespace($whitespace);
if ($repository) {
$changeset_view->setRepository($repository, $target);
}
$changeset_view->setSymbolIndexes($symbol_indexes);
$diff_history = new DifferentialRevisionUpdateHistoryView();
$diff_history->setDiffs($diffs);
$diff_history->setSelectedVersusDiffID($diff_vs);
$diff_history->setSelectedDiffID($target->getID());
$diff_history->setSelectedWhitespace($whitespace);
$diff_history->setUser($user);
$local_view = new DifferentialLocalCommitsView();
$local_view->setUser($user);
$local_view->setLocalCommits(idx($props, 'local:commits'));
if ($repository) {
$other_revisions = $this->loadOtherRevisions(
$changesets,
$target,
$repository);
} else {
$other_revisions = array();
}
$other_view = null;
if ($other_revisions) {
$other_view = $this->renderOtherRevisions($other_revisions);
}
$toc_view = new DifferentialDiffTableOfContentsView();
$toc_view->setChangesets($changesets);
$toc_view->setVisibleChangesets($visible_changesets);
$toc_view->setUnitTestData(idx($props, 'arc:unit', array()));
if ($repository) {
$toc_view->setRepository($repository);
}
$toc_view->setDiff($target);
$toc_view->setUser($user);
$toc_view->setStandaloneViewLink(empty($visible_changesets));
$toc_view->setVsMap($vs_map);
$toc_view->setRevisionID($revision->getID());
$toc_view->setWhitespace($whitespace);
if (!$viewer_is_anonymous) {
$draft = id(new PhabricatorDraft())->loadOneWhere(
'authorPHID = %s AND draftKey = %s',
$user->getPHID(),
'differential-comment-'.$revision->getID());
if ($draft) {
$draft = $draft->getDraft();
} else {
$draft = null;
}
$comment_form = new DifferentialAddCommentView();
$comment_form->setRevision($revision);
$comment_form->setActions($this->getRevisionCommentActions($revision));
$comment_form->setActionURI('/differential/comment/save/');
$comment_form->setUser($user);
$comment_form->setDraft($draft);
}
$pane_id = celerity_generate_unique_node_id();
Javelin::initBehavior(
'differential-keyboard-navigation',
array(
'haunt' => $pane_id,
));
$page_pane = id(new DifferentialPrimaryPaneView())
->setLineWidthFromChangesets($changesets)
->setID($pane_id)
->appendChild(
$comment_view->render().
$diff_history->render().
$warning.
$local_view->render().
$toc_view->render().
$other_view.
$changeset_view->render());
if ($comment_form) {
$page_pane->appendChild($comment_form->render());
}
return $this->buildStandardPageResponse(
array(
$reviewer_warning,
$revision_detail,
$page_pane,
),
array(
'title' => 'D'.$revision->getID().' '.$revision->getTitle(),
));
}
private function getImplicitComments(DifferentialRevision $revision) {
$template = new DifferentialComment();
$template->setAuthorPHID($revision->getAuthorPHID());
$template->setRevisionID($revision->getID());
$template->setDateCreated($revision->getDateCreated());
$comments = array();
if (strlen($revision->getSummary())) {
$summary_comment = clone $template;
$summary_comment->setContent($revision->getSummary());
$summary_comment->setAction(DifferentialAction::ACTION_SUMMARIZE);
$comments[] = $summary_comment;
}
if (strlen($revision->getTestPlan())) {
$testplan_comment = clone $template;
$testplan_comment->setContent($revision->getTestPlan());
$testplan_comment->setAction(DifferentialAction::ACTION_TESTPLAN);
$comments[] = $testplan_comment;
}
return $comments;
}
private function getRevisionActions(DifferentialRevision $revision) {
$user = $this->getRequest()->getUser();
$viewer_phid = $user->getPHID();
$viewer_is_owner = ($revision->getAuthorPHID() == $viewer_phid);
$viewer_is_reviewer = in_array($viewer_phid, $revision->getReviewers());
$viewer_is_cc = in_array($viewer_phid, $revision->getCCPHIDs());
$viewer_is_anonymous = !$this->getRequest()->getUser()->isLoggedIn();
$status = $revision->getStatus();
$revision_id = $revision->getID();
$revision_phid = $revision->getPHID();
$links = array();
if ($viewer_is_owner) {
$links[] = array(
'class' => 'revision-edit',
'href' => "/differential/revision/edit/{$revision_id}/",
'name' => 'Edit Revision',
);
}
if (!$viewer_is_anonymous) {
require_celerity_resource('phabricator-flag-css');
$flag = PhabricatorFlagQuery::loadUserFlag($user, $revision_phid);
if ($flag) {
$class = PhabricatorFlagColor::getCSSClass($flag->getColor());
$color = PhabricatorFlagColor::getColorName($flag->getColor());
$links[] = array(
'class' => 'flag-clear '.$class,
'href' => '/flag/delete/'.$flag->getID().'/',
'name' => phutil_escape_html('Remove '.$color.' Flag'),
'sigil' => 'workflow',
);
} else {
$links[] = array(
'class' => 'flag-add phabricator-flag-ghost',
'href' => '/flag/edit/'.$revision_phid.'/',
'name' => 'Flag Revision',
'sigil' => 'workflow',
);
}
if (!$viewer_is_owner && !$viewer_is_reviewer) {
$action = $viewer_is_cc ? 'rem' : 'add';
$links[] = array(
'class' => $viewer_is_cc ? 'subscribe-rem' : 'subscribe-add',
'href' => "/differential/subscribe/{$action}/{$revision_id}/",
'name' => $viewer_is_cc ? 'Unsubscribe' : 'Subscribe',
'instant' => true,
);
} else {
$links[] = array(
'class' => 'subscribe-rem unavailable',
'name' => 'Automatically Subscribed',
);
}
require_celerity_resource('phabricator-object-selector-css');
require_celerity_resource('javelin-behavior-phabricator-object-selector');
$links[] = array(
'class' => 'action-dependencies',
'name' => 'Edit Dependencies',
'href' => "/search/attach/{$revision_phid}/DREV/dependencies/",
'sigil' => 'workflow',
);
if (PhabricatorEnv::getEnvConfig('maniphest.enabled')) {
$links[] = array(
'class' => 'attach-maniphest',
'name' => 'Edit Maniphest Tasks',
'href' => "/search/attach/{$revision_phid}/TASK/",
'sigil' => 'workflow',
);
}
if ($user->getIsAdmin()) {
$links[] = array(
'class' => 'transcripts-metamta',
'name' => 'MetaMTA Transcripts',
'href' => "/mail/?phid={$revision_phid}",
);
}
$links[] = array(
'class' => 'transcripts-herald',
'name' => 'Herald Transcripts',
'href' => "/herald/transcript/?phid={$revision_phid}",
);
}
return $links;
}
private function getRevisionCommentActions(DifferentialRevision $revision) {
$actions = array(
DifferentialAction::ACTION_COMMENT => true,
);
$viewer = $this->getRequest()->getUser();
$viewer_phid = $viewer->getPHID();
$viewer_is_owner = ($viewer_phid == $revision->getAuthorPHID());
$viewer_is_reviewer = in_array($viewer_phid, $revision->getReviewers());
$viewer_did_accept = ($viewer_phid === $revision->loadReviewedBy());
$status = $revision->getStatus();
if ($viewer_is_owner) {
switch ($status) {
case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
$actions[DifferentialAction::ACTION_ABANDON] = true;
$actions[DifferentialAction::ACTION_RETHINK] = true;
break;
case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
$actions[DifferentialAction::ACTION_ABANDON] = true;
$actions[DifferentialAction::ACTION_REQUEST] = true;
break;
case ArcanistDifferentialRevisionStatus::ACCEPTED:
$actions[DifferentialAction::ACTION_ABANDON] = true;
$actions[DifferentialAction::ACTION_REQUEST] = true;
$actions[DifferentialAction::ACTION_RETHINK] = true;
- $actions[DifferentialAction::ACTION_COMMIT] = true;
+ $actions[DifferentialAction::ACTION_CLOSE] = true;
break;
- case ArcanistDifferentialRevisionStatus::COMMITTED:
+ case ArcanistDifferentialRevisionStatus::CLOSED:
break;
case ArcanistDifferentialRevisionStatus::ABANDONED:
$actions[DifferentialAction::ACTION_RECLAIM] = true;
break;
}
} else {
switch ($status) {
case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
$actions[DifferentialAction::ACTION_ACCEPT] = true;
$actions[DifferentialAction::ACTION_REJECT] = true;
$actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
break;
case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
$actions[DifferentialAction::ACTION_ACCEPT] = true;
$actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
break;
case ArcanistDifferentialRevisionStatus::ACCEPTED:
$actions[DifferentialAction::ACTION_REJECT] = true;
$actions[DifferentialAction::ACTION_RESIGN] =
$viewer_is_reviewer && !$viewer_did_accept;
break;
- case ArcanistDifferentialRevisionStatus::COMMITTED:
+ case ArcanistDifferentialRevisionStatus::CLOSED:
case ArcanistDifferentialRevisionStatus::ABANDONED:
break;
}
- if ($status != ArcanistDifferentialRevisionStatus::COMMITTED) {
+ if ($status != ArcanistDifferentialRevisionStatus::CLOSED) {
$actions[DifferentialAction::ACTION_CLAIM] = true;
}
}
$actions[DifferentialAction::ACTION_ADDREVIEWERS] = true;
$actions[DifferentialAction::ACTION_ADDCCS] = true;
$actions = array_keys(array_filter($actions));
$actions_dict = array();
foreach ($actions as $action) {
$actions_dict[$action] = DifferentialAction::getActionVerb($action);
}
return $actions_dict;
}
private function loadInlineComments(array $comments, array &$changesets) {
assert_instances_of($comments, 'DifferentialComment');
assert_instances_of($changesets, 'DifferentialChangeset');
$inline_comments = array();
$comment_ids = array_filter(mpull($comments, 'getID'));
if (!$comment_ids) {
return $inline_comments;
}
$inline_comments = id(new DifferentialInlineComment())
->loadAllWhere(
'commentID in (%Ld)',
$comment_ids);
$load_changesets = array();
foreach ($inline_comments as $inline) {
$changeset_id = $inline->getChangesetID();
if (isset($changesets[$changeset_id])) {
continue;
}
$load_changesets[$changeset_id] = true;
}
$more_changesets = array();
if ($load_changesets) {
$changeset_ids = array_keys($load_changesets);
$more_changesets += id(new DifferentialChangeset())
->loadAllWhere(
'id IN (%Ld)',
$changeset_ids);
}
if ($more_changesets) {
$changesets += $more_changesets;
$changesets = msort($changesets, 'getSortKey');
}
return $inline_comments;
}
private function loadChangesetsAndVsMap(
array $diffs,
$diff_vs,
DifferentialDiff $target) {
assert_instances_of($diffs, 'DifferentialDiff');
$load_ids = array();
if ($diff_vs) {
$load_ids[] = $diff_vs;
}
$load_ids[] = $target->getID();
$raw_changesets = id(new DifferentialChangeset())
->loadAllWhere(
'diffID IN (%Ld)',
$load_ids);
$changeset_groups = mgroup($raw_changesets, 'getDiffID');
$changesets = idx($changeset_groups, $target->getID(), array());
$changesets = mpull($changesets, null, 'getID');
$refs = array();
foreach ($changesets as $changeset) {
$refs[$changeset->getID()] = $changeset->getID();
}
$vs_map = array();
if ($diff_vs) {
$vs_changesets = idx($changeset_groups, $diff_vs, array());
$vs_changesets = mpull($vs_changesets, null, 'getFilename');
foreach ($changesets as $key => $changeset) {
$file = $changeset->getFilename();
if (isset($vs_changesets[$file])) {
$vs_map[$changeset->getID()] = $vs_changesets[$file]->getID();
$refs[$changeset->getID()] =
$changeset->getID().'/'.$vs_changesets[$file]->getID();
unset($vs_changesets[$file]);
} else {
$refs[$changeset->getID()] = $changeset->getID();
}
}
foreach ($vs_changesets as $changeset) {
$changesets[$changeset->getID()] = $changeset;
$vs_map[$changeset->getID()] = -1;
$refs[$changeset->getID()] = $changeset->getID().'/-1';
}
}
$changesets = msort($changesets, 'getSortKey');
return array($changesets, $vs_map, $refs);
}
private function loadAuxiliaryFieldsAndProperties(
DifferentialRevision $revision,
DifferentialDiff $diff,
array $special_properties) {
$aux_fields = DifferentialFieldSelector::newSelector()
->getFieldSpecifications();
foreach ($aux_fields as $key => $aux_field) {
if (!$aux_field->shouldAppearOnRevisionView()) {
unset($aux_fields[$key]);
} else {
$aux_field->setUser($this->getRequest()->getUser());
}
}
$aux_fields = DifferentialAuxiliaryField::loadFromStorage(
$revision,
$aux_fields);
$aux_props = array();
foreach ($aux_fields as $key => $aux_field) {
$aux_field->setDiff($diff);
$aux_props[$key] = $aux_field->getRequiredDiffProperties();
}
$required_properties = array_mergev($aux_props);
$required_properties = array_merge(
$required_properties,
$special_properties);
$property_map = array();
if ($required_properties) {
$properties = id(new DifferentialDiffProperty())->loadAllWhere(
'diffID = %d AND name IN (%Ls)',
$diff->getID(),
$required_properties);
$property_map = mpull($properties, 'getData', 'getName');
}
foreach ($aux_fields as $key => $aux_field) {
// Give each field only the properties it specifically required, and
// set 'null' for each requested key which we didn't actually load a
// value for (otherwise, getDiffProperty() will throw).
if ($aux_props[$key]) {
$props = array_select_keys($property_map, $aux_props[$key]) +
array_fill_keys($aux_props[$key], null);
} else {
$props = array();
}
$aux_field->setDiffProperties($props);
}
return array(
$aux_fields,
array_select_keys(
$property_map,
$special_properties));
}
private function buildSymbolIndexes(
DifferentialDiff $target,
PhabricatorRepositoryArcanistProject $arc_project,
array $visible_changesets) {
assert_instances_of($visible_changesets, 'DifferentialChangeset');
$engine = PhabricatorSyntaxHighlighter::newEngine();
$langs = $arc_project->getSymbolIndexLanguages();
if (!$langs) {
return array();
}
$symbol_indexes = array();
$project_phids = array_merge(
array($arc_project->getPHID()),
nonempty($arc_project->getSymbolIndexProjects(), array()));
$indexed_langs = array_fill_keys($langs, true);
foreach ($visible_changesets as $key => $changeset) {
$lang = $engine->getLanguageFromFilename($changeset->getFilename());
if (isset($indexed_langs[$lang])) {
$symbol_indexes[$key] = array(
'lang' => $lang,
'projects' => $project_phids,
);
}
}
return $symbol_indexes;
}
private function loadOtherRevisions(
array $changesets,
DifferentialDiff $target,
PhabricatorRepository $repository) {
assert_instances_of($changesets, 'DifferentialChangeset');
$paths = array();
foreach ($changesets as $changeset) {
$paths[] = $changeset->getAbsoluteRepositoryPath(
$repository,
$target);
}
if (!$paths) {
return array();
}
$path_map = id(new DiffusionPathIDQuery($paths))->loadPathIDs();
if (!$path_map) {
return array();
}
$query = id(new DifferentialRevisionQuery())
->withStatus(DifferentialRevisionQuery::STATUS_OPEN)
->setOrder(DifferentialRevisionQuery::ORDER_PATH_MODIFIED)
->setLimit(10)
->needRelationships(true);
foreach ($path_map as $path => $path_id) {
$query->withPath($repository->getID(), $path_id);
}
$results = $query->execute();
// Strip out *this* revision.
foreach ($results as $key => $result) {
if ($result->getID() == $this->revisionID) {
unset($results[$key]);
}
}
return $results;
}
private function renderOtherRevisions(array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$view = id(new DifferentialRevisionListView())
->setRevisions($revisions)
->setFields(DifferentialRevisionListView::getDefaultFields())
->setUser($this->getRequest()->getUser());
$phids = $view->getRequiredHandlePHIDs();
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
$view->setHandles($handles);
return
'<div class="differential-panel">'.
'<h1>Open Revisions Affecting These Files</h1>'.
$view->render().
'</div>';
}
}
diff --git a/src/applications/differential/editor/comment/DifferentialCommentEditor.php b/src/applications/differential/editor/comment/DifferentialCommentEditor.php
index 93f9b8f8f1..ab76ace9a2 100644
--- a/src/applications/differential/editor/comment/DifferentialCommentEditor.php
+++ b/src/applications/differential/editor/comment/DifferentialCommentEditor.php
@@ -1,634 +1,633 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class DifferentialCommentEditor {
protected $revision;
protected $actorPHID;
protected $action;
protected $attachInlineComments;
protected $message;
protected $changedByCommit;
protected $addedReviewers = array();
private $addedCCs = array();
private $parentMessageID;
private $contentSource;
private $isDaemonWorkflow;
public function __construct(
DifferentialRevision $revision,
$actor_phid,
$action) {
$this->revision = $revision;
$this->actorPHID = $actor_phid;
$this->action = $action;
}
public function setParentMessageID($parent_message_id) {
$this->parentMessageID = $parent_message_id;
return $this;
}
public function setMessage($message) {
$this->message = $message;
return $this;
}
public function setAttachInlineComments($attach) {
$this->attachInlineComments = $attach;
return $this;
}
public function setChangedByCommit($changed_by_commit) {
$this->changedByCommit = $changed_by_commit;
return $this;
}
public function getChangedByCommit() {
return $this->changedByCommit;
}
public function setAddedReviewers(array $added_reviewers) {
$this->addedReviewers = $added_reviewers;
return $this;
}
public function getAddedReviewers() {
return $this->addedReviewers;
}
public function setAddedCCs($added_ccs) {
$this->addedCCs = $added_ccs;
return $this;
}
public function getAddedCCs() {
return $this->addedCCs;
}
public function setContentSource(PhabricatorContentSource $content_source) {
$this->contentSource = $content_source;
return $this;
}
public function setIsDaemonWorkflow($is_daemon) {
$this->isDaemonWorkflow = $is_daemon;
return $this;
}
public function save() {
$revision = $this->revision;
$action = $this->action;
$actor_phid = $this->actorPHID;
$actor = id(new PhabricatorUser())->loadOneWhere('PHID = %s', $actor_phid);
$actor_is_author = ($actor_phid == $revision->getAuthorPHID());
$revision_status = $revision->getStatus();
$revision->loadRelationships();
$reviewer_phids = $revision->getReviewers();
if ($reviewer_phids) {
$reviewer_phids = array_combine($reviewer_phids, $reviewer_phids);
}
$metadata = array();
$inline_comments = array();
if ($this->attachInlineComments) {
$inline_comments = id(new DifferentialInlineComment())->loadAllWhere(
'authorPHID = %s AND revisionID = %d AND commentID IS NULL',
$this->actorPHID,
$revision->getID());
}
switch ($action) {
case DifferentialAction::ACTION_COMMENT:
if (!$this->message && !$inline_comments) {
throw new DifferentialActionHasNoEffectException(
"You are submitting an empty comment with no action: ".
"you must act on the revision or post a comment.");
}
break;
case DifferentialAction::ACTION_RESIGN:
if ($actor_is_author) {
throw new Exception('You can not resign from your own revision!');
}
if (empty($reviewer_phids[$actor_phid])) {
throw new DifferentialActionHasNoEffectException(
"You can not resign from this revision because you are not ".
"a reviewer.");
}
DifferentialRevisionEditor::alterReviewers(
$revision,
$reviewer_phids,
$rem = array($actor_phid),
$add = array(),
$actor_phid);
break;
case DifferentialAction::ACTION_ABANDON:
if (!$actor_is_author) {
throw new Exception('You can only abandon your own revisions.');
}
- if ($revision_status == ArcanistDifferentialRevisionStatus::COMMITTED) {
+ if ($revision_status == ArcanistDifferentialRevisionStatus::CLOSED) {
throw new DifferentialActionHasNoEffectException(
"You can not abandon this revision because it has already ".
- "been committed.");
+ "been closed.");
}
if ($revision_status == ArcanistDifferentialRevisionStatus::ABANDONED) {
throw new DifferentialActionHasNoEffectException(
"You can not abandon this revision because it has already ".
"been abandoned.");
}
$revision->setStatus(ArcanistDifferentialRevisionStatus::ABANDONED);
break;
case DifferentialAction::ACTION_ACCEPT:
if ($actor_is_author) {
throw new Exception('You can not accept your own revision.');
}
if (($revision_status !=
ArcanistDifferentialRevisionStatus::NEEDS_REVIEW) &&
($revision_status !=
ArcanistDifferentialRevisionStatus::NEEDS_REVISION)) {
switch ($revision_status) {
case ArcanistDifferentialRevisionStatus::ACCEPTED:
throw new DifferentialActionHasNoEffectException(
"You can not accept this revision because someone else ".
"already accepted it.");
case ArcanistDifferentialRevisionStatus::ABANDONED:
throw new DifferentialActionHasNoEffectException(
"You can not accept this revision because it has been ".
"abandoned.");
- case ArcanistDifferentialRevisionStatus::COMMITTED:
+ case ArcanistDifferentialRevisionStatus::CLOSED:
throw new DifferentialActionHasNoEffectException(
"You can not accept this revision because it has already ".
- "been committed.");
+ "been closed.");
default:
throw new Exception(
"Unexpected revision state '{$revision_status}'!");
}
}
$revision
->setStatus(ArcanistDifferentialRevisionStatus::ACCEPTED);
if (!isset($reviewer_phids[$actor_phid])) {
DifferentialRevisionEditor::alterReviewers(
$revision,
$reviewer_phids,
$rem = array(),
$add = array($actor_phid),
$actor_phid);
}
break;
case DifferentialAction::ACTION_REQUEST:
if (!$actor_is_author) {
throw new Exception('You must own a revision to request review.');
}
switch ($revision_status) {
case ArcanistDifferentialRevisionStatus::ACCEPTED:
case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
$revision->setStatus(
ArcanistDifferentialRevisionStatus::NEEDS_REVIEW);
break;
case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
throw new DifferentialActionHasNoEffectException(
"You can not request review of this revision because it has ".
"been abandoned.");
case ArcanistDifferentialRevisionStatus::ABANDONED:
throw new DifferentialActionHasNoEffectException(
"You can not request review of this revision because it has ".
"been abandoned.");
- case ArcanistDifferentialRevisionStatus::COMMITTED:
+ case ArcanistDifferentialRevisionStatus::CLOSED:
throw new DifferentialActionHasNoEffectException(
"You can not request review of this revision because it has ".
- "already been committed.");
+ "already been closed.");
default:
throw new Exception(
"Unexpected revision state '{$revision_status}'!");
}
$added_reviewers = $this->addReviewers();
if ($added_reviewers) {
$key = DifferentialComment::METADATA_ADDED_REVIEWERS;
$metadata[$key] = $added_reviewers;
}
break;
case DifferentialAction::ACTION_REJECT:
if ($actor_is_author) {
throw new Exception(
'You can not request changes to your own revision.');
}
switch ($revision_status) {
case ArcanistDifferentialRevisionStatus::ACCEPTED:
case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
// NOTE: We allow you to reject an already-rejected revision
// because it doesn't create any ambiguity and avoids a rather
// needless dialog.
break;
case ArcanistDifferentialRevisionStatus::ABANDONED:
throw new DifferentialActionHasNoEffectException(
"You can not request changes to this revision because it has ".
"been abandoned.");
- case ArcanistDifferentialRevisionStatus::COMMITTED:
+ case ArcanistDifferentialRevisionStatus::CLOSED:
throw new DifferentialActionHasNoEffectException(
"You can not request changes to this revision because it has ".
- "already been committed.");
+ "already been closed.");
default:
throw new Exception(
"Unexpected revision state '{$revision_status}'!");
}
if (!isset($reviewer_phids[$actor_phid])) {
DifferentialRevisionEditor::alterReviewers(
$revision,
$reviewer_phids,
$rem = array(),
$add = array($actor_phid),
$actor_phid);
}
$revision
->setStatus(ArcanistDifferentialRevisionStatus::NEEDS_REVISION);
break;
case DifferentialAction::ACTION_RETHINK:
if (!$actor_is_author) {
throw new Exception(
"You can not plan changes to somebody else's revision");
}
switch ($revision_status) {
case ArcanistDifferentialRevisionStatus::ACCEPTED:
case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
break;
case ArcanistDifferentialRevisionStatus::ABANDONED:
throw new DifferentialActionHasNoEffectException(
"You can not plan changes to this revision because it has ".
"been abandoned.");
- case ArcanistDifferentialRevisionStatus::COMMITTED:
+ case ArcanistDifferentialRevisionStatus::CLOSED:
throw new DifferentialActionHasNoEffectException(
"You can not plan changes to this revision because it has ".
- "already been committed.");
+ "already been closed.");
default:
throw new Exception(
"Unexpected revision state '{$revision_status}'!");
}
$revision
->setStatus(ArcanistDifferentialRevisionStatus::NEEDS_REVISION);
break;
case DifferentialAction::ACTION_RECLAIM:
if (!$actor_is_author) {
throw new Exception('You can not reclaim a revision you do not own.');
}
if ($revision_status != ArcanistDifferentialRevisionStatus::ABANDONED) {
throw new DifferentialActionHasNoEffectException(
"You can not reclaim this revision because it is not abandoned.");
}
$revision
->setStatus(ArcanistDifferentialRevisionStatus::NEEDS_REVIEW);
break;
- case DifferentialAction::ACTION_COMMIT:
+ case DifferentialAction::ACTION_CLOSE:
- // NOTE: The daemons can mark things committed from any state. We treat
+ // NOTE: The daemons can mark things closed from any state. We treat
// them as completely authoritative.
if (!$this->isDaemonWorkflow) {
if (!$actor_is_author) {
throw new Exception(
- "You can not mark a revision you don't own as committed.");
+ "You can not mark a revision you don't own as closed.");
}
- $status_committed = ArcanistDifferentialRevisionStatus::COMMITTED;
+ $status_closed = ArcanistDifferentialRevisionStatus::CLOSED;
$status_accepted = ArcanistDifferentialRevisionStatus::ACCEPTED;
- if ($revision_status == $status_committed) {
+ if ($revision_status == $status_closed) {
throw new DifferentialActionHasNoEffectException(
- "You can not mark this revision as committed because it has ".
- "already been marked as committed.");
+ "You can not mark this revision as closed because it has ".
+ "already been marked as closed.");
}
if ($revision_status != $status_accepted) {
throw new DifferentialActionHasNoEffectException(
- "You can not mark this revision as committed because it has ".
- "not been accepted.");
+ "You can not mark this revision as closed because it is ".
+ "has not been accepted.");
}
}
if (!$revision->getDateCommitted()) {
$revision->setDateCommitted(time());
}
- $revision
- ->setStatus(ArcanistDifferentialRevisionStatus::COMMITTED);
+ $revision->setStatus(ArcanistDifferentialRevisionStatus::CLOSED);
break;
case DifferentialAction::ACTION_ADDREVIEWERS:
$added_reviewers = $this->addReviewers();
if ($added_reviewers) {
$key = DifferentialComment::METADATA_ADDED_REVIEWERS;
$metadata[$key] = $added_reviewers;
} else {
$user_tried_to_add = count($this->getAddedReviewers());
if ($user_tried_to_add == 0) {
throw new DifferentialActionHasNoEffectException(
"You can not add reviewers, because you did not specify any ".
"reviewers.");
} else if ($user_tried_to_add == 1) {
throw new DifferentialActionHasNoEffectException(
"You can not add that reviewer, because they are already an ".
"author or reviewer.");
} else {
throw new DifferentialActionHasNoEffectException(
"You can not add those reviewers, because they are all already ".
"authors or reviewers.");
}
}
break;
case DifferentialAction::ACTION_ADDCCS:
$added_ccs = $this->getAddedCCs();
$user_tried_to_add = count($added_ccs);
$added_ccs = $this->filterAddedCCs($added_ccs);
if ($added_ccs) {
foreach ($added_ccs as $cc) {
DifferentialRevisionEditor::addCC(
$revision,
$cc,
$this->actorPHID);
}
$key = DifferentialComment::METADATA_ADDED_CCS;
$metadata[$key] = $added_ccs;
} else {
if ($user_tried_to_add == 0) {
throw new DifferentialActionHasNoEffectException(
"You can not add CCs, because you did not specify any ".
"CCs.");
} else if ($user_tried_to_add == 1) {
throw new DifferentialActionHasNoEffectException(
"You can not add that CC, because they are already an ".
"author, reviewer or CC.");
} else {
throw new DifferentialActionHasNoEffectException(
"You can not add those CCs, because they are all already ".
"authors, reviewers or CCs.");
}
}
break;
case DifferentialAction::ACTION_CLAIM:
if ($actor_is_author) {
throw new Exception("You can not commandeer your own revision.");
}
switch ($revision_status) {
- case ArcanistDifferentialRevisionStatus::COMMITTED:
+ case ArcanistDifferentialRevisionStatus::CLOSED:
throw new DifferentialActionHasNoEffectException(
"You can not commandeer this revision because it has ".
- "already been committed.");
+ "already been closed.");
break;
}
$this->setAddedReviewers(array($revision->getAuthorPHID()));
// NOTE: Set the new author PHID before calling addReviewers(), since it
// doesn't permit the author to become a reviewer.
$revision->setAuthorPHID($actor_phid);
$added_reviewers = $this->addReviewers();
if ($added_reviewers) {
$key = DifferentialComment::METADATA_ADDED_REVIEWERS;
$metadata[$key] = $added_reviewers;
}
break;
default:
throw new Exception('Unsupported action.');
}
// Update information about reviewer in charge.
if ($action == DifferentialAction::ACTION_ACCEPT ||
$action == DifferentialAction::ACTION_REJECT) {
$revision->setLastReviewerPHID($actor_phid);
}
// Always save the revision (even if we didn't actually change any of its
// properties) so that it jumps to the top of the revision list when sorted
// by "updated". Notably, this allows "ping" comments to push it to the
// top of the action list.
$revision->save();
if ($action != DifferentialAction::ACTION_RESIGN) {
DifferentialRevisionEditor::addCC(
$revision,
$this->actorPHID,
$this->actorPHID);
}
$comment = id(new DifferentialComment())
->setAuthorPHID($this->actorPHID)
->setRevisionID($revision->getID())
->setAction($action)
->setContent((string)$this->message)
->setMetadata($metadata);
if ($this->contentSource) {
$comment->setContentSource($this->contentSource);
}
$comment->save();
$changesets = array();
if ($inline_comments) {
$load_ids = mpull($inline_comments, 'getChangesetID');
if ($load_ids) {
$load_ids = array_unique($load_ids);
$changesets = id(new DifferentialChangeset())->loadAllWhere(
'id in (%Ld)',
$load_ids);
}
foreach ($inline_comments as $inline) {
$inline->setCommentID($comment->getID());
$inline->save();
}
}
// Find any "@mentions" in the comment blocks.
$content_blocks = array($comment->getContent());
foreach ($inline_comments as $inline) {
$content_blocks[] = $inline->getContent();
}
$mention_ccs = PhabricatorMarkupEngine::extractPHIDsFromMentions(
$content_blocks);
if ($mention_ccs) {
$mention_ccs = $this->filterAddedCCs($mention_ccs);
if ($mention_ccs) {
$metadata = $comment->getMetadata();
$metacc = idx(
$metadata,
DifferentialComment::METADATA_ADDED_CCS,
array());
foreach ($mention_ccs as $cc_phid) {
DifferentialRevisionEditor::addCC(
$revision,
$cc_phid,
$this->actorPHID);
$metacc[] = $cc_phid;
}
$metadata[DifferentialComment::METADATA_ADDED_CCS] = $metacc;
$comment->setMetadata($metadata);
$comment->save();
}
}
$phids = array($this->actorPHID);
$handles = id(new PhabricatorObjectHandleData($phids))
->loadHandles();
$actor_handle = $handles[$this->actorPHID];
$xherald_header = HeraldTranscript::loadXHeraldRulesHeader(
$revision->getPHID());
id(new DifferentialCommentMail(
$revision,
$actor_handle,
$comment,
$changesets,
$inline_comments))
->setToPHIDs(
array_merge(
$revision->getReviewers(),
array($revision->getAuthorPHID())))
->setCCPHIDs($revision->getCCPHIDs())
->setChangedByCommit($this->getChangedByCommit())
->setXHeraldRulesHeader($xherald_header)
->setParentMessageID($this->parentMessageID)
->send();
$event_data = array(
'revision_id' => $revision->getID(),
'revision_phid' => $revision->getPHID(),
'revision_name' => $revision->getTitle(),
'revision_author_phid' => $revision->getAuthorPHID(),
'action' => $comment->getAction(),
'feedback_content' => $comment->getContent(),
'actor_phid' => $this->actorPHID,
);
id(new PhabricatorTimelineEvent('difx', $event_data))
->recordEvent();
// TODO: Move to a daemon?
id(new PhabricatorFeedStoryPublisher())
->setStoryType(PhabricatorFeedStoryTypeConstants::STORY_DIFFERENTIAL)
->setStoryData($event_data)
->setStoryTime(time())
->setStoryAuthorPHID($this->actorPHID)
->setRelatedPHIDs(
array(
$revision->getPHID(),
$this->actorPHID,
$revision->getAuthorPHID(),
))
->publish();
// TODO: Move to a daemon?
PhabricatorSearchDifferentialIndexer::indexRevision($revision);
return $comment;
}
private function filterAddedCCs(array $ccs) {
$revision = $this->revision;
$current_ccs = $revision->getCCPHIDs();
$current_ccs = array_fill_keys($current_ccs, true);
$reviewer_phids = $revision->getReviewers();
$reviewer_phids = array_fill_keys($reviewer_phids, true);
foreach ($ccs as $key => $cc) {
if (isset($current_ccs[$cc])) {
unset($ccs[$key]);
}
if (isset($reviewer_phids[$cc])) {
unset($ccs[$key]);
}
if ($cc == $revision->getAuthorPHID()) {
unset($ccs[$key]);
}
}
return $ccs;
}
private function addReviewers() {
$revision = $this->revision;
$added_reviewers = $this->getAddedReviewers();
$reviewer_phids = $revision->getReviewers();
foreach ($added_reviewers as $k => $user_phid) {
if ($user_phid == $revision->getAuthorPHID()) {
unset($added_reviewers[$k]);
}
if (!empty($reviewer_phids[$user_phid])) {
unset($added_reviewers[$k]);
}
}
$added_reviewers = array_unique($added_reviewers);
if ($added_reviewers) {
DifferentialRevisionEditor::alterReviewers(
$revision,
$reviewer_phids,
$rem = array(),
$added_reviewers,
$this->actorPHID);
}
return $added_reviewers;
}
}
diff --git a/src/applications/differential/editor/revision/DifferentialRevisionEditor.php b/src/applications/differential/editor/revision/DifferentialRevisionEditor.php
index e31ad1f260..491854d850 100644
--- a/src/applications/differential/editor/revision/DifferentialRevisionEditor.php
+++ b/src/applications/differential/editor/revision/DifferentialRevisionEditor.php
@@ -1,963 +1,963 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Handle major edit operations to DifferentialRevision -- adding and removing
* reviewers, diffs, and CCs. Unlike simple edits, these changes trigger
* complicated email workflows.
*/
final class DifferentialRevisionEditor {
protected $revision;
protected $actorPHID;
protected $cc = null;
protected $reviewers = null;
protected $diff;
protected $comments;
protected $silentUpdate;
private $auxiliaryFields = array();
private $contentSource;
public function __construct(DifferentialRevision $revision, $actor_phid) {
$this->revision = $revision;
$this->actorPHID = $actor_phid;
}
public static function newRevisionFromConduitWithDiff(
array $fields,
DifferentialDiff $diff,
$user_phid) {
$revision = new DifferentialRevision();
$revision->setPHID($revision->generatePHID());
$revision->setAuthorPHID($user_phid);
$revision->setStatus(ArcanistDifferentialRevisionStatus::NEEDS_REVIEW);
$editor = new DifferentialRevisionEditor($revision, $user_phid);
$editor->copyFieldsFromConduit($fields);
$editor->addDiff($diff, null);
$editor->save();
return $revision;
}
public function copyFieldsFromConduit(array $fields) {
$revision = $this->revision;
$revision->loadRelationships();
$aux_fields = DifferentialFieldSelector::newSelector()
->getFieldSpecifications();
$user = id(new PhabricatorUser())->loadOneWhere(
'phid = %s',
$this->actorPHID);
foreach ($aux_fields as $key => $aux_field) {
$aux_field->setRevision($revision);
$aux_field->setUser($user);
if (!$aux_field->shouldAppearOnCommitMessage()) {
unset($aux_fields[$key]);
}
}
$aux_fields = mpull($aux_fields, null, 'getCommitMessageKey');
foreach ($fields as $field => $value) {
if (empty($aux_fields[$field])) {
throw new Exception(
"Parsed commit message contains unrecognized field '{$field}'.");
}
$aux_fields[$field]->setValueFromParsedCommitMessage($value);
}
foreach ($aux_fields as $aux_field) {
$aux_field->validateField();
}
$aux_fields = array_values($aux_fields);
$this->setAuxiliaryFields($aux_fields);
}
public function setAuxiliaryFields(array $auxiliary_fields) {
assert_instances_of($auxiliary_fields, 'DifferentialFieldSpecification');
$this->auxiliaryFields = $auxiliary_fields;
return $this;
}
public function getRevision() {
return $this->revision;
}
public function setReviewers(array $reviewers) {
$this->reviewers = $reviewers;
return $this;
}
public function setCCPHIDs(array $cc) {
$this->cc = $cc;
return $this;
}
public function setContentSource(PhabricatorContentSource $content_source) {
$this->contentSource = $content_source;
return $this;
}
public function addDiff(DifferentialDiff $diff, $comments) {
if ($diff->getRevisionID() &&
$diff->getRevisionID() != $this->getRevision()->getID()) {
$diff_id = (int)$diff->getID();
$targ_id = (int)$this->getRevision()->getID();
$real_id = (int)$diff->getRevisionID();
throw new Exception(
"Can not attach diff #{$diff_id} to Revision D{$targ_id}, it is ".
"already attached to D{$real_id}.");
}
$this->diff = $diff;
$this->comments = $comments;
return $this;
}
protected function getDiff() {
return $this->diff;
}
protected function getComments() {
return $this->comments;
}
protected function getActorPHID() {
return $this->actorPHID;
}
public function isNewRevision() {
return !$this->getRevision()->getID();
}
/**
* A silent update does not trigger Herald rules or send emails. This is used
* for auto-amends at commit time.
*/
public function setSilentUpdate($silent) {
$this->silentUpdate = $silent;
return $this;
}
public function save() {
$revision = $this->getRevision();
$is_new = $this->isNewRevision();
if ($is_new) {
$this->initializeNewRevision($revision);
}
$revision->loadRelationships();
$this->willWriteRevision();
if ($this->reviewers === null) {
$this->reviewers = $revision->getReviewers();
}
if ($this->cc === null) {
$this->cc = $revision->getCCPHIDs();
}
$diff = $this->getDiff();
if ($diff) {
$revision->setLineCount($diff->getLineCount());
}
// Save the revision, to generate its ID and PHID if it is new. We need
// the ID/PHID in order to record them in Herald transcripts, but don't
// want to hold a transaction open while running Herald because it is
// potentially somewhat slow. The downside is that we may end up with a
// saved revision/diff pair without appropriate CCs. We could be better
// about this -- for example:
//
// - Herald can't affect reviewers, so we could compute them before
// opening the transaction and then save them in the transaction.
// - Herald doesn't *really* need PHIDs to compute its effects, we could
// run it before saving these objects and then hand over the PHIDs later.
//
// But this should address the problem of orphaned revisions, which is
// currently the only problem we experience in practice.
$revision->openTransaction();
if ($diff) {
$revision->setBranchName($diff->getBranch());
$revision->setArcanistProjectPHID($diff->getArcanistProjectPHID());
}
$revision->save();
if ($diff) {
$diff->setRevisionID($revision->getID());
$diff->save();
}
$revision->saveTransaction();
// We're going to build up three dictionaries: $add, $rem, and $stable. The
// $add dictionary has added reviewers/CCs. The $rem dictionary has
// reviewers/CCs who have been removed, and the $stable array is
// reviewers/CCs who haven't changed. We're going to send new reviewers/CCs
// a different ("welcome") email than we send stable reviewers/CCs.
$old = array(
'rev' => array_fill_keys($revision->getReviewers(), true),
'ccs' => array_fill_keys($revision->getCCPHIDs(), true),
);
$xscript_header = null;
$xscript_uri = null;
$new = array(
'rev' => array_fill_keys($this->reviewers, true),
'ccs' => array_fill_keys($this->cc, true),
);
$rem_ccs = array();
$xscript_phid = null;
if ($diff) {
$adapter = new HeraldDifferentialRevisionAdapter(
$revision,
$diff);
$adapter->setExplicitCCs($new['ccs']);
$adapter->setExplicitReviewers($new['rev']);
$adapter->setForbiddenCCs($revision->getUnsubscribedPHIDs());
$xscript = HeraldEngine::loadAndApplyRules($adapter);
$xscript_uri = PhabricatorEnv::getProductionURI(
'/herald/transcript/'.$xscript->getID().'/');
$xscript_phid = $xscript->getPHID();
$xscript_header = $xscript->getXHeraldRulesHeader();
$xscript_header = HeraldTranscript::saveXHeraldRulesHeader(
$revision->getPHID(),
$xscript_header);
$sub = array(
'rev' => array(),
'ccs' => $adapter->getCCsAddedByHerald(),
);
$rem_ccs = $adapter->getCCsRemovedByHerald();
} else {
$sub = array(
'rev' => array(),
'ccs' => array(),
);
}
// Remove any CCs which are prevented by Herald rules.
$sub['ccs'] = array_diff_key($sub['ccs'], $rem_ccs);
$new['ccs'] = array_diff_key($new['ccs'], $rem_ccs);
$add = array();
$rem = array();
$stable = array();
foreach (array('rev', 'ccs') as $key) {
$add[$key] = array();
if ($new[$key] !== null) {
$add[$key] += array_diff_key($new[$key], $old[$key]);
}
$add[$key] += array_diff_key($sub[$key], $old[$key]);
$combined = $sub[$key];
if ($new[$key] !== null) {
$combined += $new[$key];
}
$rem[$key] = array_diff_key($old[$key], $combined);
$stable[$key] = array_diff_key($old[$key], $add[$key] + $rem[$key]);
}
self::alterReviewers(
$revision,
$this->reviewers,
array_keys($rem['rev']),
array_keys($add['rev']),
$this->actorPHID);
// We want to attribute new CCs to a "reasonPHID", representing the reason
// they were added. This is either a user (if some user explicitly CCs
// them, or uses "Add CCs...") or a Herald transcript PHID, indicating that
// they were added by a Herald rule.
if ($add['ccs'] || $rem['ccs']) {
$reasons = array();
foreach ($add['ccs'] as $phid => $ignored) {
if (empty($new['ccs'][$phid])) {
$reasons[$phid] = $xscript_phid;
} else {
$reasons[$phid] = $this->actorPHID;
}
}
foreach ($rem['ccs'] as $phid => $ignored) {
if (empty($new['ccs'][$phid])) {
$reasons[$phid] = $this->actorPHID;
} else {
$reasons[$phid] = $xscript_phid;
}
}
} else {
$reasons = $this->actorPHID;
}
self::alterCCs(
$revision,
$this->cc,
array_keys($rem['ccs']),
array_keys($add['ccs']),
$reasons);
$this->updateAuxiliaryFields();
// Add the author and users included from Herald rules to the relevant set
// of users so they get a copy of the email.
if (!$this->silentUpdate) {
if ($is_new) {
$add['rev'][$this->getActorPHID()] = true;
if ($diff) {
$add['rev'] += $adapter->getEmailPHIDsAddedByHerald();
}
} else {
$stable['rev'][$this->getActorPHID()] = true;
if ($diff) {
$stable['rev'] += $adapter->getEmailPHIDsAddedByHerald();
}
}
}
$mail = array();
$phids = array($this->getActorPHID());
$handles = id(new PhabricatorObjectHandleData($phids))
->loadHandles();
$actor_handle = $handles[$this->getActorPHID()];
$changesets = null;
$comment = null;
if ($diff) {
$changesets = $diff->loadChangesets();
// TODO: This should probably be in DifferentialFeedbackEditor?
if (!$is_new) {
$comment = $this->createComment();
}
if ($comment) {
$mail[] = id(new DifferentialNewDiffMail(
$revision,
$actor_handle,
$changesets))
->setIsFirstMailAboutRevision($is_new)
->setIsFirstMailToRecipients($is_new)
->setComments($this->getComments())
->setToPHIDs(array_keys($stable['rev']))
->setCCPHIDs(array_keys($stable['ccs']));
}
// Save the changes we made above.
$diff->setDescription(preg_replace('/\n.*/s', '', $this->getComments()));
$diff->save();
$this->updateAffectedPathTable($revision, $diff, $changesets);
$this->updateRevisionHashTable($revision, $diff);
- // An updated diff should require review, as long as it's not committed
+ // An updated diff should require review, as long as it's not closed
// or accepted. The "accepted" status is "sticky" to encourage courtesy
// re-diffs after someone accepts with minor changes/suggestions.
$status = $revision->getStatus();
- if ($status != ArcanistDifferentialRevisionStatus::COMMITTED &&
+ if ($status != ArcanistDifferentialRevisionStatus::CLOSED &&
$status != ArcanistDifferentialRevisionStatus::ACCEPTED) {
$revision->setStatus(ArcanistDifferentialRevisionStatus::NEEDS_REVIEW);
}
} else {
$diff = $revision->loadActiveDiff();
if ($diff) {
$changesets = $diff->loadChangesets();
} else {
$changesets = array();
}
}
$revision->save();
$this->didWriteRevision();
$event_data = array(
'revision_id' => $revision->getID(),
'revision_phid' => $revision->getPHID(),
'revision_name' => $revision->getTitle(),
'revision_author_phid' => $revision->getAuthorPHID(),
'action' => $is_new
? DifferentialAction::ACTION_CREATE
: DifferentialAction::ACTION_UPDATE,
'feedback_content' => $is_new
? phutil_utf8_shorten($revision->getSummary(), 140)
: $this->getComments(),
'actor_phid' => $revision->getAuthorPHID(),
);
id(new PhabricatorTimelineEvent('difx', $event_data))
->recordEvent();
id(new PhabricatorFeedStoryPublisher())
->setStoryType(PhabricatorFeedStoryTypeConstants::STORY_DIFFERENTIAL)
->setStoryData($event_data)
->setStoryTime(time())
->setStoryAuthorPHID($revision->getAuthorPHID())
->setRelatedPHIDs(
array(
$revision->getPHID(),
$revision->getAuthorPHID(),
))
->publish();
// TODO: Move this into a worker task thing.
PhabricatorSearchDifferentialIndexer::indexRevision($revision);
if ($this->silentUpdate) {
return;
}
$revision->loadRelationships();
if ($add['rev']) {
$message = id(new DifferentialNewDiffMail(
$revision,
$actor_handle,
$changesets))
->setIsFirstMailAboutRevision($is_new)
->setIsFirstMailToRecipients(true)
->setToPHIDs(array_keys($add['rev']));
if ($is_new) {
// The first time we send an email about a revision, put the CCs in
// the "CC:" field of the same "Review Requested" email that reviewers
// get, so you don't get two initial emails if you're on a list that
// is CC'd.
$message->setCCPHIDs(array_keys($add['ccs']));
}
$mail[] = $message;
}
// If we added CCs, we want to send them an email, but only if they were not
// already a reviewer and were not added as one (in these cases, they got
// a "NewDiff" mail, either in the past or just a moment ago). You can still
// get two emails, but only if a revision is updated and you are added as a
// reviewer at the same time a list you are on is added as a CC, which is
// rare and reasonable.
$implied_ccs = self::getImpliedCCs($revision);
$implied_ccs = array_fill_keys($implied_ccs, true);
$add['ccs'] = array_diff_key($add['ccs'], $implied_ccs);
if (!$is_new && $add['ccs']) {
$mail[] = id(new DifferentialCCWelcomeMail(
$revision,
$actor_handle,
$changesets))
->setIsFirstMailToRecipients(true)
->setToPHIDs(array_keys($add['ccs']));
}
foreach ($mail as $message) {
$message->setHeraldTranscriptURI($xscript_uri);
$message->setXHeraldRulesHeader($xscript_header);
$message->send();
}
}
public static function addCCAndUpdateRevision(
$revision,
$phid,
$reason) {
self::addCC($revision, $phid, $reason);
$unsubscribed = $revision->getUnsubscribed();
if (isset($unsubscribed[$phid])) {
unset($unsubscribed[$phid]);
$revision->setUnsubscribed($unsubscribed);
$revision->save();
}
}
public static function removeCCAndUpdateRevision(
$revision,
$phid,
$reason) {
self::removeCC($revision, $phid, $reason);
$unsubscribed = $revision->getUnsubscribed();
if (empty($unsubscribed[$phid])) {
$unsubscribed[$phid] = true;
$revision->setUnsubscribed($unsubscribed);
$revision->save();
}
}
public static function addCC(
DifferentialRevision $revision,
$phid,
$reason) {
return self::alterCCs(
$revision,
$revision->getCCPHIDs(),
$rem = array(),
$add = array($phid),
$reason);
}
public static function removeCC(
DifferentialRevision $revision,
$phid,
$reason) {
return self::alterCCs(
$revision,
$revision->getCCPHIDs(),
$rem = array($phid),
$add = array(),
$reason);
}
protected static function alterCCs(
DifferentialRevision $revision,
array $stable_phids,
array $rem_phids,
array $add_phids,
$reason_phid) {
$dont_add = self::getImpliedCCs($revision);
$add_phids = array_diff($add_phids, $dont_add);
return self::alterRelationships(
$revision,
$stable_phids,
$rem_phids,
$add_phids,
$reason_phid,
DifferentialRevision::RELATION_SUBSCRIBED);
}
private static function getImpliedCCs(DifferentialRevision $revision) {
return array_merge(
$revision->getReviewers(),
array($revision->getAuthorPHID()));
}
public static function alterReviewers(
DifferentialRevision $revision,
array $stable_phids,
array $rem_phids,
array $add_phids,
$reason_phid) {
return self::alterRelationships(
$revision,
$stable_phids,
$rem_phids,
$add_phids,
$reason_phid,
DifferentialRevision::RELATION_REVIEWER);
}
private static function alterRelationships(
DifferentialRevision $revision,
array $stable_phids,
array $rem_phids,
array $add_phids,
$reason_phid,
$relation_type) {
$rem_map = array_fill_keys($rem_phids, true);
$add_map = array_fill_keys($add_phids, true);
$seq_map = array_values($stable_phids);
$seq_map = array_flip($seq_map);
foreach ($rem_map as $phid => $ignored) {
if (!isset($seq_map[$phid])) {
$seq_map[$phid] = count($seq_map);
}
}
foreach ($add_map as $phid => $ignored) {
if (!isset($seq_map[$phid])) {
$seq_map[$phid] = count($seq_map);
}
}
$raw = $revision->getRawRelations($relation_type);
$raw = ipull($raw, null, 'objectPHID');
$sequence = count($seq_map);
foreach ($raw as $phid => $ignored) {
if (isset($seq_map[$phid])) {
$raw[$phid]['sequence'] = $seq_map[$phid];
} else {
$raw[$phid]['sequence'] = $sequence++;
}
}
$raw = isort($raw, 'sequence');
foreach ($raw as $phid => $ignored) {
if (isset($rem_map[$phid])) {
unset($raw[$phid]);
}
}
foreach ($add_phids as $add) {
$reason = is_array($reason_phid)
? idx($reason_phid, $add)
: $reason_phid;
$raw[$add] = array(
'objectPHID' => $add,
'sequence' => idx($seq_map, $add, $sequence++),
'reasonPHID' => $reason,
);
}
$conn_w = $revision->establishConnection('w');
$sql = array();
foreach ($raw as $relation) {
$sql[] = qsprintf(
$conn_w,
'(%d, %s, %s, %d, %s)',
$revision->getID(),
$relation_type,
$relation['objectPHID'],
$relation['sequence'],
$relation['reasonPHID']);
}
$conn_w->openTransaction();
queryfx(
$conn_w,
'DELETE FROM %T WHERE revisionID = %d AND relation = %s',
DifferentialRevision::RELATIONSHIP_TABLE,
$revision->getID(),
$relation_type);
if ($sql) {
queryfx(
$conn_w,
'INSERT INTO %T
(revisionID, relation, objectPHID, sequence, reasonPHID)
VALUES %Q',
DifferentialRevision::RELATIONSHIP_TABLE,
implode(', ', $sql));
}
$conn_w->saveTransaction();
$revision->loadRelationships();
}
private function createComment() {
$revision_id = $this->revision->getID();
$comment = id(new DifferentialComment())
->setAuthorPHID($this->getActorPHID())
->setRevisionID($revision_id)
->setContent($this->getComments())
->setAction(DifferentialAction::ACTION_UPDATE)
->setMetadata(
array(
DifferentialComment::METADATA_DIFF_ID => $this->getDiff()->getID(),
));
if ($this->contentSource) {
$comment->setContentSource($this->contentSource);
}
$comment->save();
return $comment;
}
private function updateAuxiliaryFields() {
$aux_map = array();
foreach ($this->auxiliaryFields as $aux_field) {
$key = $aux_field->getStorageKey();
if ($key !== null) {
$val = $aux_field->getValueForStorage();
$aux_map[$key] = $val;
}
}
if (!$aux_map) {
return;
}
$revision = $this->revision;
$fields = id(new DifferentialAuxiliaryField())->loadAllWhere(
'revisionPHID = %s AND name IN (%Ls)',
$revision->getPHID(),
array_keys($aux_map));
$fields = mpull($fields, null, 'getName');
foreach ($aux_map as $key => $val) {
$obj = idx($fields, $key);
if (!strlen($val)) {
// If the new value is empty, just delete the old row if one exists and
// don't add a new row if it doesn't.
if ($obj) {
$obj->delete();
}
} else {
if (!$obj) {
$obj = new DifferentialAuxiliaryField();
$obj->setRevisionPHID($revision->getPHID());
$obj->setName($key);
}
if ($obj->getValue() !== $val) {
$obj->setValue($val);
$obj->save();
}
}
}
}
private function willWriteRevision() {
foreach ($this->auxiliaryFields as $aux_field) {
$aux_field->willWriteRevision($this);
}
}
private function didWriteRevision() {
foreach ($this->auxiliaryFields as $aux_field) {
$aux_field->didWriteRevision($this);
}
}
/**
* Update the table which links Differential revisions to paths they affect,
* so Diffusion can efficiently find pending revisions for a given file.
*/
private function updateAffectedPathTable(
DifferentialRevision $revision,
DifferentialDiff $diff,
array $changesets) {
assert_instances_of($changesets, 'DifferentialChangeset');
$project = $diff->loadArcanistProject();
if (!$project) {
// Probably an old revision from before projects.
return;
}
$repository = $project->loadRepository();
if (!$repository) {
// Probably no project <-> repository link, or the repository where the
// project lives is untracked.
return;
}
$path_prefix = null;
$local_root = $diff->getSourceControlPath();
if ($local_root) {
// We're in a working copy which supports subdirectory checkouts (e.g.,
// SVN) so we need to figure out what prefix we should add to each path
// (e.g., trunk/projects/example/) to get the absolute path from the
// root of the repository. DVCS systems like Git and Mercurial are not
// affected.
// Normalize both paths and check if the repository root is a prefix of
// the local root. If so, throw it away. Note that this correctly handles
// the case where the remote path is "/".
$local_root = id(new PhutilURI($local_root))->getPath();
$local_root = rtrim($local_root, '/');
$repo_root = id(new PhutilURI($repository->getRemoteURI()))->getPath();
$repo_root = rtrim($repo_root, '/');
if (!strncmp($repo_root, $local_root, strlen($repo_root))) {
$path_prefix = substr($local_root, strlen($repo_root));
}
}
$paths = array();
foreach ($changesets as $changeset) {
$paths[] = $path_prefix.'/'.$changeset->getFilename();
}
// Mark this as also touching all parent paths, so you can see all pending
// changes to any file within a directory.
$all_paths = array();
foreach ($paths as $local) {
foreach (DiffusionPathIDQuery::expandPathToRoot($local) as $path) {
$all_paths[$path] = true;
}
}
$all_paths = array_keys($all_paths);
$path_map = id(new DiffusionPathIDQuery($all_paths))->loadPathIDs();
$table = new DifferentialAffectedPath();
$conn_w = $table->establishConnection('w');
$sql = array();
foreach ($all_paths as $path) {
$path_id = idx($path_map, $path);
if (!$path_id) {
// Don't bother creating these, it probably means we're either adding
// a file (in which case having this row is irrelevant since Diffusion
// won't be querying for it) or something is misconfigured (in which
// case we'd just be writing garbage).
continue;
}
$sql[] = qsprintf(
$conn_w,
'(%d, %d, %d, %d)',
$repository->getID(),
$path_id,
time(),
$revision->getID());
}
queryfx(
$conn_w,
'DELETE FROM %T WHERE revisionID = %d',
$table->getTableName(),
$revision->getID());
foreach (array_chunk($sql, 256) as $chunk) {
queryfx(
$conn_w,
'INSERT INTO %T (repositoryID, pathID, epoch, revisionID) VALUES %Q',
$table->getTableName(),
implode(', ', $chunk));
}
}
/**
* Update the table connecting revisions to DVCS local hashes, so we can
* identify revisions by commit/tree hashes.
*/
private function updateRevisionHashTable(
DifferentialRevision $revision,
DifferentialDiff $diff) {
$vcs = $diff->getSourceControlSystem();
if ($vcs == DifferentialRevisionControlSystem::SVN) {
// Subversion has no local commit or tree hash information, so we don't
// have to do anything.
return;
}
$property = id(new DifferentialDiffProperty())->loadOneWhere(
'diffID = %d AND name = %s',
$diff->getID(),
'local:commits');
if (!$property) {
return;
}
$hashes = array();
$data = $property->getData();
switch ($vcs) {
case DifferentialRevisionControlSystem::GIT:
foreach ($data as $commit) {
$hashes[] = array(
ArcanistDifferentialRevisionHash::HASH_GIT_COMMIT,
$commit['commit'],
);
$hashes[] = array(
ArcanistDifferentialRevisionHash::HASH_GIT_TREE,
$commit['tree'],
);
}
break;
case DifferentialRevisionControlSystem::MERCURIAL:
foreach ($data as $commit) {
$hashes[] = array(
ArcanistDifferentialRevisionHash::HASH_MERCURIAL_COMMIT,
$commit['rev'],
);
}
break;
}
$conn_w = $revision->establishConnection('w');
$sql = array();
foreach ($hashes as $info) {
list($type, $hash) = $info;
$sql[] = qsprintf(
$conn_w,
'(%d, %s, %s)',
$revision->getID(),
$type,
$hash);
}
queryfx(
$conn_w,
'DELETE FROM %T WHERE revisionID = %d',
ArcanistDifferentialRevisionHash::TABLE_NAME,
$revision->getID());
if ($sql) {
queryfx(
$conn_w,
'INSERT INTO %T (revisionID, type, hash) VALUES %Q',
ArcanistDifferentialRevisionHash::TABLE_NAME,
implode(', ', $sql));
}
}
private function initializeNewRevision(DifferentialRevision $revision) {
// These fields aren't nullable; set them to sensible defaults if they
// haven't been configured. We're just doing this so we can generate an
// ID for the revision if we don't have one already.
$revision->setLineCount(0);
if ($revision->getStatus() === null) {
$revision->setStatus(ArcanistDifferentialRevisionStatus::NEEDS_REVIEW);
}
if ($revision->getTitle() === null) {
$revision->setTitle('Untitled Revision');
}
if ($revision->getAuthorPHID() === null) {
$revision->setAuthorPHID($this->getActorPHID());
}
if ($revision->getSummary() === null) {
$revision->setSummary('');
}
if ($revision->getTestPlan() === null) {
$revision->setTestPlan('');
}
}
}
diff --git a/src/applications/differential/mail/comment/DifferentialCommentMail.php b/src/applications/differential/mail/comment/DifferentialCommentMail.php
index fb78849b5e..0164c06d56 100644
--- a/src/applications/differential/mail/comment/DifferentialCommentMail.php
+++ b/src/applications/differential/mail/comment/DifferentialCommentMail.php
@@ -1,204 +1,204 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class DifferentialCommentMail extends DifferentialMail {
protected $changedByCommit;
public function setChangedByCommit($changed_by_commit) {
$this->changedByCommit = $changed_by_commit;
return $this;
}
public function getChangedByCommit() {
return $this->changedByCommit;
}
public function __construct(
DifferentialRevision $revision,
PhabricatorObjectHandle $actor,
DifferentialComment $comment,
array $changesets,
array $inline_comments) {
assert_instances_of($changesets, 'DifferentialChangeset');
assert_instances_of($inline_comments, 'PhabricatorInlineCommentInterface');
$this->setRevision($revision);
$this->setActorHandle($actor);
$this->setComment($comment);
$this->setChangesets($changesets);
$this->setInlineComments($inline_comments);
}
protected function getMailTags() {
$comment = $this->getComment();
$action = $comment->getAction();
$tags = array();
switch ($action) {
case DifferentialAction::ACTION_ADDCCS:
$tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_CC;
break;
- case DifferentialAction::ACTION_COMMIT:
- $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_COMMITTED;
+ case DifferentialAction::ACTION_CLOSE:
+ $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_CLOSED;
break;
}
if (strlen(trim($comment->getContent()))) {
switch ($action) {
- case DifferentialAction::ACTION_COMMIT:
+ case DifferentialAction::ACTION_CLOSE:
// Commit comments are auto-generated and not especially interesting,
// so don't tag them as having a comment.
break;
default:
$tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_COMMENT;
break;
}
}
return $tags;
}
protected function renderVarySubject() {
$verb = ucwords($this->getVerb());
return "[{$verb}] ".$this->renderSubject();
}
protected function getVerb() {
$comment = $this->getComment();
$action = $comment->getAction();
$verb = DifferentialAction::getActionPastTenseVerb($action);
return $verb;
}
protected function renderBody() {
$comment = $this->getComment();
$actor = $this->getActorName();
$name = $this->getRevision()->getTitle();
$verb = $this->getVerb();
$body = array();
$body[] = "{$actor} has {$verb} the revision \"{$name}\".";
// If the commented added reviewers or CCs, list them explicitly.
$meta = $comment->getMetadata();
$m_reviewers = idx(
$meta,
DifferentialComment::METADATA_ADDED_REVIEWERS,
array());
$m_cc = idx(
$meta,
DifferentialComment::METADATA_ADDED_CCS,
array());
$load = array_merge($m_reviewers, $m_cc);
if ($load) {
$handles = id(new PhabricatorObjectHandleData($load))->loadHandles();
if ($m_reviewers) {
$body[] = 'Added Reviewers: '.$this->renderHandleList(
$handles,
$m_reviewers);
}
if ($m_cc) {
$body[] = 'Added CCs: '.$this->renderHandleList(
$handles,
$m_cc);
}
}
$body[] = null;
$content = $comment->getContent();
if (strlen($content)) {
$body[] = $this->formatText($content);
$body[] = null;
}
if ($this->getChangedByCommit()) {
$body[] = 'CHANGED PRIOR TO COMMIT';
$body[] = ' This revision was updated prior to commit.';
$body[] = null;
}
$inlines = $this->getInlineComments();
if ($inlines) {
$body[] = 'INLINE COMMENTS';
$changesets = $this->getChangesets();
foreach ($inlines as $inline) {
$changeset = $changesets[$inline->getChangesetID()];
if (!$changeset) {
throw new Exception('Changeset missing!');
}
$file = $changeset->getFilename();
$start = $inline->getLineNumber();
$len = $inline->getLineLength();
if ($len) {
$range = $start.'-'.($start + $len);
} else {
$range = $start;
}
$content = $inline->getContent();
$body[] = $this->formatText("{$file}:{$range} {$content}");
}
$body[] = null;
}
$body[] = $this->renderRevisionDetailLink();
$body[] = null;
$revision = $this->getRevision();
$status = $revision->getStatus();
if ($status == ArcanistDifferentialRevisionStatus::NEEDS_REVISION ||
$status == ArcanistDifferentialRevisionStatus::ACCEPTED) {
$diff = $revision->loadActiveDiff();
if ($diff) {
$branch = $diff->getBranch();
if ($branch) {
$body[] = "BRANCH\n $branch";
$body[] = null;
}
}
}
- if ($status == ArcanistDifferentialRevisionStatus::COMMITTED) {
+ if ($status == ArcanistDifferentialRevisionStatus::CLOSED) {
$phids = $revision->loadCommitPHIDs();
if ($phids) {
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
if (count($handles) == 1) {
$body[] = "COMMIT";
} else {
// This is unlikely to ever happen since we'll send this mail the
// first time we discover a commit, but it's not impossible if data
// was migrated, etc.
$body[] = "COMMITS";
}
foreach ($handles as $handle) {
$body[] = ' '.PhabricatorEnv::getProductionURI($handle->getURI());
}
$body[] = null;
}
}
return implode("\n", $body);
}
}
diff --git a/src/applications/differential/query/revision/DifferentialRevisionQuery.php b/src/applications/differential/query/revision/DifferentialRevisionQuery.php
index e14aa25470..12a8978f90 100644
--- a/src/applications/differential/query/revision/DifferentialRevisionQuery.php
+++ b/src/applications/differential/query/revision/DifferentialRevisionQuery.php
@@ -1,859 +1,866 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Flexible query API for Differential revisions. Example:
*
* // Load open revisions
* $revisions = id(new DifferentialRevisionQuery())
* ->withStatus(DifferentialRevisionQuery::STATUS_OPEN)
* ->execute();
*
* @task config Query Configuration
* @task exec Query Execution
* @task internal Internals
*/
final class DifferentialRevisionQuery {
// TODO: Replace DifferentialRevisionListData with this class.
private $pathIDs = array();
private $status = 'status-any';
const STATUS_ANY = 'status-any';
const STATUS_OPEN = 'status-open';
const STATUS_ACCEPTED = 'status-accepted';
- const STATUS_COMMITTED = 'status-committed';
+ const STATUS_CLOSED = 'status-closed'; // NOTE: Same as 'committed'.
+ const STATUS_COMMITTED = 'status-committed'; // TODO: Remove.
const STATUS_ABANDONED = 'status-abandoned';
private $authors = array();
private $draftAuthors = array();
private $ccs = array();
private $reviewers = array();
private $revIDs = array();
private $commitHashes = array();
private $phids = array();
private $subscribers = array();
private $responsibles = array();
private $branches = array();
private $arcanistProjectPHIDs = array();
private $order = 'order-modified';
const ORDER_MODIFIED = 'order-modified';
const ORDER_CREATED = 'order-created';
/**
* This is essentially a denormalized copy of the revision modified time that
* should perform better for path queries with a LIMIT. Critically, when you
* browse "/", every revision in that repository for all time will match so
* the query benefits from being able to stop before fully materializing the
* result set.
*/
const ORDER_PATH_MODIFIED = 'order-path-modified';
private $limit = 1000;
private $offset = 0;
private $needRelationships = false;
private $needActiveDiffs = false;
private $needDiffIDs = false;
private $needCommitPHIDs = false;
/* -( Query Configuration )------------------------------------------------ */
/**
* Filter results to revisions which affect a Diffusion path ID in a given
* repository. You can call this multiple times to select revisions for
* several paths.
*
* @param int Diffusion repository ID.
* @param int Diffusion path ID.
* @return this
* @task config
*/
public function withPath($repository_id, $path_id) {
$this->pathIDs[] = array(
'repositoryID' => $repository_id,
'pathID' => $path_id,
);
return $this;
}
/**
* Filter results to revisions authored by one of the given PHIDs. Calling
* this function will clear anything set by previous calls to
* @{method:withAuthors}.
*
* @param array List of PHIDs of authors
* @return this
* @task config
*/
public function withAuthors(array $author_phids) {
$this->authors = $author_phids;
return $this;
}
/**
* Filter results to revisions with comments authored bythe given PHIDs
*
* @param array List of PHIDs of authors
* @return this
* @task config
*/
public function withDraftRepliesByAuthors(array $author_phids) {
$this->draftAuthors = $author_phids;
return $this;
}
/**
* Filter results to revisions which CC one of the listed people. Calling this
* function will clear anything set by previous calls to @{method:withCCs}.
*
* @param array List of PHIDs of subscribers
* @return this
* @task config
*/
public function withCCs(array $cc_phids) {
$this->ccs = $cc_phids;
return $this;
}
/**
* Filter results to revisions that have one of the provided PHIDs as
* reviewers. Calling this function will clear anything set by previous calls
* to @{method:withReviewers}.
*
* @param array List of PHIDs of reviewers
* @return this
* @task config
*/
public function withReviewers(array $reviewer_phids) {
$this->reviewers = $reviewer_phids;
return $this;
}
/**
* Filter results to revisions that have one of the provided commit hashes.
* Calling this function will clear anything set by previous calls to
* @{method:withCommitHashes}.
*
* @param array List of pairs <Class
* ArcanistDifferentialRevisionHash::HASH_$type constant,
* hash>
* @return this
* @task config
*/
public function withCommitHashes(array $commit_hashes) {
$this->commitHashes = $commit_hashes;
return $this;
}
/**
* Filter results to revisions with a given status. Provide a class constant,
* such as ##DifferentialRevisionQuery::STATUS_OPEN##.
*
* @param const Class STATUS constant, like STATUS_OPEN.
* @return this
* @task config
*/
public function withStatus($status_constant) {
$this->status = $status_constant;
return $this;
}
/**
* Filter results to revisions on given branches.
*
* @param list List of branch names.
* @return this
* @task config
*/
public function withBranches(array $branches) {
$this->branches = $branches;
return $this;
}
/**
* Filter results to only return revisions whose ids are in the given set.
*
* @param array List of revision ids
* @return this
* @task config
*/
public function withIDs(array $ids) {
$this->revIDs = $ids;
return $this;
}
/**
* Filter results to only return revisions whose PHIDs are in the given set.
*
* @param array List of revision PHIDs
* @return this
* @task config
*/
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
/**
* Given a set of users, filter results to return only revisions they are
* responsible for (i.e., they are either authors or reviewers).
*
* @param array List of user PHIDs.
* @return this
* @task config
*/
public function withResponsibleUsers(array $responsible_phids) {
$this->responsibles = $responsible_phids;
return $this;
}
/**
* Filter results to only return revisions with a given set of subscribers
* (i.e., they are authors, reviewers or CC'd).
*
* @param array List of user PHIDs.
* @return this
* @task config
*/
public function withSubscribers(array $subscriber_phids) {
$this->subscribers = $subscriber_phids;
return $this;
}
/**
* Filter results to only return revisions with a given set of arcanist
* projects.
*
* @param array List of project PHIDs.
* @return this
* @task config
*/
public function withArcanistProjectPHIDs(array $arc_project_phids) {
$this->arcanistProjectPHIDs = $arc_project_phids;
return $this;
}
/**
* Set result ordering. Provide a class constant, such as
* ##DifferentialRevisionQuery::ORDER_CREATED##.
*
* @task config
*/
public function setOrder($order_constant) {
$this->order = $order_constant;
return $this;
}
/**
* Set result limit. If unspecified, defaults to 1000.
*
* @param int Result limit.
* @return this
* @task config
*/
public function setLimit($limit) {
$this->limit = $limit;
return $this;
}
/**
* Set result offset. If unspecified, defaults to 0.
*
* @param int Result offset.
* @return this
* @task config
*/
public function setOffset($offset) {
$this->offset = $offset;
return $this;
}
/**
* Set whether or not the query will load and attach relationships.
*
* @param bool True to load and attach relationships.
* @return this
* @task config
*/
public function needRelationships($need_relationships) {
$this->needRelationships = $need_relationships;
return $this;
}
/**
* Set whether or not the query should load the active diff for each
* revision.
*
* @param bool True to load and attach diffs.
* @return this
* @task config
*/
public function needActiveDiffs($need_active_diffs) {
$this->needActiveDiffs = $need_active_diffs;
return $this;
}
/**
* Set whether or not the query should load the associated commit PHIDs for
* each revision.
*
* @param bool True to load and attach diffs.
* @return this
* @task config
*/
public function needCommitPHIDs($need_commit_phids) {
$this->needCommitPHIDs = $need_commit_phids;
return $this;
}
/**
* Set whether or not the query should load associated diff IDs for each
* revision.
*
* @param bool True to load and attach diff IDs.
* @return this
* @task config
*/
public function needDiffIDs($need_diff_ids) {
$this->needDiffIDs = $need_diff_ids;
return $this;
}
/* -( Query Execution )---------------------------------------------------- */
/**
* Execute the query as configured, returning matching
* @{class:DifferentialRevision} objects.
*
* @return list List of matching DifferentialRevision objects.
* @task exec
*/
public function execute() {
$table = new DifferentialRevision();
$conn_r = $table->establishConnection('r');
if ($this->shouldUseResponsibleFastPath()) {
$data = $this->loadDataUsingResponsibleFastPath();
} else {
$data = $this->loadData();
}
$revisions = $table->loadAllFromArray($data);
if ($revisions) {
if ($this->needRelationships) {
$this->loadRelationships($conn_r, $revisions);
}
if ($this->needCommitPHIDs) {
$this->loadCommitPHIDs($conn_r, $revisions);
}
$need_active = $this->needActiveDiffs;
$need_ids = $need_active ||
$this->needDiffIDs;
if ($need_ids) {
$this->loadDiffIDs($conn_r, $revisions);
}
if ($need_active) {
$this->loadActiveDiffs($conn_r, $revisions);
}
}
return $revisions;
}
/**
* Determine if we should execute an optimized, fast-path query to fetch
* open revisions for one responsible user. This is used by the Differential
* dashboard and much faster when executed as a UNION ALL than with JOIN
* and WHERE, which is why we special case it.
*/
private function shouldUseResponsibleFastPath() {
if ((count($this->responsibles) == 1) &&
($this->status == self::STATUS_OPEN) &&
($this->order == self::ORDER_MODIFIED) &&
!$this->offset &&
!$this->limit &&
!$this->subscribers &&
!$this->reviewers &&
!$this->ccs &&
!$this->authors &&
!$this->revIDs &&
!$this->commitHashes &&
!$this->phids &&
!$this->branches &&
!$this->arcanistProjectPHIDs) {
return true;
}
return false;
}
private function loadDataUsingResponsibleFastPath() {
$table = new DifferentialRevision();
$conn_r = $table->establishConnection('r');
$responsible_phid = reset($this->responsibles);
$open_statuses = array(
ArcanistDifferentialRevisionStatus::NEEDS_REVIEW,
ArcanistDifferentialRevisionStatus::NEEDS_REVISION,
ArcanistDifferentialRevisionStatus::ACCEPTED,
);
return queryfx_all(
$conn_r,
'SELECT * FROM %T WHERE authorPHID = %s AND status IN (%Ld)
UNION ALL
SELECT r.* FROM %T r JOIN %T rel
ON rel.revisionID = r.id
AND rel.relation = %s
AND rel.objectPHID = %s
WHERE r.status IN (%Ld) ORDER BY dateModified DESC',
$table->getTableName(),
$responsible_phid,
$open_statuses,
$table->getTableName(),
DifferentialRevision::RELATIONSHIP_TABLE,
DifferentialRevision::RELATION_REVIEWER,
$responsible_phid,
$open_statuses);
}
private function loadData() {
$table = new DifferentialRevision();
$conn_r = $table->establishConnection('r');
$select = qsprintf(
$conn_r,
'SELECT r.* FROM %T r',
$table->getTableName());
$joins = $this->buildJoinsClause($conn_r);
$where = $this->buildWhereClause($conn_r);
$group_by = $this->buildGroupByClause($conn_r);
$order_by = $this->buildOrderByClause($conn_r);
$limit = '';
if ($this->offset || $this->limit) {
$limit = qsprintf(
$conn_r,
'LIMIT %d, %d',
(int)$this->offset,
$this->limit);
}
return queryfx_all(
$conn_r,
'%Q %Q %Q %Q %Q %Q',
$select,
$joins,
$where,
$group_by,
$order_by,
$limit);
}
/* -( Internals )---------------------------------------------------------- */
/**
* @task internal
*/
private function buildJoinsClause($conn_r) {
$joins = array();
if ($this->pathIDs) {
$path_table = new DifferentialAffectedPath();
$joins[] = qsprintf(
$conn_r,
'JOIN %T p ON p.revisionID = r.id',
$path_table->getTableName());
}
if ($this->commitHashes) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T hash_rel ON hash_rel.revisionID = r.id',
ArcanistDifferentialRevisionHash::TABLE_NAME);
}
if ($this->ccs) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T cc_rel ON cc_rel.revisionID = r.id '.
'AND cc_rel.relation = %s '.
'AND cc_rel.objectPHID in (%Ls)',
DifferentialRevision::RELATIONSHIP_TABLE,
DifferentialRevision::RELATION_SUBSCRIBED,
$this->ccs);
}
if ($this->reviewers) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T reviewer_rel ON reviewer_rel.revisionID = r.id '.
'AND reviewer_rel.relation = %s '.
'AND reviewer_rel.objectPHID in (%Ls)',
DifferentialRevision::RELATIONSHIP_TABLE,
DifferentialRevision::RELATION_REVIEWER,
$this->reviewers);
}
if ($this->subscribers) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T sub_rel ON sub_rel.revisionID = r.id '.
'AND sub_rel.relation IN (%Ls) '.
'AND sub_rel.objectPHID in (%Ls)',
DifferentialRevision::RELATIONSHIP_TABLE,
array(
DifferentialRevision::RELATION_SUBSCRIBED,
DifferentialRevision::RELATION_REVIEWER,
),
$this->subscribers);
}
if ($this->responsibles) {
$joins[] = qsprintf(
$conn_r,
'LEFT JOIN %T responsibles_rel ON responsibles_rel.revisionID = r.id '.
'AND responsibles_rel.relation = %s '.
'AND responsibles_rel.objectPHID in (%Ls)',
DifferentialRevision::RELATIONSHIP_TABLE,
DifferentialRevision::RELATION_REVIEWER,
$this->responsibles);
}
if ($this->draftAuthors) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T inline_comment ON inline_comment.revisionID = r.id '.
'AND inline_comment.commentID is NULL',
id(new DifferentialInlineComment())->getTableName());
}
$joins = implode(' ', $joins);
return $joins;
}
/**
* @task internal
*/
private function buildWhereClause($conn_r) {
$where = array();
if ($this->pathIDs) {
$path_clauses = array();
$repo_info = igroup($this->pathIDs, 'repositoryID');
foreach ($repo_info as $repository_id => $paths) {
$path_clauses[] = qsprintf(
$conn_r,
'(repositoryID = %d AND pathID IN (%Ld))',
$repository_id,
ipull($paths, 'pathID'));
}
$path_clauses = '('.implode(' OR ', $path_clauses).')';
$where[] = $path_clauses;
}
if ($this->authors) {
$where[] = qsprintf(
$conn_r,
'authorPHID IN (%Ls)',
$this->authors);
}
if ($this->draftAuthors) {
$where[] = qsprintf(
$conn_r,
'inline_comment.authorPHID IN (%Ls)',
$this->draftAuthors);
}
if ($this->revIDs) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->revIDs);
}
if ($this->commitHashes) {
$hash_clauses = array();
foreach ($this->commitHashes as $info) {
list($type, $hash) = $info;
$hash_clauses[] = qsprintf(
$conn_r,
'(hash_rel.type = %s AND hash_rel.hash = %s)',
$type,
$hash);
}
$hash_clauses = '('.implode(' OR ', $hash_clauses).')';
$where[] = $hash_clauses;
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->responsibles) {
$where[] = qsprintf(
$conn_r,
'(responsibles_rel.objectPHID IS NOT NULL OR r.authorPHID IN (%Ls))',
$this->responsibles);
}
if ($this->branches) {
$where[] = qsprintf(
$conn_r,
'r.branchName in (%Ls)',
$this->branches);
}
if ($this->arcanistProjectPHIDs) {
$where[] = qsprintf(
$conn_r,
'r.arcanistProjectPHID in (%Ls)',
$this->arcanistProjectPHIDs);
}
switch ($this->status) {
case self::STATUS_ANY:
break;
case self::STATUS_OPEN:
$where[] = qsprintf(
$conn_r,
'status IN (%Ld)',
array(
ArcanistDifferentialRevisionStatus::NEEDS_REVIEW,
ArcanistDifferentialRevisionStatus::NEEDS_REVISION,
ArcanistDifferentialRevisionStatus::ACCEPTED,
));
break;
case self::STATUS_ACCEPTED:
$where[] = qsprintf(
$conn_r,
'status IN (%Ld)',
array(
ArcanistDifferentialRevisionStatus::ACCEPTED,
));
break;
case self::STATUS_COMMITTED:
+ phlog(
+ "WARNING: DifferentialRevisionQuery using deprecated ".
+ "STATUS_COMMITTED constant. This will be removed soon. ".
+ "Use STATUS_CLOSED.");
+ // fallthrough
+ case self::STATUS_CLOSED:
$where[] = qsprintf(
$conn_r,
'status IN (%Ld)',
array(
- ArcanistDifferentialRevisionStatus::COMMITTED,
+ ArcanistDifferentialRevisionStatus::CLOSED,
));
break;
case self::STATUS_ABANDONED:
$where[] = qsprintf(
$conn_r,
'status IN (%Ld)',
array(
ArcanistDifferentialRevisionStatus::ABANDONED,
));
break;
default:
throw new Exception(
"Unknown revision status filter constant '{$this->status}'!");
}
if ($where) {
$where = 'WHERE '.implode(' AND ', $where);
} else {
$where = '';
}
return $where;
}
/**
* @task internal
*/
private function buildGroupByClause($conn_r) {
$join_triggers = array_merge(
$this->pathIDs,
$this->ccs,
$this->reviewers,
$this->subscribers,
$this->responsibles);
$needs_distinct = (count($join_triggers) > 1);
if ($needs_distinct) {
return 'GROUP BY r.id';
} else {
return '';
}
}
/**
* @task internal
*/
private function buildOrderByClause($conn_r) {
switch ($this->order) {
case self::ORDER_MODIFIED:
return 'ORDER BY r.dateModified DESC';
case self::ORDER_CREATED:
return 'ORDER BY r.dateCreated DESC';
case self::ORDER_PATH_MODIFIED:
if (!$this->pathIDs) {
throw new Exception(
"To use ORDER_PATH_MODIFIED, you must specify withPath().");
}
return 'ORDER BY p.epoch DESC';
default:
throw new Exception("Unknown query order constant '{$this->order}'.");
}
}
private function loadRelationships($conn_r, array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$relationships = queryfx_all(
$conn_r,
'SELECT * FROM %T WHERE revisionID in (%Ld) ORDER BY sequence',
DifferentialRevision::RELATIONSHIP_TABLE,
mpull($revisions, 'getID'));
$relationships = igroup($relationships, 'revisionID');
foreach ($revisions as $revision) {
$revision->attachRelationships(
idx(
$relationships,
$revision->getID(),
array()));
}
}
private function loadCommitPHIDs($conn_r, array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$commit_phids = queryfx_all(
$conn_r,
'SELECT * FROM %T WHERE revisionID IN (%Ld)',
DifferentialRevision::TABLE_COMMIT,
mpull($revisions, 'getID'));
$commit_phids = igroup($commit_phids, 'revisionID');
foreach ($revisions as $revision) {
$phids = idx($commit_phids, $revision->getID(), array());
$phids = ipull($phids, 'commitPHID');
$revision->attachCommitPHIDs($phids);
}
}
private function loadDiffIDs($conn_r, array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$diff_table = new DifferentialDiff();
$diff_ids = queryfx_all(
$conn_r,
'SELECT revisionID, id FROM %T WHERE revisionID IN (%Ld)
ORDER BY id DESC',
$diff_table->getTableName(),
mpull($revisions, 'getID'));
$diff_ids = igroup($diff_ids, 'revisionID');
foreach ($revisions as $revision) {
$ids = idx($diff_ids, $revision->getID(), array());
$ids = ipull($ids, 'id');
$revision->attachDiffIDs($ids);
}
}
private function loadActiveDiffs($conn_r, array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$diff_table = new DifferentialDiff();
$load_ids = array();
foreach ($revisions as $revision) {
$diffs = $revision->getDiffIDs();
if ($diffs) {
$load_ids[] = max($diffs);
}
}
$active_diffs = array();
if ($load_ids) {
$active_diffs = $diff_table->loadAllWhere(
'id IN (%Ld)',
$load_ids);
}
$active_diffs = mpull($active_diffs, null, 'getRevisionID');
foreach ($revisions as $revision) {
$revision->attachActiveDiff(idx($active_diffs, $revision->getID()));
}
}
public static function splitResponsible(array $revisions, $user_phid) {
$active = array();
$waiting = array();
$status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW;
// Bucket revisions into $active (revisions you need to do something
// about) and $waiting (revisions you're waiting on someone else to do
// something about).
foreach ($revisions as $revision) {
$needs_review = ($revision->getStatus() == $status_review);
$filter_is_author = ($revision->getAuthorPHID() == $user_phid);
// If exactly one of "needs review" and "the user is the author" is
// true, the user needs to act on it. Otherwise, they're waiting on
// it.
if ($needs_review ^ $filter_is_author) {
$active[] = $revision;
} else {
$waiting[] = $revision;
}
}
return array($active, $waiting);
}
}
diff --git a/src/applications/differential/query/revision/__init__.php b/src/applications/differential/query/revision/__init__.php
index 04b88010cc..93b50017a9 100644
--- a/src/applications/differential/query/revision/__init__.php
+++ b/src/applications/differential/query/revision/__init__.php
@@ -1,22 +1,23 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('arcanist', 'differential/constants/revisionhash');
phutil_require_module('arcanist', 'differential/constants/revisionstatus');
phutil_require_module('phabricator', 'applications/differential/storage/affectedpath');
phutil_require_module('phabricator', 'applications/differential/storage/diff');
phutil_require_module('phabricator', 'applications/differential/storage/inlinecomment');
phutil_require_module('phabricator', 'applications/differential/storage/revision');
phutil_require_module('phabricator', 'storage/qsprintf');
phutil_require_module('phabricator', 'storage/queryfx');
+phutil_require_module('phutil', 'error');
phutil_require_module('phutil', 'utils');
phutil_require_source('DifferentialRevisionQuery.php');
diff --git a/src/applications/differential/storage/revision/DifferentialRevision.php b/src/applications/differential/storage/revision/DifferentialRevision.php
index e25431f6ee..5e5303a30a 100644
--- a/src/applications/differential/storage/revision/DifferentialRevision.php
+++ b/src/applications/differential/storage/revision/DifferentialRevision.php
@@ -1,229 +1,229 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class DifferentialRevision extends DifferentialDAO {
protected $title;
protected $status;
protected $summary;
protected $testPlan;
protected $phid;
protected $authorPHID;
protected $lastReviewerPHID;
protected $dateCommitted;
protected $lineCount;
protected $attached = array();
protected $unsubscribed = array();
protected $mailKey;
protected $branchName;
protected $arcanistProjectPHID;
private $relationships;
private $commits;
private $activeDiff = false;
private $diffIDs;
const RELATIONSHIP_TABLE = 'differential_relationship';
const TABLE_COMMIT = 'differential_commit';
const RELATION_REVIEWER = 'revw';
const RELATION_SUBSCRIBED = 'subd';
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'attached' => self::SERIALIZATION_JSON,
'unsubscribed' => self::SERIALIZATION_JSON,
),
) + parent::getConfiguration();
}
public function loadCommitPHIDs() {
if (!$this->getID()) {
return ($this->commits = array());
}
$commits = queryfx_all(
$this->establishConnection('r'),
'SELECT commitPHID FROM %T WHERE revisionID = %d',
self::TABLE_COMMIT,
$this->getID());
$commits = ipull($commits, 'commitPHID');
return ($this->commits = $commits);
}
public function getCommitPHIDs() {
if ($this->commits === null) {
throw new Exception("Must attach commits first!");
}
return $this->commits;
}
public function getActiveDiff() {
// TODO: Because it's currently technically possible to create a revision
// without an associated diff, we allow an attached-but-null active diff.
// It would be good to get rid of this once we make diff-attaching
// transactional.
if ($this->activeDiff === false) {
throw new Exception("Must attach active diff first!");
}
return $this->activeDiff;
}
public function attachActiveDiff($diff) {
$this->activeDiff = $diff;
return $this;
}
public function getDiffIDs() {
if ($this->diffIDs === null) {
throw new Exception("Must attach diff IDs first!");
}
return $this->diffIDs;
}
public function attachDiffIDs(array $ids) {
rsort($ids);
$this->diffIDs = array_values($ids);
return $this;
}
public function attachCommitPHIDs(array $phids) {
$this->commits = array_values($phids);
return $this;
}
public function getAttachedPHIDs($type) {
return array_keys(idx($this->attached, $type, array()));
}
public function setAttachedPHIDs($type, array $phids) {
$this->attached[$type] = array_fill_keys($phids, array());
return $this;
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorPHIDConstants::PHID_TYPE_DREV);
}
public function loadDiffs() {
if (!$this->getID()) {
return array();
}
return id(new DifferentialDiff())->loadAllWhere(
'revisionID = %d',
$this->getID());
}
public function loadComments() {
if (!$this->getID()) {
return array();
}
return id(new DifferentialComment())->loadAllWhere(
'revisionID = %d',
$this->getID());
}
public function loadActiveDiff() {
return id(new DifferentialDiff())->loadOneWhere(
'revisionID = %d ORDER BY id DESC LIMIT 1',
$this->getID());
}
public function save() {
if (!$this->getMailKey()) {
$this->mailKey = Filesystem::readRandomCharacters(40);
}
return parent::save();
}
public function loadRelationships() {
if (!$this->getID()) {
$this->relationships = array();
return;
}
$data = queryfx_all(
$this->establishConnection('r'),
'SELECT * FROM %T WHERE revisionID = %d ORDER BY sequence',
self::RELATIONSHIP_TABLE,
$this->getID());
return $this->attachRelationships($data);
}
public function attachRelationships(array $relationships) {
$this->relationships = igroup($relationships, 'relation');
return $this;
}
public function getReviewers() {
return $this->getRelatedPHIDs(self::RELATION_REVIEWER);
}
public function getCCPHIDs() {
return $this->getRelatedPHIDs(self::RELATION_SUBSCRIBED);
}
private function getRelatedPHIDs($relation) {
if ($this->relationships === null) {
throw new Exception("Must load relationships!");
}
return ipull($this->getRawRelations($relation), 'objectPHID');
}
public function getRawRelations($relation) {
return idx($this->relationships, $relation, array());
}
public function getUnsubscribedPHIDs() {
return array_keys($this->getUnsubscribed());
}
public function loadReviewedBy() {
$reviewer = null;
if ($this->status == ArcanistDifferentialRevisionStatus::ACCEPTED ||
- $this->status == ArcanistDifferentialRevisionStatus::COMMITTED) {
+ $this->status == ArcanistDifferentialRevisionStatus::CLOSED) {
$comments = $this->loadComments();
foreach ($comments as $comment) {
$action = $comment->getAction();
if ($action == DifferentialAction::ACTION_ACCEPT) {
$reviewer = $comment->getAuthorPHID();
} else if ($action == DifferentialAction::ACTION_REJECT ||
$action == DifferentialAction::ACTION_ABANDON ||
$action == DifferentialAction::ACTION_RETHINK) {
$reviewer = null;
}
}
}
return $reviewer;
}
}
diff --git a/src/applications/feed/story/differential/PhabricatorFeedStoryDifferential.php b/src/applications/feed/story/differential/PhabricatorFeedStoryDifferential.php
index 15113cdca5..efa55e6766 100644
--- a/src/applications/feed/story/differential/PhabricatorFeedStoryDifferential.php
+++ b/src/applications/feed/story/differential/PhabricatorFeedStoryDifferential.php
@@ -1,76 +1,76 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorFeedStoryDifferential extends PhabricatorFeedStory {
public function getRequiredHandlePHIDs() {
$data = $this->getStoryData();
return array(
$this->getStoryData()->getAuthorPHID(),
$data->getValue('revision_phid'),
$data->getValue('revision_author_phid'),
);
}
public function getRequiredObjectPHIDs() {
return array(
$this->getStoryData()->getAuthorPHID(),
);
}
public function renderView() {
$data = $this->getStoryData();
$author_phid = $data->getAuthorPHID();
$view = new PhabricatorFeedStoryView();
$revision_phid = $data->getValue('revision_phid');
$action = $data->getValue('action');
$verb = DifferentialAction::getActionPastTenseVerb($action);
$view->setTitle(
$this->linkTo($author_phid).
" {$verb} revision ".
$this->linkTo($revision_phid).'.');
$view->setEpoch($data->getEpoch());
$action = $data->getValue('action');
switch ($action) {
case DifferentialAction::ACTION_CREATE:
- case DifferentialAction::ACTION_COMMIT:
+ case DifferentialAction::ACTION_CLOSE:
$full_size = true;
break;
default:
$full_size = false;
break;
}
if ($full_size) {
$view->setImage($this->getHandle($author_phid)->getImageURI());
$content = $this->renderSummary($data->getValue('feedback_content'));
$view->appendChild($content);
} else {
$view->setOneLineStory(true);
}
return $view;
}
}
diff --git a/src/applications/metamta/constants/notificationtype/MetaMTANotificationType.php b/src/applications/metamta/constants/notificationtype/MetaMTANotificationType.php
index 9200fdf6e9..c73ee3d9f6 100644
--- a/src/applications/metamta/constants/notificationtype/MetaMTANotificationType.php
+++ b/src/applications/metamta/constants/notificationtype/MetaMTANotificationType.php
@@ -1,32 +1,32 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class MetaMTANotificationType
extends MetaMTAConstants {
- const TYPE_DIFFERENTIAL_COMMITTED = 'differential-committed';
+ const TYPE_DIFFERENTIAL_CLOSED = 'differential-committed';
const TYPE_DIFFERENTIAL_CC = 'differential-cc';
const TYPE_DIFFERENTIAL_COMMENT = 'differential-comment';
const TYPE_MANIPHEST_PROJECTS = 'maniphest-projects';
const TYPE_MANIPHEST_PRIORITY = 'maniphest-priority';
const TYPE_MANIPHEST_CC = 'maniphest-cc';
const TYPE_MANIPHEST_OTHER = 'maniphest-other';
const TYPE_MANIPHEST_COMMENT = 'maniphest-comment';
}
diff --git a/src/applications/people/controller/settings/panels/emailpref/PhabricatorUserEmailPreferenceSettingsPanelController.php b/src/applications/people/controller/settings/panels/emailpref/PhabricatorUserEmailPreferenceSettingsPanelController.php
index d8d3b73dc7..61fc9f459c 100644
--- a/src/applications/people/controller/settings/panels/emailpref/PhabricatorUserEmailPreferenceSettingsPanelController.php
+++ b/src/applications/people/controller/settings/panels/emailpref/PhabricatorUserEmailPreferenceSettingsPanelController.php
@@ -1,253 +1,253 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorUserEmailPreferenceSettingsPanelController
extends PhabricatorUserSettingsPanelController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$preferences = $user->loadPreferences();
$pref_re_prefix = PhabricatorUserPreferences::PREFERENCE_RE_PREFIX;
$pref_vary = PhabricatorUserPreferences::PREFERENCE_VARY_SUBJECT;
$pref_no_self_mail = PhabricatorUserPreferences::PREFERENCE_NO_SELF_MAIL;
$errors = array();
if ($request->isFormPost()) {
if (PhabricatorMetaMTAMail::shouldMultiplexAllMail()) {
if ($request->getStr($pref_re_prefix) == 'default') {
$preferences->unsetPreference($pref_re_prefix);
} else {
$preferences->setPreference(
$pref_re_prefix,
$request->getBool($pref_re_prefix));
}
if ($request->getStr($pref_vary) == 'default') {
$preferences->unsetPreference($pref_vary);
} else {
$preferences->setPreference(
$pref_vary,
$request->getBool($pref_vary));
}
}
$preferences->setPreference(
$pref_no_self_mail,
$request->getStr($pref_no_self_mail));
$new_tags = $request->getArr('mailtags');
$mailtags = $preferences->getPreference('mailtags', array());
foreach ($this->getMailTags() as $key => $label) {
$mailtags[$key] = (bool)idx($new_tags, $key, false);
}
$preferences->setPreference('mailtags', $mailtags);
$preferences->save();
return id(new AphrontRedirectResponse())
->setURI('/settings/page/emailpref/?saved=true');
}
$notice = null;
if (!$errors) {
if ($request->getStr('saved')) {
$notice = new AphrontErrorView();
$notice->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
$notice->setTitle('Changes Saved');
$notice->appendChild('<p>Your changes have been saved.</p>');
}
} else {
$notice = new AphrontErrorView();
$notice->setTitle('Form Errors');
$notice->setErrors($errors);
}
$re_prefix_default = PhabricatorEnv::getEnvConfig('metamta.re-prefix')
? 'Enabled'
: 'Disabled';
$vary_default = PhabricatorEnv::getEnvConfig('metamta.vary-subjects')
? 'Vary'
: 'Do Not Vary';
$re_prefix_value = $preferences->getPreference($pref_re_prefix);
if ($re_prefix_value === null) {
$re_prefix_value = 'default';
} else {
$re_prefix_value = $re_prefix_value
? 'true'
: 'false';
}
$vary_value = $preferences->getPreference($pref_vary);
if ($vary_value === null) {
$vary_value = 'default';
} else {
$vary_value = $vary_value
? 'true'
: 'false';
}
$form = new AphrontFormView();
$form
->setUser($user)
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Self Actions')
->setName($pref_no_self_mail)
->setOptions(
array(
'0' => 'Send me an email when I take an action',
'1' => 'Do not send me an email when I take an action',
))
->setCaption('You can disable email about your own actions.')
->setValue($preferences->getPreference($pref_no_self_mail, 0)));
if (PhabricatorMetaMTAMail::shouldMultiplexAllMail()) {
$form
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Add "Re:" Prefix')
->setName($pref_re_prefix)
->setCaption(
'Enable this option to fix threading in Mail.app on OS X Lion, '.
'or if you like "Re:" in your email subjects.')
->setOptions(
array(
'default' => 'Use Server Default ('.$re_prefix_default.')',
'true' => 'Enable "Re:" prefix',
'false' => 'Disable "Re:" prefix',
))
->setValue($re_prefix_value))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Vary Subjects')
->setName($pref_vary)
->setCaption(
'This option adds more information email subjects, but may '.
'break threading in some clients.')
->setOptions(
array(
'default' => 'Use Server Default ('.$vary_default.')',
'true' => 'Vary Subjects',
'false' => 'Do Not Vary Subjects',
))
->setValue($vary_value));
}
$form
->appendChild(
'<br />'.
'<p class="aphront-form-instructions">'.
'You can customize what mail you receive from Phabricator here.'.
'</p>'.
'<p class="aphront-form-instructions">'.
'<strong>NOTE:</strong> If an update makes several changes (like '.
'adding CCs to a task, closing it, and adding a comment) you will '.
'still receive an email as long as at least one of the changes '.
'is set to notify you.'.
'</p>'
);
$mailtags = $preferences->getPreference('mailtags', array());
$form
->appendChild(
$this->buildMailTagCheckboxes(
$this->getDifferentialMailTags(),
$mailtags)
->setLabel('Differential'))
->appendChild(
$this->buildMailTagCheckboxes(
$this->getManiphestMailTags(),
$mailtags)
->setLabel('Maniphest'));
$form
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Save Preferences'));
$panel = new AphrontPanelView();
$panel->setHeader('Email Preferences');
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
$panel->appendChild($form);
return id(new AphrontNullView())
->appendChild(
array(
$notice,
$panel,
));
}
private function getMailTags() {
return array(
MetaMTANotificationType::TYPE_DIFFERENTIAL_CC =>
"Send me email when a revision's CCs change.",
- MetaMTANotificationType::TYPE_DIFFERENTIAL_COMMITTED =>
- "Send me email when a revision is committed.",
+ MetaMTANotificationType::TYPE_DIFFERENTIAL_CLOSED =>
+ "Send me email when a revision is closed.",
MetaMTANotificationType::TYPE_MANIPHEST_PROJECTS =>
"Send me email when a task's associated projects change.",
MetaMTANotificationType::TYPE_MANIPHEST_PRIORITY =>
"Send me email when a task's priority changes.",
MetaMTANotificationType::TYPE_MANIPHEST_CC =>
"Send me email when a task's CCs change.",
);
}
private function getManiphestMailTags() {
return array_select_keys(
$this->getMailTags(),
array(
MetaMTANotificationType::TYPE_MANIPHEST_PROJECTS,
MetaMTANotificationType::TYPE_MANIPHEST_PRIORITY,
MetaMTANotificationType::TYPE_MANIPHEST_CC,
));
}
private function getDifferentialMailTags() {
return array_select_keys(
$this->getMailTags(),
array(
MetaMTANotificationType::TYPE_DIFFERENTIAL_CC,
- MetaMTANotificationType::TYPE_DIFFERENTIAL_COMMITTED,
+ MetaMTANotificationType::TYPE_DIFFERENTIAL_CLOSED,
));
}
private function buildMailTagCheckboxes(
array $tags,
array $prefs) {
$control = new AphrontFormCheckboxControl();
foreach ($tags as $key => $label) {
$control->addCheckbox(
'mailtags['.$key.']',
1,
$label,
idx($prefs, $key, 1));
}
return $control;
}
}
diff --git a/src/applications/phid/handle/data/PhabricatorObjectHandleData.php b/src/applications/phid/handle/data/PhabricatorObjectHandleData.php
index cc7b25eb77..a50a106b4d 100644
--- a/src/applications/phid/handle/data/PhabricatorObjectHandleData.php
+++ b/src/applications/phid/handle/data/PhabricatorObjectHandleData.php
@@ -1,483 +1,483 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorObjectHandleData {
private $phids;
public function __construct(array $phids) {
$this->phids = array_unique($phids);
}
public static function loadOneHandle($phid) {
$handles = id(new PhabricatorObjectHandleData(array($phid)))->loadHandles();
return $handles[$phid];
}
public function loadObjects() {
$types = array();
foreach ($this->phids as $phid) {
$type = phid_get_type($phid);
$types[$type][] = $phid;
}
$objects = array();
foreach ($types as $type => $phids) {
switch ($type) {
case PhabricatorPHIDConstants::PHID_TYPE_USER:
$user_dao = newv('PhabricatorUser', array());
$users = $user_dao->loadAllWhere(
'phid in (%Ls)',
$phids);
foreach ($users as $user) {
$objects[$user->getPHID()] = $user;
}
break;
case PhabricatorPHIDConstants::PHID_TYPE_CMIT:
$commit_dao = newv('PhabricatorRepositoryCommit', array());
$commits = $commit_dao->loadAllWhere(
'phid IN (%Ls)',
$phids);
$commit_data = array();
if ($commits) {
$data_dao = newv('PhabricatorRepositoryCommitData', array());
$commit_data = $data_dao->loadAllWhere(
'commitID IN (%Ld)',
mpull($commits, 'getID'));
$commit_data = mpull($commit_data, null, 'getCommitID');
}
foreach ($commits as $commit) {
$data = idx($commit_data, $commit->getID());
if ($data) {
$commit->attachCommitData($data);
$objects[$commit->getPHID()] = $commit;
} else {
// If we couldn't load the commit data, just act as though we
// couldn't load the object at all so we don't load half an object.
}
}
break;
case PhabricatorPHIDConstants::PHID_TYPE_TASK:
$task_dao = newv('ManiphestTask', array());
$tasks = $task_dao->loadAllWhere(
'phid IN (%Ls)',
$phids);
foreach ($tasks as $task) {
$objects[$task->getPHID()] = $task;
}
break;
case PhabricatorPHIDConstants::PHID_TYPE_DREV:
$revision_dao = newv('DifferentialRevision', array());
$revisions = $revision_dao->loadAllWhere(
'phid IN (%Ls)',
$phids);
foreach ($revisions as $revision) {
$objects[$revision->getPHID()] = $revision;
}
break;
}
}
return $objects;
}
public function loadHandles() {
$types = phid_group_by_type($this->phids);
$handles = array();
$external_loaders = PhabricatorEnv::getEnvConfig('phid.external-loaders');
foreach ($types as $type => $phids) {
switch ($type) {
case PhabricatorPHIDConstants::PHID_TYPE_MAGIC:
// Black magic!
foreach ($phids as $phid) {
$handle = new PhabricatorObjectHandle();
$handle->setPHID($phid);
$handle->setType($type);
switch ($phid) {
case ManiphestTaskOwner::OWNER_UP_FOR_GRABS:
$handle->setName('Up For Grabs');
$handle->setFullName('upforgrabs (Up For Grabs)');
$handle->setComplete(true);
break;
case ManiphestTaskOwner::PROJECT_NO_PROJECT:
$handle->setName('No Project');
$handle->setFullName('noproject (No Project)');
$handle->setComplete(true);
break;
default:
$handle->setName('Foul Magicks');
break;
}
$handles[$phid] = $handle;
}
break;
case PhabricatorPHIDConstants::PHID_TYPE_USER:
$class = 'PhabricatorUser';
PhutilSymbolLoader::loadClass($class);
$object = newv($class, array());
$users = $object->loadAllWhere('phid IN (%Ls)', $phids);
$users = mpull($users, null, 'getPHID');
$image_phids = mpull($users, 'getProfileImagePHID');
$image_phids = array_unique(array_filter($image_phids));
$images = array();
if ($image_phids) {
$images = id(new PhabricatorFile())->loadAllWhere(
'phid IN (%Ls)',
$image_phids);
$images = mpull($images, 'getBestURI', 'getPHID');
}
foreach ($phids as $phid) {
$handle = new PhabricatorObjectHandle();
$handle->setPHID($phid);
$handle->setType($type);
if (empty($users[$phid])) {
$handle->setName('Unknown User');
} else {
$user = $users[$phid];
$handle->setName($user->getUsername());
$handle->setURI('/p/'.$user->getUsername().'/');
$handle->setEmail($user->getEmail());
$handle->setFullName(
$user->getUsername().' ('.$user->getRealName().')');
$handle->setAlternateID($user->getID());
$handle->setComplete(true);
$handle->setDisabled($user->getIsDisabled());
$img_uri = idx($images, $user->getProfileImagePHID());
if ($img_uri) {
$handle->setImageURI($img_uri);
}
}
$handles[$phid] = $handle;
}
break;
case PhabricatorPHIDConstants::PHID_TYPE_MLST:
$class = 'PhabricatorMetaMTAMailingList';
PhutilSymbolLoader::loadClass($class);
$object = newv($class, array());
$lists = $object->loadAllWhere('phid IN (%Ls)', $phids);
$lists = mpull($lists, null, 'getPHID');
foreach ($phids as $phid) {
$handle = new PhabricatorObjectHandle();
$handle->setPHID($phid);
$handle->setType($type);
if (empty($lists[$phid])) {
$handle->setName('Unknown Mailing List');
} else {
$list = $lists[$phid];
$handle->setEmail($list->getEmail());
$handle->setName($list->getName());
$handle->setURI($list->getURI());
$handle->setFullName($list->getName());
$handle->setComplete(true);
}
$handles[$phid] = $handle;
}
break;
case PhabricatorPHIDConstants::PHID_TYPE_DREV:
$class = 'DifferentialRevision';
PhutilSymbolLoader::loadClass($class);
$object = newv($class, array());
$revs = $object->loadAllWhere('phid in (%Ls)', $phids);
$revs = mpull($revs, null, 'getPHID');
foreach ($phids as $phid) {
$handle = new PhabricatorObjectHandle();
$handle->setPHID($phid);
$handle->setType($type);
if (empty($revs[$phid])) {
$handle->setName('Unknown Revision');
} else {
$rev = $revs[$phid];
$handle->setName($rev->getTitle());
$handle->setURI('/D'.$rev->getID());
$handle->setFullName('D'.$rev->getID().': '.$rev->getTitle());
$handle->setComplete(true);
$status = $rev->getStatus();
- if (($status == ArcanistDifferentialRevisionStatus::COMMITTED) ||
+ if (($status == ArcanistDifferentialRevisionStatus::CLOSED) ||
($status == ArcanistDifferentialRevisionStatus::ABANDONED)) {
$closed = PhabricatorObjectHandleStatus::STATUS_CLOSED;
$handle->setStatus($closed);
}
}
$handles[$phid] = $handle;
}
break;
case PhabricatorPHIDConstants::PHID_TYPE_CMIT:
$class = 'PhabricatorRepositoryCommit';
PhutilSymbolLoader::loadClass($class);
$object = newv($class, array());
$commits = $object->loadAllWhere('phid in (%Ls)', $phids);
$commits = mpull($commits, null, 'getPHID');
$repository_ids = array();
$callsigns = array();
if ($commits) {
$repository_ids = mpull($commits, 'getRepositoryID');
$repositories = id(new PhabricatorRepository())->loadAllWhere(
'id in (%Ld)', array_unique($repository_ids));
$callsigns = mpull($repositories, 'getCallsign');
}
foreach ($phids as $phid) {
$handle = new PhabricatorObjectHandle();
$handle->setPHID($phid);
$handle->setType($type);
if (empty($commits[$phid]) ||
!isset($callsigns[$repository_ids[$phid]])) {
$handle->setName('Unknown Commit');
} else {
$commit = $commits[$phid];
$callsign = $callsigns[$repository_ids[$phid]];
$repository = $repositories[$repository_ids[$phid]];
$commit_identifier = $commit->getCommitIdentifier();
// In case where the repository for the commit was deleted,
// we don't have have info about the repository anymore.
if ($repository) {
$name = $repository->formatCommitName($commit_identifier);
$handle->setName($name);
} else {
$handle->setName('Commit '.'r'.$callsign.$commit_identifier);
}
$handle->setURI('/r'.$callsign.$commit_identifier);
$handle->setFullName('r'.$callsign.$commit_identifier);
$handle->setTimestamp($commit->getEpoch());
$handle->setComplete(true);
}
$handles[$phid] = $handle;
}
break;
case PhabricatorPHIDConstants::PHID_TYPE_TASK:
$class = 'ManiphestTask';
PhutilSymbolLoader::loadClass($class);
$object = newv($class, array());
$tasks = $object->loadAllWhere('phid in (%Ls)', $phids);
$tasks = mpull($tasks, null, 'getPHID');
foreach ($phids as $phid) {
$handle = new PhabricatorObjectHandle();
$handle->setPHID($phid);
$handle->setType($type);
if (empty($tasks[$phid])) {
$handle->setName('Unknown Revision');
} else {
$task = $tasks[$phid];
$handle->setName($task->getTitle());
$handle->setURI('/T'.$task->getID());
$handle->setFullName('T'.$task->getID().': '.$task->getTitle());
$handle->setComplete(true);
$handle->setAlternateID($task->getID());
if ($task->getStatus() != ManiphestTaskStatus::STATUS_OPEN) {
$closed = PhabricatorObjectHandleStatus::STATUS_CLOSED;
$handle->setStatus($closed);
}
}
$handles[$phid] = $handle;
}
break;
case PhabricatorPHIDConstants::PHID_TYPE_FILE:
$class = 'PhabricatorFile';
PhutilSymbolLoader::loadClass($class);
$object = newv($class, array());
$files = $object->loadAllWhere('phid IN (%Ls)', $phids);
$files = mpull($files, null, 'getPHID');
foreach ($phids as $phid) {
$handle = new PhabricatorObjectHandle();
$handle->setPHID($phid);
$handle->setType($type);
if (empty($files[$phid])) {
$handle->setName('Unknown File');
} else {
$file = $files[$phid];
$handle->setName($file->getName());
$handle->setURI($file->getBestURI());
$handle->setComplete(true);
}
$handles[$phid] = $handle;
}
break;
case PhabricatorPHIDConstants::PHID_TYPE_PROJ:
$class = 'PhabricatorProject';
PhutilSymbolLoader::loadClass($class);
$object = newv($class, array());
$projects = $object->loadAllWhere('phid IN (%Ls)', $phids);
$projects = mpull($projects, null, 'getPHID');
foreach ($phids as $phid) {
$handle = new PhabricatorObjectHandle();
$handle->setPHID($phid);
$handle->setType($type);
if (empty($projects[$phid])) {
$handle->setName('Unknown Project');
} else {
$project = $projects[$phid];
$handle->setName($project->getName());
$handle->setURI('/project/view/'.$project->getID().'/');
$handle->setComplete(true);
}
$handles[$phid] = $handle;
}
break;
case PhabricatorPHIDConstants::PHID_TYPE_REPO:
$class = 'PhabricatorRepository';
PhutilSymbolLoader::loadClass($class);
$object = newv($class, array());
$repositories = $object->loadAllWhere('phid in (%Ls)', $phids);
$repositories = mpull($repositories, null, 'getPHID');
foreach ($phids as $phid) {
$handle = new PhabricatorObjectHandle();
$handle->setPHID($phid);
$handle->setType($type);
if (empty($repositories[$phid])) {
$handle->setName('Unknown Repository');
} else {
$repository = $repositories[$phid];
$handle->setName($repository->getCallsign());
$handle->setURI('/diffusion/'.$repository->getCallsign().'/');
$handle->setComplete(true);
}
$handles[$phid] = $handle;
}
break;
case PhabricatorPHIDConstants::PHID_TYPE_OPKG:
$class = 'PhabricatorOwnersPackage';
PhutilSymbolLoader::loadClass($class);
$object = newv($class, array());
$packages = $object->loadAllWhere('phid in (%Ls)', $phids);
$packages = mpull($packages, null, 'getPHID');
foreach ($phids as $phid) {
$handle = new PhabricatorObjectHandle();
$handle->setPHID($phid);
$handle->setType($type);
if (empty($packages[$phid])) {
$handle->setName('Unknown Package');
} else {
$package = $packages[$phid];
$handle->setName($package->getName());
$handle->setURI('/owners/package/'.$package->getID().'/');
$handle->setComplete(true);
}
$handles[$phid] = $handle;
}
break;
case PhabricatorPHIDConstants::PHID_TYPE_APRJ:
$project_dao = newv('PhabricatorRepositoryArcanistProject', array());
$projects = $project_dao->loadAllWhere(
'phid IN (%Ls)',
$phids);
$projects = mpull($projects, null, 'getPHID');
foreach ($phids as $phid) {
$handle = new PhabricatorObjectHandle();
$handle->setPHID($phid);
$handle->setType($type);
if (empty($projects[$phid])) {
$handle->setName('Unknown Arcanist Project');
} else {
$project = $projects[$phid];
$handle->setName($project->getName());
$handle->setComplete(true);
}
$handles[$phid] = $handle;
}
break;
case PhabricatorPHIDConstants::PHID_TYPE_WIKI:
$document_dao = newv('PhrictionDocument', array());
$content_dao = newv('PhrictionContent', array());
$conn = $document_dao->establishConnection('r');
$documents = queryfx_all(
$conn,
'SELECT * FROM %T document JOIN %T content
ON document.contentID = content.id
WHERE document.phid IN (%Ls)',
$document_dao->getTableName(),
$content_dao->getTableName(),
$phids);
$documents = ipull($documents, null, 'phid');
foreach ($phids as $phid) {
$handle = new PhabricatorObjectHandle();
$handle->setPHID($phid);
$handle->setType($type);
if (empty($documents[$phid])) {
$handle->setName('Unknown Document');
} else {
$info = $documents[$phid];
$handle->setName($info['title']);
$handle->setURI(PhrictionDocument::getSlugURI($info['slug']));
$handle->setComplete(true);
}
$handles[$phid] = $handle;
}
break;
default:
$loader = null;
if (isset($external_loaders[$type])) {
$loader = $external_loaders[$type];
} else if (isset($external_loaders['*'])) {
$loader = $external_loaders['*'];
}
if ($loader) {
PhutilSymbolLoader::loadClass($loader);
$object = newv($loader, array());
$handles += $object->loadHandles($phids);
break;
}
foreach ($phids as $phid) {
$handle = new PhabricatorObjectHandle();
$handle->setType($type);
$handle->setPHID($phid);
$handle->setName('Unknown Object');
$handle->setFullName('An Unknown Object');
$handles[$phid] = $handle;
}
break;
}
}
return $handles;
}
}
diff --git a/src/applications/repository/parser/default/PhabricatorRepositoryDefaultCommitMessageDetailParser.php b/src/applications/repository/parser/default/PhabricatorRepositoryDefaultCommitMessageDetailParser.php
index 4a84c997e4..3be0bbbe87 100644
--- a/src/applications/repository/parser/default/PhabricatorRepositoryDefaultCommitMessageDetailParser.php
+++ b/src/applications/repository/parser/default/PhabricatorRepositoryDefaultCommitMessageDetailParser.php
@@ -1,88 +1,88 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* TODO: Facebook extends this (I think?), but should it?
*/
class PhabricatorRepositoryDefaultCommitMessageDetailParser
extends PhabricatorRepositoryCommitMessageDetailParser {
public function parseCommitDetails() {
$commit = $this->getCommit();
$data = $this->getCommitData();
$details = nonempty($data->getCommitDetails(), array());
$message = $data->getCommitMessage();
$author_name = $data->getAuthorName();
// TODO: Some day, it would be good to drive all of this via
// DifferentialFieldSpecification configuration directly.
$match = null;
if (preg_match(
'/^\s*Differential Revision:\s*(\S+)\s*$/mi',
$message,
$match)) {
// NOTE: We now accept ONLY full URIs because if we accept numeric IDs
// then anyone importing the Phabricator repository will have their
- // first few thousand revisions marked committed. This does mean that
+ // first few thousand revisions marked closed. This does mean that
// some older revisions won't re-parse correctly, but that shouldn't
// really affect anyone. If necessary, an install can extend the parser
// and restore the older, more-liberal parsing fairly easily.
$id = DifferentialRevisionIDFieldSpecification::parseRevisionIDFromURI(
$match[1]);
if ($id) {
$details['differential.revisionID'] = $id;
$revision = id(new DifferentialRevision())->load($id);
if ($revision) {
$details['differential.revisionPHID'] = $revision->getPHID();
}
}
}
if (preg_match(
'/^\s*Reviewed By:\s*(\S+)\s*$/mi',
$message,
$match)) {
$details['reviewerName'] = $match[1];
$reviewer_phid = $this->resolveUserPHID($details['reviewerName']);
if ($reviewer_phid) {
$details['reviewerPHID'] = $reviewer_phid;
} else {
unset($details['reviewerPHID']);
}
} else {
unset($details['reviewerName']);
unset($details['reviewerPHID']);
}
$author_phid = $this->resolveUserPHID($author_name);
if ($author_phid) {
$details['authorPHID'] = $author_phid;
} else {
unset($details['authorPHID']);
}
$data->setCommitDetails($details);
}
}
diff --git a/src/applications/repository/worker/commitmessageparser/base/PhabricatorRepositoryCommitMessageParserWorker.php b/src/applications/repository/worker/commitmessageparser/base/PhabricatorRepositoryCommitMessageParserWorker.php
index 1e3a5150ca..3c54d4f7ab 100644
--- a/src/applications/repository/worker/commitmessageparser/base/PhabricatorRepositoryCommitMessageParserWorker.php
+++ b/src/applications/repository/worker/commitmessageparser/base/PhabricatorRepositoryCommitMessageParserWorker.php
@@ -1,183 +1,183 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
abstract class PhabricatorRepositoryCommitMessageParserWorker
extends PhabricatorRepositoryCommitParserWorker {
abstract protected function getCommitHashes(
PhabricatorRepository $repository,
PhabricatorRepositoryCommit $commit);
final protected function updateCommitData($author, $message) {
$commit = $this->commit;
$data = id(new PhabricatorRepositoryCommitData())->loadOneWhere(
'commitID = %d',
$commit->getID());
if (!$data) {
$data = new PhabricatorRepositoryCommitData();
}
$data->setCommitID($commit->getID());
$data->setAuthorName($author);
$data->setCommitMessage($message);
$repository = $this->repository;
$detail_parser = $repository->getDetail(
'detail-parser',
'PhabricatorRepositoryDefaultCommitMessageDetailParser');
if ($detail_parser) {
PhutilSymbolLoader::loadClass($detail_parser);
$parser_obj = newv($detail_parser, array($commit, $data));
$parser_obj->parseCommitDetails();
}
$author_phid = $data->getCommitDetail('authorPHID');
if ($author_phid) {
$commit->setAuthorPHID($author_phid);
$commit->save();
}
$data->save();
$conn_w = id(new DifferentialRevision())->establishConnection('w');
// NOTE: The `differential_commit` table has a unique ID on `commitPHID`,
// preventing more than one revision from being associated with a commit.
// Generally this is good and desirable, but with the advent of hash
// tracking we may end up in a situation where we match several different
// revisions. We just kind of ignore this and pick one, we might want to
// revisit this and do something differently. (If we match several revisions
// someone probably did something very silly, though.)
$revision_id = $data->getCommitDetail('differential.revisionID');
if (!$revision_id) {
$hashes = $this->getCommitHashes(
$this->repository,
$this->commit);
if ($hashes) {
$query = new DifferentialRevisionQuery();
$query->withCommitHashes($hashes);
$revisions = $query->execute();
if (!empty($revisions)) {
$revision = $this->identifyBestRevision($revisions);
$revision_id = $revision->getID();
}
}
}
if ($revision_id) {
$revision = id(new DifferentialRevision())->load($revision_id);
if ($revision) {
queryfx(
$conn_w,
'INSERT IGNORE INTO %T (revisionID, commitPHID) VALUES (%d, %s)',
DifferentialRevision::TABLE_COMMIT,
$revision->getID(),
$commit->getPHID());
if ($revision->getStatus() !=
- ArcanistDifferentialRevisionStatus::COMMITTED) {
+ ArcanistDifferentialRevisionStatus::CLOSED) {
$date_committed = $this->getDateCommitted($commit);
if ($date_committed) {
$revision->setDateCommitted($date_committed);
}
$message = null;
$committer = $data->getCommitDetail('authorPHID');
if (!$committer) {
$committer = $revision->getAuthorPHID();
- $message = 'Change committed by '.$data->getAuthorName().'.';
+ $message = 'Closed by '.$data->getAuthorName().'.';
}
$editor = new DifferentialCommentEditor(
$revision,
$committer,
- DifferentialAction::ACTION_COMMIT);
+ DifferentialAction::ACTION_CLOSE);
$editor->setIsDaemonWorkflow(true);
$editor->setMessage($message)->save();
}
}
}
}
protected function getDateCommitted(PhabricatorRepositoryCommit $commit) {
return null;
}
/**
* When querying for revisions by hash, more than one revision may be found.
* This function identifies the "best" revision from such a set. Typically,
* there is only one revision found. Otherwise, we try to pick an accepted
* revision first, followed by an open revision, and otherwise we go with a
- * committed or abandoned revision as a last resort.
+ * closed or abandoned revision as a last resort.
*/
private function identifyBestRevision(array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
// get the simplest, common case out of the way
if (count($revisions) == 1) {
return reset($revisions);
}
$first_choice = array();
$second_choice = array();
$third_choice = array();
foreach ($revisions as $revision) {
switch ($revision->getStatus()) {
// "Accepted" revisions -- ostensibly what we're looking for!
case ArcanistDifferentialRevisionStatus::ACCEPTED:
$first_choice[] = $revision;
break;
// "Open" revisions
case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
$second_choice[] = $revision;
break;
// default is a wtf? here
default:
case ArcanistDifferentialRevisionStatus::ABANDONED:
- case ArcanistDifferentialRevisionStatus::COMMITTED:
+ case ArcanistDifferentialRevisionStatus::CLOSED:
$third_choice[] = $revision;
break;
}
}
// go down the ladder like a bro at last call
if (!empty($first_choice)) {
return $this->identifyMostRecentRevision($first_choice);
}
if (!empty($second_choice)) {
return $this->identifyMostRecentRevision($second_choice);
}
if (!empty($third_choice)) {
return $this->identifyMostRecentRevision($third_choice);
}
}
/**
* Given a set of revisions, returns the revision with the latest
* updated time. This is ostensibly the most recent revision.
*/
private function identifyMostRecentRevision(array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$revisions = msort($revisions, 'getDateModified');
return end($revisions);
}
}
diff --git a/src/applications/search/index/indexer/differential/PhabricatorSearchDifferentialIndexer.php b/src/applications/search/index/indexer/differential/PhabricatorSearchDifferentialIndexer.php
index 5202073054..1d437a879d 100644
--- a/src/applications/search/index/indexer/differential/PhabricatorSearchDifferentialIndexer.php
+++ b/src/applications/search/index/indexer/differential/PhabricatorSearchDifferentialIndexer.php
@@ -1,115 +1,115 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group search
*/
final class PhabricatorSearchDifferentialIndexer
extends PhabricatorSearchDocumentIndexer {
public static function indexRevision(DifferentialRevision $rev) {
$doc = new PhabricatorSearchAbstractDocument();
$doc->setPHID($rev->getPHID());
$doc->setDocumentType(PhabricatorPHIDConstants::PHID_TYPE_DREV);
$doc->setDocumentTitle($rev->getTitle());
$doc->setDocumentCreated($rev->getDateCreated());
$doc->setDocumentModified($rev->getDateModified());
$doc->addField(
PhabricatorSearchField::FIELD_BODY,
$rev->getSummary());
$doc->addField(
PhabricatorSearchField::FIELD_TEST_PLAN,
$rev->getTestPlan());
$doc->addRelationship(
PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR,
$rev->getAuthorPHID(),
PhabricatorPHIDConstants::PHID_TYPE_USER,
$rev->getDateCreated());
- if ($rev->getStatus() != ArcanistDifferentialRevisionStatus::COMMITTED &&
+ if ($rev->getStatus() != ArcanistDifferentialRevisionStatus::CLOSED &&
$rev->getStatus() != ArcanistDifferentialRevisionStatus::ABANDONED) {
$doc->addRelationship(
PhabricatorSearchRelationship::RELATIONSHIP_OPEN,
$rev->getPHID(),
PhabricatorPHIDConstants::PHID_TYPE_DREV,
time());
}
$comments = id(new DifferentialInlineComment())->loadAllWhere(
'revisionID = %d AND commentID is not null',
$rev->getID());
$touches = array();
foreach ($comments as $comment) {
if (strlen($comment->getContent())) {
// TODO: we should also index inline comments.
$doc->addField(
PhabricatorSearchField::FIELD_COMMENT,
$comment->getContent());
}
$author = $comment->getAuthorPHID();
$touches[$author] = $comment->getDateCreated();
}
foreach ($touches as $touch => $time) {
$doc->addRelationship(
PhabricatorSearchRelationship::RELATIONSHIP_TOUCH,
$touch,
PhabricatorPHIDConstants::PHID_TYPE_USER,
$time);
}
$rev->loadRelationships();
// If a revision needs review, the owners are the reviewers. Otherwise, the
- // owner is the author (e.g., accepted, rejected, committed).
+ // owner is the author (e.g., accepted, rejected, closed).
if ($rev->getStatus() == ArcanistDifferentialRevisionStatus::NEEDS_REVIEW) {
foreach ($rev->getReviewers() as $phid) {
$doc->addRelationship(
PhabricatorSearchRelationship::RELATIONSHIP_OWNER,
$phid,
PhabricatorPHIDConstants::PHID_TYPE_USER,
$rev->getDateModified()); // Bogus timestamp.
}
} else {
$doc->addRelationship(
PhabricatorSearchRelationship::RELATIONSHIP_OWNER,
$rev->getAuthorPHID(),
PhabricatorPHIDConstants::PHID_TYPE_USER,
$rev->getDateCreated());
}
$ccphids = $rev->getCCPHIDs();
$handles = id(new PhabricatorObjectHandleData($ccphids))
->loadHandles();
foreach ($handles as $phid => $handle) {
$doc->addRelationship(
PhabricatorSearchRelationship::RELATIONSHIP_SUBSCRIBER,
$phid,
$handle->getType(),
$rev->getDateModified()); // Bogus timestamp.
}
self::reindexAbstractDocument($doc);
}
}
diff --git a/src/docs/userguide/remarkup.diviner b/src/docs/userguide/remarkup.diviner
index 9dc4bb01bb..f3cd27bad5 100644
--- a/src/docs/userguide/remarkup.diviner
+++ b/src/docs/userguide/remarkup.diviner
@@ -1,357 +1,357 @@
@title Remarkup Reference
@group userguide
Explains how to make bold text; this makes your words louder so you can win
arguments.
= Overview =
Phabricator uses a lightweight markup language called "Remarkup", similar to
other lightweight markup langauges like Markdown and Wiki markup.
This document describes how to format text using Remarkup.
= Quick Reference =
All the syntax is explained in more detail below, but this is a quick guide to
formatting text in Remarkup.
These are inline styles, and can be applied to most text:
**bold** //italic// ##monospaced## `monospaced` ~~deleted~~
D123 T123 rX123 # Link to Objects
{D123} {T123} # Link to Objects (Full Name)
{F123} # Embed Images
@username # Mention a user
[[wiki page]] # Link to Phriction
[[wiki page | name]] # Named link to Phriction
http://xyz/ # Link to web
[[http://xyz/ | name]] # Named link to web
These are block styles, and must be separated from surrounding text by
empty lines:
= Large Header =
== Smaller Header ==
> Quoted Text
Use "- " or "* " for bulleted lists, and "# " for numbered lists.
Use ``` or indent two spaces for code.
Use %%% for a literal block.
= Basic Styling =
Format **basic text styles** like this:
**bold text**
//italic text//
##monospaced text##
`monospaced text`
~~deleted text~~
Those produce **bold text**, //italic text//, ##monospaced text##,
`monospaced text` and ~~deleted text~~, respectively.
= Layout =
Make **headers** like this:
= Large Header =
== Smaller Header ==
===== Very Small Header =====
You can optionally omit the trailing "=" signs -- that is, these are the same:
== Smaller Header ==
== Smaller Header
This produces headers like the ones in this document. Make sure you have an
empty line before and after the header.
Make **lists** by beginning each item with a "-" or a "*":
lang=text
- milk
- eggs
- bread
* duck
* duck
* goose
This produces a list like this:
- milk
- eggs
- bread
(Note that you need to put a space after the "-" or "*".)
You can make numbered lists with a "#" instead of "-" or "*":
# Articuno
# Zapdos
# Moltres
You can also nest lists:
```- Body
- Head
- Arm
- Elbow
- Hand
# Thumb
# Index
# Middle
# Ring
# Pinkie
- Leg
- Knee
- Foot```
...which produces:
- Body
- Head
- Arm
- Elbow
- Hand
# Thumb
# Index
# Middle
# Ring
# Pinkie
- Leg
- Knee
- Foot
If you prefer, you can indent lists using multiple characters to show indent
depth, like this:
```- Tree
-- Branch
--- Twig```
As expected, this produces:
- Tree
-- Branch
--- Twig
Make **code blocks** by indenting two spaces:
f(x, y);
You can also use three backticks to enclose the code block:
```f(x, y);
g(f);```
You can specify a language for syntax highlighting with "lang=xxx":
lang=text
lang=html
<a href="#">...</a>
This will highlight the block using a highlighter for that language, if one is
available (in most cases, this means you need to configure Pygments):
lang=html
<a href="#">...</a>
You can also use a "COUNTEREXAMPLE" header to show that a block of code is
bad and shouldn't be copied:
lang=text
COUNTEREXAMPLE
function f() {
global $$variable_variable;
}
This produces a block like this:
COUNTEREXAMPLE
function f() {
global $$variable_variable;
}
You can use ##lines=N## to limit the vertical size of a chunk of code, and
##name=some_name.ext## to give it a name. For example, this:
lang=text
lang=html, name=example.html, lines=12, counterexample
...
...produces this:
lang=html, name=example.html, lines=12, counterexample
<p>Apple</p>
<p>Apricot</p>
<p>Avocado</p>
<p>Banana</p>
<p>Bilberry</p>
<p>Blackberry</p>
<p>Blackcurrant</p>
<p>Blueberry</p>
<p>Currant</p>
<p>Cherry</p>
<p>Cherimoya</p>
<p>Clementine</p>
<p>Date</p>
<p>Damson</p>
<p>Durian</p>
<p>Eggplant</p>
<p>Elderberry</p>
<p>Feijoa</p>
<p>Gooseberry</p>
<p>Grape</p>
<p>Grapefruit</p>
<p>Guava</p>
<p>Huckleberry</p>
<p>Jackfruit</p>
<p>Jambul</p>
<p>Kiwi fruit</p>
<p>Kumquat</p>
<p>Legume</p>
<p>Lemon</p>
<p>Lime</p>
<p>Lychee</p>
<p>Mandarine</p>
<p>Mango</p>
<p>Mangostine</p>
<p>Melon</p>
You can also use "NOTE:" to call out an important idea.
NOTE: Don't cross the streams!
= Linking URIs =
URIs are automatically linked: http://phabricator.org/
If you have a URI with problematic characters in it, like
"##http://comma.org/,##", you can surround it with angle brackets:
<http://comma.org/,>
This will force the parser to consume the whole URI: <http://comma.org/,>
You can also use create named links, where you choose the displayed text. These
work within Phabricator or on the internet at large:
[[/herald/transcript/ | Herald Transcripts]]
[[http://www.boring-legal-documents.com/ | exciting legal documents]]
= Linking to Objects =
You can link to Differential revisions, Diffusion commits and Maniphest tasks
by mentioning the name of an object:
D123 # Link to Differential revision D123
rX123 # Link to SVN commit 123 from the "X" repository
rXaf3192cd5 # Link to Git commit "af3192cd5..." from the "X" repository.
# You must specify at least 7 characters of the hash.
T123 # Link to Maniphest task T123
You can also link directly to a comment in Maniphest and Differential:
T123#4 # Link to comment #4 of T123
You can also generate full-name references to some objects by using braces:
{D123} # Link to Differential revision D123 with the full name
{T123} # Link to Maniphest task T123 with the full name
These references will also show when an object changes state (for instance, a
-task is closed or a revision is committed).
+task or revision is closed).
= Quoting Text =
To quote text, preface it with an ">":
> This is quoted text.
This appears like this:
> This is quoted text.
= Embedding Images =
You can embed an image by using braces to refer to it:
{F123} # Embed the image file F123
In most interfaces, you can drag-and-drop an image from your computer into the
text area to upload and reference it.
Some browsers (e.g. Chrome) support uploading an image data just by pasting them
from clipboard into the text area.
You can set file display options like this:
{F123, layout=left, float, size=full}
Valid options are:
- **layout** left (default), center, right, inline, link (render a link
instead of a thumbnail for images)
- **float** If layout is set to left or right, the image will be floated so
text wraps around it.
- **size** thumb (default), full
- **name** with `layout=link` or for non-images, use this name for the link
text
= Embedding Media =
If you set configuration flags, you can embed media directly in text:
- **files.enable-proxy**: allows you to paste in image URLs and have them
render inline.
- **remarkup.enable-embedded-youtube**: allows you to paste in YouTube videos
and have them render inline.
These options are disabled by default because they have security and/or
silliness implications, read their descriptions in ##default.conf.php## before
enabling them.
= Image Macros =
You can upload image macros (Files -> Image Macros) which will replace text
strings with the image you specify. For instance, you could upload an image of a
dancing banana to create a macro named "peanutbutterjellytime", and then any
time you type that string it will be replaced with the image of a dancing
banana.
= Mentioning Users =
In Differential and Maniphest, you can mention another user by writing:
@username
When you submit your comment, this will add them as a CC on the revision or task
if they aren't already CC'd.
= Phriction Documents =
You can link to Phriction documents with a name or path:
Make sure you sign and date your [[legal/Letter of Marque and Reprisal]]!
With a pipe (##|##), you can retitle the link. Use this to mislead your
opponents:
Check out these [[legal/boring_documents/ | exciting legal documents]]!
= Literal Blocks =
To place text in a literal block use "%%%":
%%%Text that won't be processed by remarkup
[[http://www.example.com | example]]
%%%
Remarkup will not process the text inside of literal blocks (other than to
escape HTML, linkify URLs, and preserve line breaks).
diff --git a/src/infrastructure/daemon/irc/handler/whatsnew/PhabricatorIRCWhatsNewHandler.php b/src/infrastructure/daemon/irc/handler/whatsnew/PhabricatorIRCWhatsNewHandler.php
index aa6d2bb41f..ff04dfc1c1 100644
--- a/src/infrastructure/daemon/irc/handler/whatsnew/PhabricatorIRCWhatsNewHandler.php
+++ b/src/infrastructure/daemon/irc/handler/whatsnew/PhabricatorIRCWhatsNewHandler.php
@@ -1,165 +1,165 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Responds to "Whats new?" using the feed.
*
* @group irc
*/
final class PhabricatorIRCWhatsNewHandler extends PhabricatorIRCHandler {
private $floodblock = 0;
public function receiveMessage(PhabricatorIRCMessage $message) {
switch ($message->getCommand()) {
case 'PRIVMSG':
$reply_to = $message->getReplyTo();
if (!$reply_to) {
break;
}
$message = $message->getMessageText();
$prompt = '~what( i|\')?s new\?~i';
if (preg_match($prompt, $message)) {
if (time() < $this->floodblock) {
return;
}
$this->floodblock = time() + 60;
$this->getLatest($reply_to);
}
break;
}
}
public function getLatest($reply_to) {
$latest = $this->getConduit()->callMethodSynchronous(
'feed.query',
array(
'limit'=>5
));
$phids = array();
foreach ($latest as $action) {
if (isset($action['data']['actor_phid'])) {
$uid = $action['data']['actor_phid'];
}
else {
$uid = $action['authorPHID'];
}
switch ($action['class']) {
case 'PhabricatorFeedStoryDifferential':
$phids[] = $action['data']['revision_phid'];
break;
case 'PhabricatorFeedStoryAudit':
$phids[] = $action['data']['commitPHID'];
break;
case 'PhabricatorFeedStoryManiphest':
$phids[] = $action['data']['taskPHID'];
break;
default:
$phids[] = $uid;
break;
}
array_push($phids,$uid);
}
$infs = $this->getConduit()->callMethodSynchronous(
'phid.query',
array(
'phids'=>$phids
));
$cphid = 0;
foreach ($latest as $action) {
if (isset($action['data']['actor_phid'])) {
$uid = $action['data']['actor_phid'];
}
else {
$uid = $action['authorPHID'];
}
switch ($action['class']) {
case 'PhabricatorFeedStoryDifferential':
$rinf = $infs[$action['data']['revision_phid']];
break;
case 'PhabricatorFeedStoryAudit':
$rinf = $infs[$action['data']['commitPHID']];
break;
case 'PhabricatorFeedStoryManiphest':
$rinf = $infs[$action['data']['taskPHID']];
break;
default:
$rinf = array('name'=>$action['class']);
break;
}
$uinf = $infs[$uid];
$action = $this->getRhetoric($action['data']['action']);
$user = $uinf['name'];
$title = $rinf['fullName'];
$uri = $rinf['uri'];
$color = chr(3);
$blue = $color.'12';
$gray = $color.'15';
$bold = chr(2);
$reset = chr(15);
$content = "{$bold}{$user}{$reset} {$gray}{$action} {$blue}{$bold}".
"{$title}{$reset} - {$gray}{$uri}{$reset}";
$this->write('PRIVMSG',"{$reply_to} :{$content}");
}
return;
}
public function getRhetoric($input) {
switch ($input) {
case 'comment':
case 'none':
return 'commented on';
break;
case 'update':
return 'updated';
break;
case 'commit':
- return 'committed';
+ return 'closed';
break;
case 'create':
return 'created';
break;
case 'concern':
return 'raised concern for';
break;
case 'abandon':
return 'abandonned';
break;
case 'accept':
return 'accepted';
break;
default:
return $input;
break;
}
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jan 19, 18:04 (1 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1127233
Default Alt Text
(364 KB)

Event Timeline