Page MenuHomestyx hydra

No OneTemporary

diff --git a/conf/default.conf.php b/conf/default.conf.php
index 95f99eb5f9..ed6cc98bb0 100644
--- a/conf/default.conf.php
+++ b/conf/default.conf.php
@@ -1,289 +1,290 @@
<?php
/*
* Copyright 2011 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.
*/
return array(
// The root URI which Phabricator is installed on.
// Example: "http://phabricator.example.com/"
'phabricator.base-uri' => null,
// The default PHID for users who haven't uploaded a profile image. It should
// be 50x50px.
'user.default-profile-image-phid' => 'PHID-FILE-f57aaefce707fc4060ef',
// -- Access Control -------------------------------------------------------- //
// Phabricator users have one of three access levels: "anyone", "verified",
// or "admin". "anyone" means every user, including users who do not have
// accounts or are not logged into the system. "verified" is users who have
// accounts, are logged in, and have satisfied whatever verification steps
// the configuration requires (e.g., email verification and/or manual
// approval). "admin" is verified users with the "administrator" flag set.
// These configuration options control which access level is required to read
// data from Phabricator (e.g., view revisions and comments in Differential)
// and write data to Phabricator (e.g., upload files and create diffs). By
// default they are both set to "verified", meaning only verified user
// accounts can interact with the system in any meaningful way.
// If you are configuring an install for an open source project, you may
// want to reduce the "phabricator.read-access" requirement to "anyone". This
// will allow anyone to browse Phabricator content, even without logging in.
// Alternatively, you could raise the "phabricator.write-access" requirement
// to "admin", effectively creating a read-only install.
// Controls the minimum access level required to read data from Phabricator
// (e.g., view revisions in Differential). Allowed values are "anyone",
// "verified", or "admin". Note that "anyone" includes users who are not
// logged in! You should leave this at 'verified' unless you want your data
// to be publicly readable (e.g., you are developing open source software).
'phabricator.read-access' => 'verified',
// Controls the minimum access level required to write data to Phabricator
// (e.g., create new revisions in Differential). Allowed values are
// "verified" or "admin". Setting this to "admin" will effectively create a
// read-only install.
'phabricator.write-access' => 'verified',
// -- DarkConsole ----------------------------------------------------------- //
// DarkConsole is a administrative debugging/profiling tool built into
// Phabricator. You can leave it disabled unless you're developing against
// Phabricator.
// Determines whether or not DarkConsole is available. DarkConsole exposes
// some data like queries and stack traces, so you should be careful about
// turning it on in production (although users can not normally see it, even
// if the deployment configuration enables it).
'darkconsole.enabled' => true,
// Always enable DarkConsole, even for logged out users. This potentially
// exposes sensitive information to users, so make sure untrusted users can
// not access an install running in this mode. You should definitely leave
// this off in production. It is only really useful for using DarkConsole
// utilties to debug or profile logged-out pages. You must set
// 'darkconsole.enabled' to use this option.
'darkconsole.always-on' => false,
// Allows you to mask certain configuration values from appearing in the
// "Config" tab of DarkConsole.
'darkconsole.config-mask' => array(
'mysql.pass',
'amazon-ses.secret-key',
'recaptcha.private-key',
'phabricator.csrf-key',
'facebook.application-secret',
'github.secret',
),
// -- MySQL --------------------------------------------------------------- //
// The username to use when connecting to MySQL.
'mysql.user' => 'root',
// The password to use when connecting to MySQL.
'mysql.pass' => '',
// The MySQL server to connect to.
'mysql.host' => 'localhost',
// -- Email ----------------------------------------------------------------- //
// Some Phabricator tools send email notifications, e.g. when Differential
// revisions are updated or Maniphest tasks are changed. These options allow
// you to configure how email is delivered.
// You can test your mail setup by going to "MetaMTA" in the web interface,
// clicking "Send New Message", and then composing a message.
// Default address to send mail "From".
'metamta.default-address' => 'noreply@example.com',
// When a user takes an action which generates an email notification (like
// commenting on a Differential revision), Phabricator can either send that
// mail "From" the user's email address (like "alincoln@logcabin.com") or
// "From" the 'metamta.default-address' address. The user experience is
// generally better if Phabricator uses the user's real address as the "From"
// since the messages are easier to organize when they appear in mail clients,
// but this will only work if the server is authorized to send email on behalf
// of the "From" domain. Practically, this means:
// - If you are doing an install for Example Corp and all the users will
// have corporate @corp.example.com addresses and any hosts Phabricator
// is running on are authorized to send email from corp.example.com,
// you can enable this to make the user experience a little better.
// - If you are doing an install for an open source project and your
// users will be registering via Facebook and using personal email
// addresses, you MUST NOT enable this or virtually all of your outgoing
// email will vanish into SFP blackholes.
// - If your install is anything else, you're much safer leaving this
// off since the risk in turning it on is that your outgoing mail will
// mostly never arrive.
'metamta.can-send-as-user' => false,
// Adapter class to use to transmit mail to the MTA. The default uses
// PHPMailerLite, which will invoke PHP's mail() function. This is appropriate
// if mail() actually works on your host, but if you haven't configured mail
// it may not be so great. You can also use Amazon SES, by changing this to
// 'PhabricatorMailImplementationAmazonSESAdapter', signing up for SES, and
// filling in your 'amazon-ses.access-key' and 'amazon-ses.secret-key' below.
'metamta.mail-adapter' =>
'PhabricatorMailImplementationPHPMailerLiteAdapter',
// When email is sent, try to hand it off to the MTA immediately. This may
// be worth disabling if your MTA infrastructure is slow or unreliable. If you
// disable this option, you must run the 'metamta_mta.php' daemon or mail
// won't be handed off to the MTA. If you're using Amazon SES it can be a
// little slugish sometimes so it may be worth disabling this and moving to
// the daemon after you've got your install up and running. If you have a
// properly configured local MTA it should not be necessary to disable this.
'metamta.send-immediately' => true,
// If you're using Amazon SES to send email, provide your AWS access key
// and AWS secret key here. To set up Amazon SES with Phabricator, you need
// to:
// - Make sure 'metamta.mail-adapter' is set to:
// "PhabricatorMailImplementationAmazonSESAdapter"
// - Make sure 'metamta.can-send-as-user' is false.
// - Make sure 'metamta.default-address' is configured to something sensible.
// - Make sure 'metamta.default-address' is a validated SES "From" address.
'amazon-ses.access-key' => null,
'amazon-ses.secret-key' => null,
// -- Auth ------------------------------------------------------------------ //
// Can users login with a username/password, or by following the link from
// a password reset email? You can disable this and configure one or more
// OAuth providers instead.
'auth.password-auth-enabled' => true,
// -- Accounts -------------------------------------------------------------- //
// Is basic account information (email, real name, profile picture) editable?
// If you set up Phabricator to automatically synchronize account information
// from some other authoritative system, you can disable this to ensure
// information remains consistent across both systems.
'account.editable' => true,
// -- Facebook ------------------------------------------------------------ //
// Can users use Facebook credentials to login to Phabricator?
'facebook.auth-enabled' => false,
// Can users use Facebook credentials to create new Phabricator accounts?
'facebook.registration-enabled' => true,
// Are Facebook accounts permanently linked to Phabricator accounts, or can
// the user unlink them?
'facebook.auth-permanent' => false,
// The Facebook "Application ID" to use for Facebook API access.
'facebook.application-id' => null,
// The Facebook "Application Secret" to use for Facebook API access.
'facebook.application-secret' => null,
// -- Github ---------------------------------------------------------------- //
// Can users use Github credentials to login to Phabricator?
'github.auth-enabled' => false,
// Can users use Github credentials to create new Phabricator accounts?
'github.registration-enabled' => true,
// Are Github accounts permanently linked to Phabricator accounts, or can
// the user unlink them?
'github.auth-permanent' => false,
// The Github "Client ID" to use for Github API access.
'github.application-id' => null,
// The Github "Secret" to use for Github API access.
'github.application-secret' => null,
// -- Recaptcha ------------------------------------------------------------- //
// Is Recaptcha enabled? If disabled, captchas will not appear.
'recaptcha.enabled' => false,
// Your Recaptcha public key, obtained from Recaptcha.
'recaptcha.public-key' => null,
// Your Recaptcha private key, obtained from Recaptcha.
'recaptcha.private-key' => null,
// -- Misc ------------------------------------------------------------------ //
// This is hashed with other inputs to generate CSRF tokens. If you want, you
// can change it to some other string which is unique to your install. This
// will make your install more secure in a vague, mostly theoretical way. But
// it will take you like 3 seconds of mashing on your keyboard to set it up so
// you might as well.
'phabricator.csrf-key' => '0b7ec0592e0a2829d8b71df2fa269b2c6172eca3',
// Version string displayed in the footer. You probably should leave this
// alone.
'phabricator.version' => 'UNSTABLE',
// -- Files ----------------------------------------------------------------- //
// Lists which uploaded file types may be viewed in the browser. If a file
// has a mime type which does not appear in this list, it will always be
// downloaded instead of displayed. This is a security consideration: if a
// user uploads a file of type "text/html" and it is displayed as
// "text/html", they can eaily execute XSS attacks. This is also a usability
// consideration, since browsers tend to freak out when viewing enormous
// binary files.
//
// The keys in this array are viewable mime types; the values are the mime
// types they will be delivered as when they are viewed in the browser.
'files.viewable-mime-types' => array(
'image/jpeg' => 'image/jpeg',
'image/jpg' => 'image/jpg',
'image/png' => 'image/png',
+ 'image/gif' => 'image/gif',
'text/plain' => 'text/plain; charset=utf-8',
),
// -- Customization --------------------------------------------------------- //
// Paths to additional phutil libraries to load.
'load-libraries' => array(),
'aphront.default-application-configuration-class' =>
'AphrontDefaultApplicationConfiguration',
'controller.oauth-registration' =>
'PhabricatorOAuthDefaultRegistrationController',
// Directory that phd (the Phabricator daemon control script) should use to
// track running daemons.
'phd.pid-directory' => '/var/tmp/phd',
);
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index d455550255..782c95c853 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1,709 +1,715 @@
<?php
/**
* This file is automatically generated. Use 'phutil_mapper.php' to rebuild it.
* @generated
*/
phutil_register_library_map(array(
'class' =>
array(
'Aphront400Response' => 'aphront/response/400',
'Aphront404Response' => 'aphront/response/404',
'AphrontAjaxResponse' => 'aphront/response/ajax',
'AphrontApplicationConfiguration' => 'aphront/applicationconfiguration',
'AphrontController' => 'aphront/controller',
'AphrontCrumbsView' => 'view/layout/crumbs',
'AphrontDatabaseConnection' => 'storage/connection/base',
'AphrontDefaultApplicationConfiguration' => 'aphront/default/configuration',
'AphrontDefaultApplicationController' => 'aphront/default/controller',
'AphrontDialogResponse' => 'aphront/response/dialog',
'AphrontDialogView' => 'view/dialog',
'AphrontErrorView' => 'view/form/error',
'AphrontException' => 'aphront/exception/base',
'AphrontFileResponse' => 'aphront/response/file',
'AphrontFormCheckboxControl' => 'view/form/control/checkbox',
'AphrontFormControl' => 'view/form/control/base',
'AphrontFormDividerControl' => 'view/form/control/divider',
'AphrontFormFileControl' => 'view/form/control/file',
'AphrontFormMarkupControl' => 'view/form/control/markup',
'AphrontFormPasswordControl' => 'view/form/control/password',
'AphrontFormRecaptchaControl' => 'view/form/control/recaptcha',
'AphrontFormSelectControl' => 'view/form/control/select',
'AphrontFormStaticControl' => 'view/form/control/static',
'AphrontFormSubmitControl' => 'view/form/control/submit',
'AphrontFormTextAreaControl' => 'view/form/control/textarea',
'AphrontFormTextControl' => 'view/form/control/text',
'AphrontFormTokenizerControl' => 'view/form/control/tokenizer',
'AphrontFormView' => 'view/form/base',
'AphrontHeadsupActionListView' => 'view/layout/headsup/actionlist',
'AphrontHeadsupActionView' => 'view/layout/headsup/action',
'AphrontMySQLDatabaseConnection' => 'storage/connection/mysql',
'AphrontNullView' => 'view/null',
'AphrontPageView' => 'view/page/base',
'AphrontPanelView' => 'view/layout/panel',
'AphrontQueryConnectionException' => 'storage/exception/connection',
'AphrontQueryConnectionLostException' => 'storage/exception/connectionlost',
'AphrontQueryCountException' => 'storage/exception/count',
'AphrontQueryDuplicateKeyException' => 'storage/exception/duplicatekey',
'AphrontQueryException' => 'storage/exception/base',
'AphrontQueryObjectMissingException' => 'storage/exception/objectmissing',
'AphrontQueryParameterException' => 'storage/exception/parameter',
'AphrontQueryRecoverableException' => 'storage/exception/recoverable',
'AphrontRedirectException' => 'aphront/exception/redirect',
'AphrontRedirectResponse' => 'aphront/response/redirect',
'AphrontRequest' => 'aphront/request',
'AphrontRequestFailureView' => 'view/page/failure',
'AphrontResponse' => 'aphront/response/base',
'AphrontSideNavView' => 'view/layout/sidenav',
'AphrontTableView' => 'view/control/table',
'AphrontTokenizerTemplateView' => 'view/control/tokenizer',
'AphrontURIMapper' => 'aphront/mapper',
'AphrontView' => 'view/base',
'AphrontWebpageResponse' => 'aphront/response/webpage',
'CelerityAPI' => 'infrastructure/celerity/api',
'CelerityResourceController' => 'infrastructure/celerity/controller',
'CelerityResourceMap' => 'infrastructure/celerity/map',
'CelerityStaticResourceResponse' => 'infrastructure/celerity/response',
'ConduitAPIMethod' => 'applications/conduit/method/base',
'ConduitAPIRequest' => 'applications/conduit/protocol/request',
'ConduitAPI_conduit_connect_Method' => 'applications/conduit/method/conduit/connect',
'ConduitAPI_conduit_ping_Method' => 'applications/conduit/method/conduit/ping',
'ConduitAPI_daemon_launched_Method' => 'applications/conduit/method/daemon/launched',
'ConduitAPI_daemon_log_Method' => 'applications/conduit/method/daemon/log',
'ConduitAPI_differential_creatediff_Method' => 'applications/conduit/method/differential/creatediff',
'ConduitAPI_differential_createrevision_Method' => 'applications/conduit/method/differential/createrevision',
'ConduitAPI_differential_find_Method' => 'applications/conduit/method/differential/find',
'ConduitAPI_differential_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_markcommitted_Method' => 'applications/conduit/method/differential/markcommitted',
'ConduitAPI_differential_parsecommitmessage_Method' => 'applications/conduit/method/differential/parsecommitmessage',
'ConduitAPI_differential_setdiffproperty_Method' => 'applications/conduit/method/differential/setdiffproperty',
'ConduitAPI_differential_updaterevision_Method' => 'applications/conduit/method/differential/updaterevision',
'ConduitAPI_file_upload_Method' => 'applications/conduit/method/file/upload',
'ConduitAPI_user_find_Method' => 'applications/conduit/method/user/find',
'ConduitException' => 'applications/conduit/protocol/exception',
'DarkConsole' => 'aphront/console/api',
'DarkConsoleConfigPlugin' => 'aphront/console/plugin/config',
'DarkConsoleController' => 'aphront/console/controller',
'DarkConsoleCore' => 'aphront/console/core',
'DarkConsoleErrorLogPlugin' => 'aphront/console/plugin/errorlog',
'DarkConsoleErrorLogPluginAPI' => 'aphront/console/plugin/errorlog/api',
'DarkConsolePlugin' => 'aphront/console/plugin/base',
'DarkConsoleRequestPlugin' => 'aphront/console/plugin/request',
'DarkConsoleServicesPlugin' => 'aphront/console/plugin/services',
'DarkConsoleServicesPluginAPI' => 'aphront/console/plugin/services/api',
'DarkConsoleXHProfPlugin' => 'aphront/console/plugin/xhprof',
'DarkConsoleXHProfPluginAPI' => 'aphront/console/plugin/xhprof/api',
'DifferentialAction' => 'applications/differential/constants/action',
'DifferentialAddCommentView' => 'applications/differential/view/addcomment',
'DifferentialAttachController' => 'applications/differential/controller/attach',
'DifferentialCCWelcomeMail' => 'applications/differential/mail/ccwelcome',
'DifferentialChangeType' => 'applications/differential/constants/changetype',
'DifferentialChangeset' => 'applications/differential/storage/changeset',
'DifferentialChangesetDetailView' => 'applications/differential/view/changesetdetailview',
'DifferentialChangesetListView' => 'applications/differential/view/changesetlistview',
'DifferentialChangesetParser' => 'applications/differential/parser/changeset',
'DifferentialChangesetViewController' => 'applications/differential/controller/changesetview',
'DifferentialComment' => 'applications/differential/storage/comment',
'DifferentialCommentEditor' => 'applications/differential/editor/comment',
'DifferentialCommentMail' => 'applications/differential/mail/comment',
'DifferentialCommentPreviewController' => 'applications/differential/controller/commentpreview',
'DifferentialCommentSaveController' => 'applications/differential/controller/commentsave',
'DifferentialCommitMessage' => 'applications/differential/parser/commitmessage',
'DifferentialCommitMessageData' => 'applications/differential/data/commitmessage',
'DifferentialCommitMessageParserException' => 'applications/differential/parser/commitmessage/exception',
'DifferentialController' => 'applications/differential/controller/base',
'DifferentialDAO' => 'applications/differential/storage/base',
'DifferentialDiff' => 'applications/differential/storage/diff',
'DifferentialDiffContentMail' => 'applications/differential/mail/diffcontent',
'DifferentialDiffCreateController' => 'applications/differential/controller/diffcreate',
'DifferentialDiffProperty' => 'applications/differential/storage/diffproperty',
'DifferentialDiffTableOfContentsView' => 'applications/differential/view/difftableofcontents',
'DifferentialDiffViewController' => 'applications/differential/controller/diffview',
'DifferentialHunk' => 'applications/differential/storage/hunk',
'DifferentialInlineComment' => 'applications/differential/storage/inlinecomment',
'DifferentialInlineCommentEditController' => 'applications/differential/controller/inlinecommentedit',
'DifferentialInlineCommentPreviewController' => 'applications/differential/controller/inlinecommentpreview',
'DifferentialInlineCommentView' => 'applications/differential/view/inlinecomment',
'DifferentialLintStatus' => 'applications/differential/constants/lintstatus',
'DifferentialMail' => 'applications/differential/mail/base',
'DifferentialMarkupEngineFactory' => 'applications/differential/parser/markup',
'DifferentialNewDiffMail' => 'applications/differential/mail/newdiff',
'DifferentialReviewRequestMail' => 'applications/differential/mail/reviewrequest',
'DifferentialRevision' => 'applications/differential/storage/revision',
'DifferentialRevisionCommentListView' => 'applications/differential/view/revisioncommentlist',
'DifferentialRevisionCommentView' => 'applications/differential/view/revisioncomment',
'DifferentialRevisionControlSystem' => 'applications/differential/constants/revisioncontrolsystem',
'DifferentialRevisionDetailView' => 'applications/differential/view/revisiondetail',
'DifferentialRevisionEditController' => 'applications/differential/controller/revisionedit',
'DifferentialRevisionEditor' => 'applications/differential/editor/revision',
'DifferentialRevisionListController' => 'applications/differential/controller/revisionlist',
'DifferentialRevisionListData' => 'applications/differential/data/revisionlist',
'DifferentialRevisionStatus' => 'applications/differential/constants/revisionstatus',
'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/revisionupdatehistory',
'DifferentialRevisionViewController' => 'applications/differential/controller/revisionview',
'DifferentialSubscribeController' => 'applications/differential/controller/subscribe',
'DifferentialUnitStatus' => 'applications/differential/constants/unitstatus',
'DiffusionBranchInformation' => 'applications/diffusion/data/branch',
'DiffusionBranchQuery' => 'applications/diffusion/query/branch/base',
'DiffusionBranchTableView' => 'applications/diffusion/view/branchtable',
'DiffusionBrowseController' => 'applications/diffusion/controller/browse',
'DiffusionBrowseFileController' => 'applications/diffusion/controller/file',
'DiffusionBrowseQuery' => 'applications/diffusion/query/browse/base',
'DiffusionBrowseTableView' => 'applications/diffusion/view/browsetable',
'DiffusionChangeController' => 'applications/diffusion/controller/change',
'DiffusionCommitChangeTableView' => 'applications/diffusion/view/commitchangetable',
'DiffusionCommitController' => 'applications/diffusion/controller/commit',
'DiffusionController' => 'applications/diffusion/controller/base',
'DiffusionFileContent' => 'applications/diffusion/data/filecontent',
'DiffusionFileContentQuery' => 'applications/diffusion/query/filecontent/base',
'DiffusionGitBranchQuery' => 'applications/diffusion/query/branch/git',
'DiffusionGitBrowseQuery' => 'applications/diffusion/query/browse/git',
'DiffusionGitFileContentQuery' => 'applications/diffusion/query/filecontent/git',
'DiffusionGitHistoryQuery' => 'applications/diffusion/query/history/git',
'DiffusionGitPathIDQuery' => 'applications/diffusion/query/pathid/base',
'DiffusionGitRequest' => 'applications/diffusion/request/git',
'DiffusionHistoryController' => 'applications/diffusion/controller/history',
'DiffusionHistoryQuery' => 'applications/diffusion/query/history/base',
'DiffusionHistoryTableView' => 'applications/diffusion/view/historytable',
'DiffusionHomeController' => 'applications/diffusion/controller/home',
'DiffusionPathChange' => 'applications/diffusion/data/pathchange',
'DiffusionPathChangeQuery' => 'applications/diffusion/query/pathchange/base',
'DiffusionRepositoryController' => 'applications/diffusion/controller/repository',
'DiffusionRepositoryPath' => 'applications/diffusion/data/repositorypath',
'DiffusionRequest' => 'applications/diffusion/request/base',
'DiffusionSvnBrowseQuery' => 'applications/diffusion/query/browse/svn',
'DiffusionSvnFileContentQuery' => 'applications/diffusion/query/filecontent/svn',
'DiffusionSvnHistoryQuery' => 'applications/diffusion/query/history/svn',
'DiffusionView' => 'applications/diffusion/view/base',
'HeraldAction' => 'applications/herald/storage/action',
'HeraldActionConfig' => 'applications/herald/config/action',
'HeraldApplyTranscript' => 'applications/herald/storage/transcript/apply',
'HeraldCondition' => 'applications/herald/storage/condition',
'HeraldConditionConfig' => 'applications/herald/config/condition',
'HeraldConditionTranscript' => 'applications/herald/storage/transcript/condition',
'HeraldContentTypeConfig' => 'applications/herald/config/contenttype',
'HeraldController' => 'applications/herald/controller/base',
'HeraldDAO' => 'applications/herald/storage/base',
'HeraldDeleteController' => 'applications/herald/controller/delete',
+ 'HeraldDifferentialRevisionAdapter' => 'applications/herald/adapter/differential',
'HeraldDryRunAdapter' => 'applications/herald/adapter/dryrun',
'HeraldEffect' => 'applications/herald/engine/effect',
'HeraldEngine' => 'applications/herald/engine/engine',
'HeraldFieldConfig' => 'applications/herald/config/field',
'HeraldHomeController' => 'applications/herald/controller/home',
'HeraldInvalidConditionException' => 'applications/herald/engine/engine/exception',
'HeraldInvalidFieldException' => 'applications/herald/engine/engine/exception',
'HeraldNewController' => 'applications/herald/controller/new',
'HeraldObjectAdapter' => 'applications/herald/adapter/base',
'HeraldObjectTranscript' => 'applications/herald/storage/transcript/object',
'HeraldRecursiveConditionsException' => 'applications/herald/engine/engine/exception',
'HeraldRule' => 'applications/herald/storage/rule',
'HeraldRuleController' => 'applications/herald/controller/rule',
'HeraldRuleTranscript' => 'applications/herald/storage/transcript/rule',
'HeraldTestConsoleController' => 'applications/herald/controller/test',
'HeraldTranscript' => 'applications/herald/storage/transcript/base',
+ 'HeraldTranscriptController' => 'applications/herald/controller/transcript',
+ 'HeraldTranscriptListController' => 'applications/herald/controller/transcriptlist',
'HeraldValueTypeConfig' => 'applications/herald/config/valuetype',
'Javelin' => 'infrastructure/javelin/api',
'LiskDAO' => 'storage/lisk/dao',
'ManiphestController' => 'applications/maniphest/controller/base',
'ManiphestDAO' => 'applications/maniphest/storage/base',
'ManiphestTask' => 'applications/maniphest/storage/task',
'ManiphestTaskDetailController' => 'applications/maniphest/controller/taskdetail',
'ManiphestTaskEditController' => 'applications/maniphest/controller/taskedit',
'ManiphestTaskListController' => 'applications/maniphest/controller/tasklist',
'ManiphestTaskListView' => 'applications/maniphest/view/tasklist',
'ManiphestTaskPriority' => 'applications/maniphest/constants/priority',
'ManiphestTaskSelectorSearchController' => 'applications/maniphest/controller/taskselectorsearch',
'ManiphestTaskStatus' => 'applications/maniphest/constants/status',
'ManiphestTaskSummaryView' => 'applications/maniphest/view/tasksummary',
'ManiphestTransaction' => 'applications/maniphest/storage/transaction',
'ManiphestTransactionDetailView' => 'applications/maniphest/view/transactiondetail',
'ManiphestTransactionEditor' => 'applications/maniphest/editor/transaction',
'ManiphestTransactionListView' => 'applications/maniphest/view/transactionlist',
'ManiphestTransactionSaveController' => 'applications/maniphest/controller/transactionsave',
'ManiphestTransactionType' => 'applications/maniphest/constants/transactiontype',
'Phabricator404Controller' => 'applications/base/controller/404',
'PhabricatorAuthController' => 'applications/auth/controller/base',
'PhabricatorConduitAPIController' => 'applications/conduit/controller/api',
'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',
'PhabricatorController' => 'applications/base/controller/base',
'PhabricatorDaemon' => 'infrastructure/daemon/base',
'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',
'PhabricatorDaemonLogViewController' => 'applications/daemon/controller/logview',
'PhabricatorDaemonReference' => 'infrastructure/daemon/control/reference',
'PhabricatorDaemonTimelineConsoleController' => 'applications/daemon/controller/timeline',
'PhabricatorDaemonTimelineEventController' => 'applications/daemon/controller/timelineevent',
'PhabricatorDirectoryCategory' => 'applications/directory/storage/category',
'PhabricatorDirectoryCategoryDeleteController' => 'applications/directory/controller/categorydelete',
'PhabricatorDirectoryCategoryEditController' => 'applications/directory/controller/categoryedit',
'PhabricatorDirectoryCategoryListController' => 'applications/directory/controller/categorylist',
'PhabricatorDirectoryController' => 'applications/directory/controller/base',
'PhabricatorDirectoryDAO' => 'applications/directory/storage/base',
'PhabricatorDirectoryItem' => 'applications/directory/storage/item',
'PhabricatorDirectoryItemDeleteController' => 'applications/directory/controller/itemdelete',
'PhabricatorDirectoryItemEditController' => 'applications/directory/controller/itemedit',
'PhabricatorDirectoryItemListController' => 'applications/directory/controller/itemlist',
'PhabricatorDirectoryMainController' => 'applications/directory/controller/main',
'PhabricatorDraft' => 'applications/draft/storage/draft',
'PhabricatorDraftDAO' => 'applications/draft/storage/base',
'PhabricatorEmailLoginController' => 'applications/auth/controller/email',
'PhabricatorEmailTokenController' => 'applications/auth/controller/emailtoken',
'PhabricatorEnv' => 'infrastructure/env',
'PhabricatorFile' => 'applications/files/storage/file',
'PhabricatorFileController' => 'applications/files/controller/base',
'PhabricatorFileDAO' => 'applications/files/storage/base',
'PhabricatorFileListController' => 'applications/files/controller/list',
'PhabricatorFileStorageBlob' => 'applications/files/storage/storageblob',
'PhabricatorFileURI' => 'applications/files/uri',
'PhabricatorFileUploadController' => 'applications/files/controller/upload',
'PhabricatorFileViewController' => 'applications/files/controller/view',
'PhabricatorGoodForNothingWorker' => 'infrastructure/daemon/workers/worker/goodfornothing',
'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/selector',
'PhabricatorLiskDAO' => 'applications/base/storage/lisk',
'PhabricatorLoginController' => 'applications/auth/controller/login',
'PhabricatorLogoutController' => 'applications/auth/controller/logout',
'PhabricatorMailImplementationAdapter' => 'applications/metamta/adapter/base',
'PhabricatorMailImplementationAmazonSESAdapter' => 'applications/metamta/adapter/amazonses',
'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'applications/metamta/adapter/phpmailerlite',
'PhabricatorMetaMTAController' => 'applications/metamta/controller/base',
'PhabricatorMetaMTADAO' => 'applications/metamta/storage/base',
'PhabricatorMetaMTADaemon' => 'applications/metamta/daemon/mta',
'PhabricatorMetaMTAListController' => 'applications/metamta/controller/list',
'PhabricatorMetaMTAMail' => 'applications/metamta/storage/mail',
'PhabricatorMetaMTAMailingList' => 'applications/metamta/storage/mailinglist',
'PhabricatorMetaMTAMailingListEditController' => 'applications/metamta/controller/mailinglistedit',
'PhabricatorMetaMTAMailingListsController' => 'applications/metamta/controller/mailinglists',
'PhabricatorMetaMTASendController' => 'applications/metamta/controller/send',
'PhabricatorMetaMTAViewController' => 'applications/metamta/controller/view',
'PhabricatorOAuthDefaultRegistrationController' => 'applications/auth/controller/oauthregistration/default',
'PhabricatorOAuthDiagnosticsController' => 'applications/auth/controller/oauthdiagnostics',
'PhabricatorOAuthFailureView' => 'applications/auth/view/oauthfailure',
'PhabricatorOAuthLoginController' => 'applications/auth/controller/oauth',
'PhabricatorOAuthProvider' => 'applications/auth/oauth/provider/base',
'PhabricatorOAuthProviderFacebook' => 'applications/auth/oauth/provider/facebook',
'PhabricatorOAuthProviderGithub' => 'applications/auth/oauth/provider/github',
'PhabricatorOAuthRegistrationController' => 'applications/auth/controller/oauthregistration/base',
'PhabricatorOAuthUnlinkController' => 'applications/auth/controller/unlink',
'PhabricatorObjectHandle' => 'applications/phid/handle',
'PhabricatorObjectHandleData' => 'applications/phid/handle/data',
'PhabricatorObjectSelectorDialog' => 'view/control/objectselector',
'PhabricatorPHID' => 'applications/phid/storage/phid',
'PhabricatorPHIDAllocateController' => 'applications/phid/controller/allocate',
'PhabricatorPHIDConstants' => 'applications/phid/constants',
'PhabricatorPHIDController' => 'applications/phid/controller/base',
'PhabricatorPHIDDAO' => 'applications/phid/storage/base',
'PhabricatorPHIDListController' => 'applications/phid/controller/list',
'PhabricatorPHIDLookupController' => 'applications/phid/controller/lookup',
'PhabricatorPHIDType' => 'applications/phid/storage/type',
'PhabricatorPHIDTypeEditController' => 'applications/phid/controller/typeedit',
'PhabricatorPHIDTypeListController' => 'applications/phid/controller/typelist',
'PhabricatorPeopleController' => 'applications/people/controller/base',
'PhabricatorPeopleEditController' => 'applications/people/controller/edit',
'PhabricatorPeopleListController' => 'applications/people/controller/list',
'PhabricatorPeopleProfileController' => 'applications/people/controller/profile',
'PhabricatorPeopleProfileEditController' => 'applications/people/controller/profileedit',
'PhabricatorProject' => 'applications/project/storage/project',
'PhabricatorProjectAffiliation' => 'applications/project/storage/affiliation',
'PhabricatorProjectAffiliationEditController' => 'applications/project/controller/editaffiliation',
'PhabricatorProjectController' => 'applications/project/controller/base',
'PhabricatorProjectDAO' => 'applications/project/storage/base',
'PhabricatorProjectEditController' => 'applications/project/controller/edit',
'PhabricatorProjectListController' => 'applications/project/controller/list',
'PhabricatorProjectProfile' => 'applications/project/storage/profile',
'PhabricatorProjectProfileController' => 'applications/project/controller/profile',
'PhabricatorRemarkupRuleDifferential' => 'infrastructure/markup/remarkup/markuprule/differential',
'PhabricatorRemarkupRuleManiphest' => 'infrastructure/markup/remarkup/markuprule/maniphest',
'PhabricatorRepository' => 'applications/repository/storage/repository',
'PhabricatorRepositoryCommit' => 'applications/repository/storage/commit',
'PhabricatorRepositoryCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/base',
'PhabricatorRepositoryCommitData' => 'applications/repository/storage/commitdata',
'PhabricatorRepositoryCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/base',
'PhabricatorRepositoryCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/base',
'PhabricatorRepositoryCommitParserWorker' => 'applications/repository/worker/base',
'PhabricatorRepositoryCommitTaskDaemon' => 'applications/repository/daemon/committask',
'PhabricatorRepositoryController' => 'applications/repository/controller/base',
'PhabricatorRepositoryCreateController' => 'applications/repository/controller/create',
'PhabricatorRepositoryDAO' => 'applications/repository/storage/base',
'PhabricatorRepositoryDaemon' => 'applications/repository/daemon/base',
'PhabricatorRepositoryEditController' => 'applications/repository/controller/edit',
'PhabricatorRepositoryGitCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/git',
'PhabricatorRepositoryGitCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/git',
'PhabricatorRepositoryGitCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/git',
'PhabricatorRepositoryGitFetchDaemon' => 'applications/repository/daemon/gitfetch',
'PhabricatorRepositoryGitHubNotification' => 'applications/repository/storage/githubnotification',
'PhabricatorRepositoryGitHubPostReceiveController' => 'applications/repository/controller/github-post-receive',
'PhabricatorRepositoryListController' => 'applications/repository/controller/list',
'PhabricatorRepositorySvnCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/svn',
'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/svn',
'PhabricatorRepositorySvnCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/svn',
'PhabricatorRepositoryType' => 'applications/repository/constants/repositorytype',
'PhabricatorSearchAbstractDocument' => 'applications/search/index/abstractdocument',
'PhabricatorSearchBaseController' => 'applications/search/controller/base',
'PhabricatorSearchController' => 'applications/search/controller/search',
'PhabricatorSearchDAO' => 'applications/search/storage/base',
'PhabricatorSearchDifferentialIndexer' => 'applications/search/index/indexer/differential',
'PhabricatorSearchDocument' => 'applications/search/storage/document/document',
'PhabricatorSearchDocumentField' => 'applications/search/storage/document/field',
'PhabricatorSearchDocumentIndexer' => 'applications/search/index/indexer/base',
'PhabricatorSearchDocumentRelationship' => 'applications/search/storage/document/relationship',
'PhabricatorSearchExecutor' => 'applications/search/execute/base',
'PhabricatorSearchField' => 'applications/search/constants/field',
'PhabricatorSearchManiphestIndexer' => 'applications/search/index/indexer/maniphest',
'PhabricatorSearchMySQLExecutor' => 'applications/search/execute/mysql',
'PhabricatorSearchQuery' => 'applications/search/storage/query',
'PhabricatorSearchRelationship' => 'applications/search/constants/relationship',
'PhabricatorStandardPageView' => 'view/page/standard',
'PhabricatorTaskmasterDaemon' => 'infrastructure/daemon/workers/taskmaster',
'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',
'PhabricatorTypeaheadCommonDatasourceController' => 'applications/typeahead/controller/common',
'PhabricatorTypeaheadDatasourceController' => 'applications/typeahead/controller/base',
'PhabricatorUser' => 'applications/people/storage/user',
'PhabricatorUserDAO' => 'applications/people/storage/base',
'PhabricatorUserOAuthInfo' => 'applications/people/storage/useroauthinfo',
'PhabricatorUserProfile' => 'applications/people/storage/profile',
'PhabricatorUserSettingsController' => 'applications/people/controller/settings',
'PhabricatorWorker' => 'infrastructure/daemon/workers/worker',
'PhabricatorWorkerDAO' => 'infrastructure/daemon/workers/storage/base',
'PhabricatorWorkerTask' => 'infrastructure/daemon/workers/storage/task',
'PhabricatorWorkerTaskData' => 'infrastructure/daemon/workers/storage/taskdata',
'PhabricatorXHProfController' => 'applications/xhprof/controller/base',
'PhabricatorXHProfProfileController' => 'applications/xhprof/controller/profile',
'PhabricatorXHProfProfileSymbolView' => 'applications/xhprof/view/symbol',
'PhabricatorXHProfProfileTopLevelView' => 'applications/xhprof/view/toplevel',
),
'function' =>
array(
'_qsprintf_check_scalar_type' => 'storage/qsprintf',
'_qsprintf_check_type' => 'storage/qsprintf',
'celerity_generate_unique_node_id' => 'infrastructure/celerity/api',
'celerity_register_resource_map' => 'infrastructure/celerity/map',
'javelin_render_tag' => 'infrastructure/javelin/markup',
'phabricator_format_relative_time' => 'view/utils',
'phabricator_format_timestamp' => 'view/utils',
'phabricator_format_units_generic' => 'view/utils',
'phabricator_render_form' => 'infrastructure/javelin/markup',
'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(
'Aphront400Response' => 'AphrontResponse',
'Aphront404Response' => 'AphrontResponse',
'AphrontAjaxResponse' => 'AphrontResponse',
'AphrontCrumbsView' => 'AphrontView',
'AphrontDefaultApplicationConfiguration' => 'AphrontApplicationConfiguration',
'AphrontDefaultApplicationController' => 'AphrontController',
'AphrontDialogResponse' => 'AphrontResponse',
'AphrontDialogView' => 'AphrontView',
'AphrontErrorView' => 'AphrontView',
'AphrontFileResponse' => 'AphrontResponse',
'AphrontFormCheckboxControl' => 'AphrontFormControl',
'AphrontFormControl' => 'AphrontView',
'AphrontFormDividerControl' => 'AphrontFormControl',
'AphrontFormFileControl' => 'AphrontFormControl',
'AphrontFormMarkupControl' => 'AphrontFormControl',
'AphrontFormPasswordControl' => 'AphrontFormControl',
'AphrontFormRecaptchaControl' => 'AphrontFormControl',
'AphrontFormSelectControl' => 'AphrontFormControl',
'AphrontFormStaticControl' => 'AphrontFormControl',
'AphrontFormSubmitControl' => 'AphrontFormControl',
'AphrontFormTextAreaControl' => 'AphrontFormControl',
'AphrontFormTextControl' => 'AphrontFormControl',
'AphrontFormTokenizerControl' => 'AphrontFormControl',
'AphrontFormView' => 'AphrontView',
'AphrontHeadsupActionListView' => 'AphrontView',
'AphrontHeadsupActionView' => 'AphrontView',
'AphrontMySQLDatabaseConnection' => 'AphrontDatabaseConnection',
'AphrontNullView' => 'AphrontView',
'AphrontPageView' => 'AphrontView',
'AphrontPanelView' => 'AphrontView',
'AphrontQueryConnectionException' => 'AphrontQueryException',
'AphrontQueryConnectionLostException' => 'AphrontQueryRecoverableException',
'AphrontQueryCountException' => 'AphrontQueryException',
'AphrontQueryDuplicateKeyException' => 'AphrontQueryException',
'AphrontQueryObjectMissingException' => 'AphrontQueryException',
'AphrontQueryParameterException' => 'AphrontQueryException',
'AphrontQueryRecoverableException' => 'AphrontQueryException',
'AphrontRedirectException' => 'AphrontException',
'AphrontRedirectResponse' => 'AphrontResponse',
'AphrontRequestFailureView' => 'AphrontView',
'AphrontSideNavView' => 'AphrontView',
'AphrontTableView' => 'AphrontView',
'AphrontTokenizerTemplateView' => 'AphrontView',
'AphrontWebpageResponse' => 'AphrontResponse',
'CelerityResourceController' => 'AphrontController',
'ConduitAPI_conduit_connect_Method' => 'ConduitAPIMethod',
'ConduitAPI_conduit_ping_Method' => 'ConduitAPIMethod',
'ConduitAPI_daemon_launched_Method' => 'ConduitAPIMethod',
'ConduitAPI_daemon_log_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_creatediff_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_createrevision_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_find_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_getcommitmessage_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_getcommitpaths_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_getdiff_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_markcommitted_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_parsecommitmessage_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_setdiffproperty_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_updaterevision_Method' => 'ConduitAPIMethod',
'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod',
'ConduitAPI_user_find_Method' => 'ConduitAPIMethod',
'DarkConsoleConfigPlugin' => 'DarkConsolePlugin',
'DarkConsoleController' => 'PhabricatorController',
'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin',
'DarkConsoleRequestPlugin' => 'DarkConsolePlugin',
'DarkConsoleServicesPlugin' => 'DarkConsolePlugin',
'DarkConsoleXHProfPlugin' => 'DarkConsolePlugin',
'DifferentialAddCommentView' => 'AphrontView',
'DifferentialAttachController' => 'DifferentialController',
'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail',
'DifferentialChangeset' => 'DifferentialDAO',
'DifferentialChangesetDetailView' => 'AphrontView',
'DifferentialChangesetListView' => 'AphrontView',
'DifferentialChangesetViewController' => 'DifferentialController',
'DifferentialComment' => 'DifferentialDAO',
'DifferentialCommentMail' => 'DifferentialMail',
'DifferentialCommentPreviewController' => 'DifferentialController',
'DifferentialCommentSaveController' => 'DifferentialController',
'DifferentialController' => 'PhabricatorController',
'DifferentialDAO' => 'PhabricatorLiskDAO',
'DifferentialDiff' => 'DifferentialDAO',
'DifferentialDiffContentMail' => 'DifferentialMail',
'DifferentialDiffCreateController' => 'DifferentialController',
'DifferentialDiffProperty' => 'DifferentialDAO',
'DifferentialDiffTableOfContentsView' => 'AphrontView',
'DifferentialDiffViewController' => 'DifferentialController',
'DifferentialHunk' => 'DifferentialDAO',
'DifferentialInlineComment' => 'DifferentialDAO',
'DifferentialInlineCommentEditController' => 'DifferentialController',
'DifferentialInlineCommentPreviewController' => 'DifferentialController',
'DifferentialInlineCommentView' => 'AphrontView',
'DifferentialNewDiffMail' => 'DifferentialReviewRequestMail',
'DifferentialReviewRequestMail' => 'DifferentialMail',
'DifferentialRevision' => 'DifferentialDAO',
'DifferentialRevisionCommentListView' => 'AphrontView',
'DifferentialRevisionCommentView' => 'AphrontView',
'DifferentialRevisionDetailView' => 'AphrontView',
'DifferentialRevisionEditController' => 'DifferentialController',
'DifferentialRevisionListController' => 'DifferentialController',
'DifferentialRevisionUpdateHistoryView' => 'AphrontView',
'DifferentialRevisionViewController' => 'DifferentialController',
'DifferentialSubscribeController' => 'DifferentialController',
'DiffusionBranchTableView' => 'DiffusionView',
'DiffusionBrowseController' => 'DiffusionController',
'DiffusionBrowseFileController' => 'DiffusionController',
'DiffusionBrowseTableView' => 'DiffusionView',
'DiffusionChangeController' => 'DiffusionController',
'DiffusionCommitChangeTableView' => 'DiffusionView',
'DiffusionCommitController' => 'DiffusionController',
'DiffusionController' => 'PhabricatorController',
'DiffusionGitBranchQuery' => 'DiffusionBranchQuery',
'DiffusionGitBrowseQuery' => 'DiffusionBrowseQuery',
'DiffusionGitFileContentQuery' => 'DiffusionFileContentQuery',
'DiffusionGitHistoryQuery' => 'DiffusionHistoryQuery',
'DiffusionGitRequest' => 'DiffusionRequest',
'DiffusionHistoryController' => 'DiffusionController',
'DiffusionHistoryTableView' => 'DiffusionView',
'DiffusionHomeController' => 'DiffusionController',
'DiffusionRepositoryController' => 'DiffusionController',
'DiffusionSvnBrowseQuery' => 'DiffusionBrowseQuery',
'DiffusionSvnFileContentQuery' => 'DiffusionFileContentQuery',
'DiffusionSvnHistoryQuery' => 'DiffusionHistoryQuery',
'DiffusionView' => 'AphrontView',
'HeraldAction' => 'HeraldDAO',
'HeraldApplyTranscript' => 'HeraldDAO',
'HeraldCondition' => 'HeraldDAO',
'HeraldController' => 'PhabricatorController',
'HeraldDAO' => 'PhabricatorLiskDAO',
'HeraldDeleteController' => 'HeraldController',
+ 'HeraldDifferentialRevisionAdapter' => 'HeraldObjectAdapter',
'HeraldDryRunAdapter' => 'HeraldObjectAdapter',
'HeraldHomeController' => 'HeraldController',
'HeraldNewController' => 'HeraldController',
'HeraldRule' => 'HeraldDAO',
'HeraldRuleController' => 'HeraldController',
'HeraldTestConsoleController' => 'HeraldController',
'HeraldTranscript' => 'HeraldDAO',
+ 'HeraldTranscriptController' => 'HeraldController',
+ 'HeraldTranscriptListController' => 'HeraldController',
'ManiphestController' => 'PhabricatorController',
'ManiphestDAO' => 'PhabricatorLiskDAO',
'ManiphestTask' => 'ManiphestDAO',
'ManiphestTaskDetailController' => 'ManiphestController',
'ManiphestTaskEditController' => 'ManiphestController',
'ManiphestTaskListController' => 'ManiphestController',
'ManiphestTaskListView' => 'AphrontView',
'ManiphestTaskSelectorSearchController' => 'ManiphestController',
'ManiphestTaskSummaryView' => 'AphrontView',
'ManiphestTransaction' => 'ManiphestDAO',
'ManiphestTransactionDetailView' => 'AphrontView',
'ManiphestTransactionListView' => 'AphrontView',
'ManiphestTransactionSaveController' => 'ManiphestController',
'Phabricator404Controller' => 'PhabricatorController',
'PhabricatorAuthController' => 'PhabricatorController',
'PhabricatorConduitAPIController' => 'PhabricatorConduitController',
'PhabricatorConduitConnectionLog' => 'PhabricatorConduitDAO',
'PhabricatorConduitConsoleController' => 'PhabricatorConduitController',
'PhabricatorConduitController' => 'PhabricatorController',
'PhabricatorConduitDAO' => 'PhabricatorLiskDAO',
'PhabricatorConduitLogController' => 'PhabricatorConduitController',
'PhabricatorConduitMethodCallLog' => 'PhabricatorConduitDAO',
'PhabricatorController' => 'AphrontController',
'PhabricatorDaemon' => 'PhutilDaemon',
'PhabricatorDaemonConsoleController' => 'PhabricatorDaemonController',
'PhabricatorDaemonController' => 'PhabricatorController',
'PhabricatorDaemonDAO' => 'PhabricatorLiskDAO',
'PhabricatorDaemonLog' => 'PhabricatorDaemonDAO',
'PhabricatorDaemonLogEvent' => 'PhabricatorDaemonDAO',
'PhabricatorDaemonLogViewController' => 'PhabricatorDaemonController',
'PhabricatorDaemonTimelineConsoleController' => 'PhabricatorDaemonController',
'PhabricatorDaemonTimelineEventController' => 'PhabricatorDaemonController',
'PhabricatorDirectoryCategory' => 'PhabricatorDirectoryDAO',
'PhabricatorDirectoryCategoryDeleteController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryCategoryEditController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryCategoryListController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryController' => 'PhabricatorController',
'PhabricatorDirectoryDAO' => 'PhabricatorLiskDAO',
'PhabricatorDirectoryItem' => 'PhabricatorDirectoryDAO',
'PhabricatorDirectoryItemDeleteController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryItemEditController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryItemListController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryMainController' => 'PhabricatorDirectoryController',
'PhabricatorDraft' => 'PhabricatorDraftDAO',
'PhabricatorDraftDAO' => 'PhabricatorLiskDAO',
'PhabricatorEmailLoginController' => 'PhabricatorAuthController',
'PhabricatorEmailTokenController' => 'PhabricatorAuthController',
'PhabricatorFile' => 'PhabricatorFileDAO',
'PhabricatorFileController' => 'PhabricatorController',
'PhabricatorFileDAO' => 'PhabricatorLiskDAO',
'PhabricatorFileListController' => 'PhabricatorFileController',
'PhabricatorFileStorageBlob' => 'PhabricatorFileDAO',
'PhabricatorFileUploadController' => 'PhabricatorFileController',
'PhabricatorFileViewController' => 'PhabricatorFileController',
'PhabricatorGoodForNothingWorker' => 'PhabricatorWorker',
'PhabricatorLiskDAO' => 'LiskDAO',
'PhabricatorLoginController' => 'PhabricatorAuthController',
'PhabricatorLogoutController' => 'PhabricatorAuthController',
'PhabricatorMailImplementationAmazonSESAdapter' => 'PhabricatorMailImplementationPHPMailerLiteAdapter',
'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'PhabricatorMailImplementationAdapter',
'PhabricatorMetaMTAController' => 'PhabricatorController',
'PhabricatorMetaMTADAO' => 'PhabricatorLiskDAO',
'PhabricatorMetaMTADaemon' => 'PhabricatorDaemon',
'PhabricatorMetaMTAListController' => 'PhabricatorMetaMTAController',
'PhabricatorMetaMTAMail' => 'PhabricatorMetaMTADAO',
'PhabricatorMetaMTAMailingList' => 'PhabricatorMetaMTADAO',
'PhabricatorMetaMTAMailingListEditController' => 'PhabricatorMetaMTAController',
'PhabricatorMetaMTAMailingListsController' => 'PhabricatorMetaMTAController',
'PhabricatorMetaMTASendController' => 'PhabricatorMetaMTAController',
'PhabricatorMetaMTAViewController' => 'PhabricatorMetaMTAController',
'PhabricatorOAuthDefaultRegistrationController' => 'PhabricatorOAuthRegistrationController',
'PhabricatorOAuthDiagnosticsController' => 'PhabricatorAuthController',
'PhabricatorOAuthFailureView' => 'AphrontView',
'PhabricatorOAuthLoginController' => 'PhabricatorAuthController',
'PhabricatorOAuthProviderFacebook' => 'PhabricatorOAuthProvider',
'PhabricatorOAuthProviderGithub' => 'PhabricatorOAuthProvider',
'PhabricatorOAuthRegistrationController' => 'PhabricatorAuthController',
'PhabricatorOAuthUnlinkController' => 'PhabricatorAuthController',
'PhabricatorPHID' => 'PhabricatorPHIDDAO',
'PhabricatorPHIDAllocateController' => 'PhabricatorPHIDController',
'PhabricatorPHIDController' => 'PhabricatorController',
'PhabricatorPHIDDAO' => 'PhabricatorLiskDAO',
'PhabricatorPHIDListController' => 'PhabricatorPHIDController',
'PhabricatorPHIDLookupController' => 'PhabricatorPHIDController',
'PhabricatorPHIDType' => 'PhabricatorPHIDDAO',
'PhabricatorPHIDTypeEditController' => 'PhabricatorPHIDController',
'PhabricatorPHIDTypeListController' => 'PhabricatorPHIDController',
'PhabricatorPeopleController' => 'PhabricatorController',
'PhabricatorPeopleEditController' => 'PhabricatorPeopleController',
'PhabricatorPeopleListController' => 'PhabricatorPeopleController',
'PhabricatorPeopleProfileController' => 'PhabricatorPeopleController',
'PhabricatorPeopleProfileEditController' => 'PhabricatorPeopleController',
'PhabricatorProject' => 'PhabricatorProjectDAO',
'PhabricatorProjectAffiliation' => 'PhabricatorProjectDAO',
'PhabricatorProjectAffiliationEditController' => 'PhabricatorProjectController',
'PhabricatorProjectController' => 'PhabricatorController',
'PhabricatorProjectDAO' => 'PhabricatorLiskDAO',
'PhabricatorProjectEditController' => 'PhabricatorProjectController',
'PhabricatorProjectListController' => 'PhabricatorProjectController',
'PhabricatorProjectProfile' => 'PhabricatorProjectDAO',
'PhabricatorProjectProfileController' => 'PhabricatorProjectController',
'PhabricatorRemarkupRuleDifferential' => 'PhutilRemarkupRule',
'PhabricatorRemarkupRuleManiphest' => 'PhutilRemarkupRule',
'PhabricatorRepository' => 'PhabricatorRepositoryDAO',
'PhabricatorRepositoryCommit' => 'PhabricatorRepositoryDAO',
'PhabricatorRepositoryCommitChangeParserWorker' => 'PhabricatorRepositoryCommitParserWorker',
'PhabricatorRepositoryCommitData' => 'PhabricatorRepositoryDAO',
'PhabricatorRepositoryCommitDiscoveryDaemon' => 'PhabricatorRepositoryDaemon',
'PhabricatorRepositoryCommitMessageParserWorker' => 'PhabricatorRepositoryCommitParserWorker',
'PhabricatorRepositoryCommitParserWorker' => 'PhabricatorWorker',
'PhabricatorRepositoryCommitTaskDaemon' => 'PhabricatorRepositoryDaemon',
'PhabricatorRepositoryController' => 'PhabricatorController',
'PhabricatorRepositoryCreateController' => 'PhabricatorRepositoryController',
'PhabricatorRepositoryDAO' => 'PhabricatorLiskDAO',
'PhabricatorRepositoryDaemon' => 'PhabricatorDaemon',
'PhabricatorRepositoryEditController' => 'PhabricatorRepositoryController',
'PhabricatorRepositoryGitCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
'PhabricatorRepositoryGitCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',
'PhabricatorRepositoryGitCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
'PhabricatorRepositoryGitFetchDaemon' => 'PhabricatorRepositoryDaemon',
'PhabricatorRepositoryGitHubNotification' => 'PhabricatorRepositoryDAO',
'PhabricatorRepositoryGitHubPostReceiveController' => 'PhabricatorRepositoryController',
'PhabricatorRepositoryListController' => 'PhabricatorRepositoryController',
'PhabricatorRepositorySvnCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',
'PhabricatorRepositorySvnCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
'PhabricatorSearchBaseController' => 'PhabricatorController',
'PhabricatorSearchController' => 'PhabricatorSearchBaseController',
'PhabricatorSearchDAO' => 'PhabricatorLiskDAO',
'PhabricatorSearchDifferentialIndexer' => 'PhabricatorSearchDocumentIndexer',
'PhabricatorSearchDocument' => 'PhabricatorSearchDAO',
'PhabricatorSearchDocumentField' => 'PhabricatorSearchDAO',
'PhabricatorSearchDocumentRelationship' => 'PhabricatorSearchDAO',
'PhabricatorSearchManiphestIndexer' => 'PhabricatorSearchDocumentIndexer',
'PhabricatorSearchMySQLExecutor' => 'PhabricatorSearchExecutor',
'PhabricatorSearchQuery' => 'PhabricatorSearchDAO',
'PhabricatorStandardPageView' => 'AphrontPageView',
'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon',
'PhabricatorTimelineCursor' => 'PhabricatorTimelineDAO',
'PhabricatorTimelineDAO' => 'PhabricatorLiskDAO',
'PhabricatorTimelineEvent' => 'PhabricatorTimelineDAO',
'PhabricatorTimelineEventData' => 'PhabricatorTimelineDAO',
'PhabricatorTypeaheadCommonDatasourceController' => 'PhabricatorTypeaheadDatasourceController',
'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController',
'PhabricatorUser' => 'PhabricatorUserDAO',
'PhabricatorUserDAO' => 'PhabricatorLiskDAO',
'PhabricatorUserOAuthInfo' => 'PhabricatorUserDAO',
'PhabricatorUserProfile' => 'PhabricatorUserDAO',
'PhabricatorUserSettingsController' => 'PhabricatorPeopleController',
'PhabricatorWorkerDAO' => 'PhabricatorLiskDAO',
'PhabricatorWorkerTask' => 'PhabricatorWorkerDAO',
'PhabricatorWorkerTaskData' => 'PhabricatorWorkerDAO',
'PhabricatorXHProfController' => 'PhabricatorController',
'PhabricatorXHProfProfileController' => 'PhabricatorXHProfController',
'PhabricatorXHProfProfileSymbolView' => 'AphrontView',
'PhabricatorXHProfProfileTopLevelView' => 'AphrontView',
),
'requires_interface' =>
array(
),
));
diff --git a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
index 363f089b99..85a47facc6 100644
--- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
+++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
@@ -1,334 +1,335 @@
<?php
/*
* Copyright 2011 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 aphront
*/
class AphrontDefaultApplicationConfiguration
extends AphrontApplicationConfiguration {
public function __construct() {
}
public function getApplicationName() {
return 'aphront-default';
}
public function getURIMap() {
return $this->getResourceURIMapRules() + array(
'/' => array(
'$' => 'PhabricatorDirectoryMainController',
),
'/directory/' => array(
'item/$'
=> 'PhabricatorDirectoryItemListController',
'item/edit/(?:(?P<id>\d+)/)?$'
=> 'PhabricatorDirectoryItemEditController',
'item/delete/(?P<id>\d+)/'
=> 'PhabricatorDirectoryItemDeleteController',
'category/$'
=> 'PhabricatorDirectoryCategoryListController',
'category/edit/(?:(?P<id>\d+)/)?$'
=> 'PhabricatorDirectoryCategoryEditController',
'category/delete/(?P<id>\d+)/'
=> 'PhabricatorDirectoryCategoryDeleteController',
),
'/file/' => array(
'$' => 'PhabricatorFileListController',
'upload/$' => 'PhabricatorFileUploadController',
'(?P<view>info)/(?P<phid>[^/]+)/' => 'PhabricatorFileViewController',
'(?P<view>view)/(?P<phid>[^/]+)/' => 'PhabricatorFileViewController',
'(?P<view>download)/(?P<phid>[^/]+)/' => 'PhabricatorFileViewController',
),
'/phid/' => array(
'$' => 'PhabricatorPHIDLookupController',
'list/$' => 'PhabricatorPHIDListController',
'type/$' => 'PhabricatorPHIDTypeListController',
'type/edit/(?:(?P<id>\d+)/)?$' => 'PhabricatorPHIDTypeEditController',
'new/$' => 'PhabricatorPHIDAllocateController',
),
'/people/' => array(
'$' => 'PhabricatorPeopleListController',
'edit/(?:(?P<username>\w+)/)?$' => 'PhabricatorPeopleEditController',
),
'/p/(?P<username>\w+)/$' => 'PhabricatorPeopleProfileController',
'/profile/' => array(
'edit/$' => 'PhabricatorPeopleProfileEditController',
),
'/conduit/' => array(
'$' => 'PhabricatorConduitConsoleController',
'method/(?P<method>[^/]+)$' => 'PhabricatorConduitConsoleController',
'log/$' => 'PhabricatorConduitLogController',
),
'/api/(?P<method>[^/]+)$' => 'PhabricatorConduitAPIController',
'/D(?P<id>\d+)' => 'DifferentialRevisionViewController',
'/differential/' => array(
'$' => 'DifferentialRevisionListController',
'filter/(?P<filter>\w+)/$' => 'DifferentialRevisionListController',
'diff/' => array(
'(?P<id>\d+)/$' => 'DifferentialDiffViewController',
'create/$' => 'DifferentialDiffCreateController',
),
'changeset/$' => 'DifferentialChangesetViewController',
'revision/edit/(?:(?P<id>\d+)/)?$'
=> 'DifferentialRevisionEditController',
'comment/' => array(
'preview/(?P<id>\d+)/$' => 'DifferentialCommentPreviewController',
'save/$' => 'DifferentialCommentSaveController',
'inline/' => array(
'preview/(?P<id>\d+)/$' =>
'DifferentialInlineCommentPreviewController',
'edit/(?P<id>\d+)/$' => 'DifferentialInlineCommentEditController',
),
),
'attach/(?P<id>\d+)/(?P<type>\w+)/$' => 'DifferentialAttachController',
'subscribe/(?P<action>add|rem)/(?P<id>\d+)/$'
=> 'DifferentialSubscribeController',
),
'/typeahead/' => array(
'common/(?P<type>\w+)/$'
=> 'PhabricatorTypeaheadCommonDatasourceController',
),
'/mail/' => array(
'$' => 'PhabricatorMetaMTAListController',
'send/$' => 'PhabricatorMetaMTASendController',
'view/(?P<id>\d+)/$' => 'PhabricatorMetaMTAViewController',
'lists/$' => 'PhabricatorMetaMTAMailingListsController',
'lists/edit/(?:(?P<id>\d+)/)?$'
=> 'PhabricatorMetaMTAMailingListEditController',
),
'/login/' => array(
'$' => 'PhabricatorLoginController',
'email/$' => 'PhabricatorEmailLoginController',
'etoken/(?P<token>\w+)/$' => 'PhabricatorEmailTokenController',
),
'/logout/$' => 'PhabricatorLogoutController',
'/oauth/' => array(
'(?P<provider>github|facebook)/' => array(
'login/$' => 'PhabricatorOAuthLoginController',
'diagnose/$' => 'PhabricatorOAuthDiagnosticsController',
'unlink/$' => 'PhabricatorOAuthUnlinkController',
),
),
'/xhprof/' => array(
'profile/(?P<phid>[^/]+)/$' => 'PhabricatorXHProfProfileController',
),
'/~/' => 'DarkConsoleController',
'/settings/' => array(
'(?:page/(?P<page>[^/]+)/)?$' => 'PhabricatorUserSettingsController',
),
'/maniphest/' => array(
'$' => 'ManiphestTaskListController',
'view/(?P<view>\w+)/$' => 'ManiphestTaskListController',
'task/' => array(
'create/$' => 'ManiphestTaskEditController',
'edit/(?P<id>\d+)/$' => 'ManiphestTaskEditController',
),
'transaction/' => array(
'save/' => 'ManiphestTransactionSaveController',
),
'select/search/$' => 'ManiphestTaskSelectorSearchController',
),
'/T(?P<id>\d+)$' => 'ManiphestTaskDetailController',
'/github-post-receive/(?P<id>\d+)/(?P<token>[^/]+)/$'
=> 'PhabricatorRepositoryGitHubPostReceiveController',
'/repository/' => array(
'$' => 'PhabricatorRepositoryListController',
'create/$' => 'PhabricatorRepositoryCreateController',
'edit/(?P<id>\d+)/(?:(?P<view>\w+)?/)?$' =>
'PhabricatorRepositoryEditController',
'delete/(?P<id>\d+)/$' => 'PhabricatorRepositoryDeleteController',
),
'/search/' => array(
'$' => 'PhabricatorSearchController',
'(?P<id>\d+)/$' => 'PhabricatorSearchController',
),
'/project/' => array(
'$' => 'PhabricatorProjectListController',
'edit/(?:(?P<id>\d+)/)?$' => 'PhabricatorProjectEditController',
'view/(?P<id>\d+)/$' => 'PhabricatorProjectProfileController',
'affiliation/(?P<id>\d+)/$'
=> 'PhabricatorProjectAffiliationEditController',
),
'/r(?P<callsign>[A-Z]+)(?P<commit>[a-z0-9]+)$'
=> 'DiffusionCommitController',
'/diffusion/' => array(
'$' => 'DiffusionHomeController',
'(?P<callsign>[A-Z]+)/' => array(
'$' => 'DiffusionRepositoryController',
'change/'.
'(?P<path>.*?)'.
'(?:[;](?P<commit>[a-z0-9]+))?'.
'$'
=> 'DiffusionChangeController',
'history/'.
'(?P<path>.*?)'.
'(?:[;](?P<commit>[a-z0-9]+))?'.
'$'
=> 'DiffusionHistoryController',
'browse/'.
'(?P<path>.*?)'.
'(?:[;](?P<commit>[a-z0-9]+))?'.
'(?:[$](?P<line>\d+))?'.
'$'
=> 'DiffusionBrowseController',
),
),
'/daemon/' => array(
'log/' => array(
'(?P<id>\d+)/$' => 'PhabricatorDaemonLogViewController',
),
'timeline/$' => 'PhabricatorDaemonTimelineConsoleController',
'timeline/(?P<id>\d+)/$' => 'PhabricatorDaemonTimelineEventController',
'$' => 'PhabricatorDaemonConsoleController',
),
'/herald/' => array(
'$' => 'HeraldHomeController',
'view/(?P<view>[^/]+)/$' => 'HeraldHomeController',
'new/(?:(?P<type>[^/]+)/)?$' => 'HeraldNewController',
'rule/(?:(?<id>\d+)/)?$' => 'HeraldRuleController',
'delete/(?P<id>\d+)/$' => 'HeraldDeleteController',
'test/$' => 'HeraldTestConsoleController',
'transcript/$' => 'HeraldTranscriptListController',
- 'transcript/(?P<id>\d+)/$' => 'HeraldTranscriptController',
+ 'transcript/(?P<id>\d+)/(?:(?P<filter>\w+)/)?$'
+ => 'HeraldTranscriptController',
),
);
}
protected function getResourceURIMapRules() {
return array(
'/res/' => array(
'(?P<package>pkg/)?(?P<hash>[a-f0-9]{8})/(?P<path>.+\.(?:css|js))$'
=> 'CelerityResourceController',
),
);
}
public function buildRequest() {
$request = new AphrontRequest($this->getHost(), $this->getPath());
$request->setRequestData($_GET + $_POST);
$request->setApplicationConfiguration($this);
return $request;
}
public function handleException(Exception $ex) {
$class = phutil_escape_html(get_class($ex));
$message = phutil_escape_html($ex->getMessage());
$content =
'<div class="aphront-unhandled-exception">'.
'<h1>Unhandled Exception "'.$class.'": '.$message.'</h1>'.
'<code>'.phutil_escape_html((string)$ex).'</code>'.
'</div>';
$user = $this->getRequest()->getUser();
if (!$user) {
// If we hit an exception very early, we won't have a user.
$user = new PhabricatorUser();
}
$dialog = new AphrontDialogView();
$dialog
->setTitle('Exception!')
->setClass('aphront-exception-dialog')
->setUser($user)
->appendChild($content)
->addCancelButton('/');
$response = new AphrontDialogResponse();
$response->setDialog($dialog);
return $response;
}
public function willSendResponse(AphrontResponse $response) {
$request = $this->getRequest();
if ($response instanceof AphrontDialogResponse) {
if (!$request->isAjax()) {
$view = new PhabricatorStandardPageView();
$view->setRequest($request);
$view->appendChild(
'<div style="padding: 2em 0;">'.
$response->buildResponseString().
'</div>');
$response = new AphrontWebpageResponse();
$response->setContent($view->render());
return $response;
} else {
return id(new AphrontAjaxResponse())
->setContent(array(
'dialog' => $response->buildResponseString(),
));
}
} else if ($response instanceof AphrontRedirectResponse) {
if ($request->isAjax()) {
return id(new AphrontAjaxResponse())
->setContent(
array(
'redirect' => $response->getURI(),
));
}
} else if ($response instanceof Aphront404Response) {
$failure = new AphrontRequestFailureView();
$failure->setHeader('404 Not Found');
$failure->appendChild(
'<p>The page you requested was not found.</p>');
$view = new PhabricatorStandardPageView();
$view->setTitle('404 Not Found');
$view->setRequest($this->getRequest());
$view->appendChild($failure);
$response = new AphrontWebpageResponse();
$response->setContent($view->render());
$response->setHTTPResponseCode(404);
return $response;
}
return $response;
}
public function build404Controller() {
return array(new Phabricator404Controller($this->getRequest()), array());
}
}
diff --git a/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php b/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
index dcc6d4ea2a..b14f66fedd 100644
--- a/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
+++ b/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
@@ -1,725 +1,730 @@
<?php
/*
* Copyright 2011 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.
*/
class DifferentialRevisionViewController extends DifferentialController {
private $revisionID;
public function willProcessRequest(array $data) {
$this->revisionID = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$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);
$diffs = mpull($diffs, null, 'getID');
if (empty($diffs[$diff_vs])) {
$diff_vs = null;
}
list($changesets, $vs_map) =
$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(),
array(
$revision->getAuthorPHID(),
$user->getPHID(),
),
mpull($comments, 'getAuthorPHID'));
foreach ($revision->getAttached() as $type => $phids) {
foreach ($phids as $phid => $info) {
$object_phids[] = $phid;
}
}
$object_phids = array_unique($object_phids);
$handles = id(new PhabricatorObjectHandleData($object_phids))
->loadHandles();
$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. Only ".
"the first {$limit} files are shown. ".
"<strong>".
phutil_render_tag(
'a',
array(
'href' => $request_uri->alter('large', 'true'),
),
'Show All Files').
"</strong>");
$warning = $warning->render();
$visible_changesets = array_slice($changesets, 0, $limit, true);
} else {
$warning = null;
$visible_changesets = $changesets;
}
$revision_detail = new DifferentialRevisionDetailView();
$revision_detail->setRevision($revision);
$properties = $this->getRevisionProperties($revision, $target, $handles);
$revision_detail->setProperties($properties);
$actions = $this->getRevisionActions($revision);
$revision_detail->setActions($actions);
$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);
$diff_history = new DifferentialRevisionUpdateHistoryView();
$diff_history->setDiffs($diffs);
$diff_history->setSelectedVersusDiffID($diff_vs);
$diff_history->setSelectedDiffID($target->getID());
$toc_view = new DifferentialDiffTableOfContentsView();
$toc_view->setChangesets($changesets);
$changeset_view = new DifferentialChangesetListView();
$changeset_view->setChangesets($visible_changesets);
$changeset_view->setEditable(true);
$changeset_view->setRevision($revision);
$changeset_view->setVsMap($vs_map);
$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);
return $this->buildStandardPageResponse(
'<div class="differential-primary-pane">'.
$revision_detail->render().
$comment_view->render().
$diff_history->render().
$toc_view->render().
$warning.
$changeset_view->render().
$comment_form->render().
'</div>',
array(
'title' => $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 getRevisionProperties(
DifferentialRevision $revision,
DifferentialDiff $diff,
array $handles) {
$properties = array();
$status = $revision->getStatus();
$status = DifferentialRevisionStatus::getNameForRevisionStatus($status);
$properties['Revision Status'] = '<strong>'.$status.'</strong>';
$author = $handles[$revision->getAuthorPHID()];
$properties['Author'] = $author->renderLink();
$properties['Reviewers'] = $this->renderHandleLinkList(
array_select_keys(
$handles,
$revision->getReviewers()));
$properties['CCs'] = $this->renderHandleLinkList(
array_select_keys(
$handles,
$revision->getCCPHIDs()));
$host = $diff->getSourceMachine();
if ($host) {
$properties['Host'] = phutil_escape_html($host);
}
$path = $diff->getSourcePath();
if ($path) {
$branch = $diff->getBranch() ? ' ('.$diff->getBranch().')' : '';
$properties['Path'] = phutil_escape_html("{$path} {$branch}");
}
$lstar = DifferentialRevisionUpdateHistoryView::renderDiffLintStar($diff);
$lmsg = DifferentialRevisionUpdateHistoryView::getDiffLintMessage($diff);
$properties['Lint'] = $lstar.' '.$lmsg;
$ustar = DifferentialRevisionUpdateHistoryView::renderDiffUnitStar($diff);
$umsg = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage($diff);
$properties['Unit'] = $ustar.' '.$umsg;
$tasks = $revision->getAttachedPHIDs(
PhabricatorPHIDConstants::PHID_TYPE_TASK);
if ($tasks) {
$links = array();
foreach ($tasks as $task_phid) {
$links[] = $handles[$task_phid]->renderLink();
}
$properties['Maniphest Tasks'] = implode('<br />', $links);
}
return $properties;
}
private function getRevisionActions(DifferentialRevision $revision) {
$viewer_phid = $this->getRequest()->getUser()->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());
$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_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',
'sigil' => 'workflow',
);
} 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' => 'attach-maniphest',
'name' => 'Edit Maniphest Tasks',
'href' => "/differential/attach/{$revision_id}/TASK/",
'sigil' => 'workflow',
);
$links[] = array(
'class' => 'transcripts-metamta',
'name' => 'MetaMTA Transcripts',
'href' => "/mail/?phid={$revision_phid}",
);
return $links;
}
private function renderHandleLinkList(array $list) {
if (empty($list)) {
return '<em>None</em>';
}
return implode(', ', mpull($list, 'renderLink'));
}
private function getRevisionCommentActions(DifferentialRevision $revision) {
$actions = array(
DifferentialAction::ACTION_COMMENT => true,
);
$viewer_phid = $this->getRequest()->getUser()->getPHID();
$viewer_is_owner = ($viewer_phid == $revision->getAuthorPHID());
if ($viewer_is_owner) {
switch ($revision->getStatus()) {
case DifferentialRevisionStatus::NEEDS_REVIEW:
$actions[DifferentialAction::ACTION_ABANDON] = true;
break;
case DifferentialRevisionStatus::NEEDS_REVISION:
case DifferentialRevisionStatus::ACCEPTED:
$actions[DifferentialAction::ACTION_ABANDON] = true;
$actions[DifferentialAction::ACTION_REQUEST] = true;
break;
case DifferentialRevisionStatus::COMMITTED:
break;
case DifferentialRevisionStatus::ABANDONED:
$actions[DifferentialAction::ACTION_RECLAIM] = true;
break;
}
} else {
switch ($revision->getStatus()) {
case DifferentialRevisionStatus::NEEDS_REVIEW:
$actions[DifferentialAction::ACTION_ACCEPT] = true;
$actions[DifferentialAction::ACTION_REJECT] = true;
break;
case DifferentialRevisionStatus::NEEDS_REVISION:
$actions[DifferentialAction::ACTION_ACCEPT] = true;
break;
case DifferentialRevisionStatus::ACCEPTED:
$actions[DifferentialAction::ACTION_REJECT] = true;
break;
case DifferentialRevisionStatus::COMMITTED:
case DifferentialRevisionStatus::ABANDONED:
break;
}
}
$actions[DifferentialAction::ACTION_ADDREVIEWERS] = true;
return array_keys($actions);
}
private function loadInlineComments(array $comments, array &$changesets) {
$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, $target) {
$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');
$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();
unset($vs_changesets[$file]);
}
}
foreach ($vs_changesets as $changeset) {
$changesets[$changeset->getID()] = $changeset;
$vs_map[$changeset->getID()] = -1;
}
}
$changesets = msort($changesets, 'getSortKey');
return array($changesets, $vs_map);
}
}
/*
protected function getSandcastleURI(Diff $diff) {
$uri = $this->getDiffProperty($diff, 'facebook:sandcastle_uri');
if (!$uri) {
$uri = $diff->getSandboxURL();
}
return $uri;
}
protected function getDiffProperty(Diff $diff, $property, $default = null) {
$diff_id = $diff->getID();
if (empty($this->diffProperties[$diff_id])) {
$props = id(new DifferentialDiffProperty())
->loadAllWhere('diffID = %s', $diff_id);
$dict = array_pull($props, 'getData', 'getName');
$this->diffProperties[$diff_id] = $dict;
}
return idx($this->diffProperties[$diff_id], $property, $default);
}
$diff_table->appendChild(
<tr>
<td colspan="8" class="diff-differ-submit">
<label>Whitespace Changes:</label>
{id(<select name="whitespace" />)->setOptions(
array(
'ignore-all' => 'Ignore All',
'ignore-trailing' => 'Ignore Trailing',
'show-all' => 'Show All',
), $request->getStr('whitespace'))}{' '}
<button type="submit">Show Diff</button>
</td>
</tr>);
$load_ids = array_filter(array($old, $diff->getID()));
$viewer_id = $this->getRequest()->getViewerContext()->getUserID();
$raw_objects = queryfx_all(
smc_get_db('cdb.differential', 'r'),
'SELECT * FROM changeset WHERE changeset.diffID IN (%Ld)',
$load_ids);
$raw_objects = array_group($raw_objects, 'diffID');
$objects = $raw_objects[$diff->getID()];
if (!$objects) {
$changesets = array();
} else {
$changesets = id(new DifferentialChangeset())->loadAllFromArray($objects);
}
$feedback = id(new DifferentialFeedback())->loadAllWithRevision($revision);
$feedback = array_merge($implied_feedback, $feedback);
$inline_comments = $this->loadInlineComments($feedback, $changesets);
$diff_map = array();
$diffs = array_psort($diffs, 'getID');
foreach ($diffs as $diff) {
$diff_map[$diff->getID()] = count($diff_map) + 1;
}
$visible_changesets = array_fill_keys($visible_changesets, true);
$hidden_changesets = array();
foreach ($changesets as $changeset) {
$id = $changeset->getID();
if (isset($visible_changesets[$id])) {
continue;
}
$hidden_changesets[$id] = $diff_map[$changeset->getDiffID()];
}
$engine = new RemarkupEngine();
$engine->enableFeature(RemarkupEngine::FEATURE_GUESS_IMAGES);
$engine->enableFeature(RemarkupEngine::FEATURE_YOUTUBE);
$engine->setCurrentSandcastle($this->getSandcastleURI($target_diff));
$syntax_link =
<a href={'http://www.intern.facebook.com/intern/wiki/index.php' .
'/Articles/Remarkup_Syntax_Reference'}
target="_blank"
tabindex="4">Remarkup Reference</a>;
$notice = null;
if ($this->getRequest()->getBool('diff_changed')) {
$notice =
<tools:notice title="Revision Updated Recently">
This revision was updated with a <strong>new diff</strong> while you
were providing feedback. Your inline comments appear on the
<strong>old diff</strong>.
</tools:notice>;
}
$blast_uri = RedirectURI(
'/intern/differential/?action=blast&fbid='.$revision->getFBID())
->setTier('intern');
$links[] = array(
'blast',
<a href={$blast_uri}>Blast Revision</a>,
);
$engineering_repository_id = RepositoryRef::getByCallsign('E')->getID();
$svn_revision = $revision->getSVNRevision();
if ($status == DifferentialConstants::COMMITTED &&
$svn_revision &&
$revision->getRepositoryID() == $engineering_repository_id) {
$href = '/intern/push/request.php?rev='.$svn_revision;
$href = RedirectURI($href)->setTier('intern');
$links[] = array(
'merge',
<a href={$href} id="ask_for_merge_link">Ask for Merge</a>,
);
}
$links[] = array(
'herald-transcript',
<a href={"/herald/transcript/?fbid=".$revision->getFBID()}
>Herald Transcripts</a>,
);
}
protected function renderDiffPropertyMoreLink(Diff $diff, $name) {
$target = <div class="star-more"
style="display: none;">
<div class="star-loading">Loading...</div>
</div>;
$meta = array(
'target' => $target->requireUniqueID(),
'uri' => '/differential/diffprop/'.$diff->getID().'/'.$name.'/',
);
$more =
<span sigil="star-link-container">
&middot;
<a mustcapture="true"
sigil="star-more"
href="#"
meta={$meta}>Show Details</a>
</span>;
return <x:frag>{$more}{$target}</x:frag>;
}
protected function getRevisionStatusDisplay(DifferentialRevision $revision) {
$viewer_id = $this->getRequest()->getViewerContext()->getUserID();
$viewer_is_owner = ($viewer_id == $revision->getOwnerID());
$status = $revision->getStatus();
$more = null;
switch ($status) {
case DifferentialConstants::NEEDS_REVIEW:
$message = 'Pending Review';
break;
case DifferentialConstants::NEEDS_REVISION:
$message = 'Awaiting Revision';
if ($viewer_is_owner) {
$more = 'Make the requested changes and update the revision.';
}
break;
case DifferentialConstants::ACCEPTED:
$message = 'Ready for Commit';
if ($viewer_is_owner) {
$more =
<x:frag>
Run <tt>arc commit</tt> (svn) or <tt>arc amend</tt> (git) to
proceed.
</x:frag>;
}
break;
case DifferentialConstants::COMMITTED:
$message = 'Committed';
$ref = $revision->getRevisionRef();
$more = $ref
? (<a href={URI($ref->getDetailURL())}>
{$ref->getName()}
</a>)
: null;
$engineering_repository_id = RepositoryRef::getByCallsign('E')->getID();
if ($revision->getSVNRevision() &&
$revision->getRepositoryID() == $engineering_repository_id) {
Javelin::initBehavior(
'differential-revtracker-status',
array(
'uri' => '/differential/revtracker/'.$revision->getID().'/',
'statusId' => 'revtracker_status',
'mergeLinkId' => 'ask_for_merge_link',
));
}
break;
case DifferentialConstants::ABANDONED:
$message = 'Abandoned';
break;
default:
throw new Exception("Unknown revision status.");
}
if ($more) {
$message =
<x:frag>
<strong id="revtracker_status">{$message}</strong>
&middot; {$more}
</x:frag>;
} else {
$message = <strong id="revtracker_status">{$message}</strong>;
}
return $message;
}
}
protected function getDetailFields(
DifferentialRevision $revision,
Diff $diff,
array $handles) {
$sandcastle = $this->getSandcastleURI($diff);
if ($sandcastle) {
$fields['Sandcastle'] = <a href={$sandcastle}>{$sandcastle}</a>;
}
$blame_rev = $revision->getSvnBlameRevision();
if ($blame_rev) {
if ($revision->getRepositoryRef() && is_numeric($blame_rev)) {
$ref = new RevisionRef($revision->getRepositoryRef(), $blame_rev);
$fields['Blame Revision'] =
<a href={URI($ref->getDetailURL())}>
{$ref->getName()}
</a>;
} else {
$fields['Blame Revision'] = $blame_rev;
}
}
$bugzilla_id = $revision->getBugzillaID();
if ($bugzilla_id) {
$href = 'http://bugs.developers.facebook.com/show_bug.cgi?id='.
$bugzilla_id;
$fields['Bugzilla'] = <a href={$href}>{'#'.$bugzilla_id}</a>;
}
$fields['Apply Patch'] = <tt>arc patch --revision {$revision->getID()}</tt>;
if ($diff->getParentRevisionID()) {
$parent = id(new DifferentialRevision())->load(
$diff->getParentRevisionID());
if ($parent) {
$fields['Depends On'] =
<a href={$parent->getURI()}>
D{$parent->getID()}: {$parent->getName()}
</a>;
}
}
Javelin::initBehavior('differential-star-more');
if ($unit_details) {
$fields['Unit Tests'] =
<x:frag>
{$fields['Unit Tests']}
{$this->renderDiffPropertyMoreLink($diff, 'unit')}
</x:frag>;
}
$platform_impact = $revision->getPlatformImpact();
if ($platform_impact) {
$fields['Platform Impact'] =
<text linebreaks="true">{$platform_impact}</text>;
}
return $fields;
}
*/
diff --git a/src/applications/herald/adapter/differential/HeraldDifferentialRevisionAdapter.php b/src/applications/herald/adapter/differential/HeraldDifferentialRevisionAdapter.php
new file mode 100644
index 0000000000..484b0ec2dd
--- /dev/null
+++ b/src/applications/herald/adapter/differential/HeraldDifferentialRevisionAdapter.php
@@ -0,0 +1,257 @@
+<?php
+
+/*
+ * Copyright 2011 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.
+ */
+
+class HeraldDifferentialRevisionAdapter extends HeraldObjectAdapter {
+
+ protected $revision;
+ protected $changesets;
+ protected $diff = null;
+
+ protected $explicitCCs;
+ protected $explicitReviewers;
+ protected $forbiddenCCs;
+ protected $forbiddenReviewers;
+
+ protected $newCCs = array();
+ protected $remCCs = array();
+
+ public function __construct(DifferentialRevision $revision) {
+ $revision->loadRelationships();
+ $this->revision = $revision;
+ }
+
+ public function setDiff(Diff $diff) {
+ $this->diff = $diff;
+ return $this;
+ }
+
+ public function setExplicitCCs($explicit_ccs) {
+ $this->explicitCCs = $explicit_ccs;
+ return $this;
+ }
+
+ public function setExplicitReviewers($explicit_reviewers) {
+ $this->explicitReviewers = $explicit_reviewers;
+ return $this;
+ }
+
+ public function setForbiddenCCs($forbidden_ccs) {
+ $this->forbiddenCCs = $forbidden_ccs;
+ return $this;
+ }
+
+ public function setForbiddenReviewers($forbidden_reviewers) {
+ $this->forbiddenReviewers = $forbidden_reviewers;
+ return $this;
+ }
+
+ public function getCCsAddedByHerald() {
+ return array_diff_key($this->newCCs, $this->remCCs);
+ }
+
+ public function getCCsRemovedByHerald() {
+ return $this->remCCs;
+ }
+
+ public function getPHID() {
+ return $this->revision->getPHID();
+ }
+
+ public function getHeraldName() {
+ return $this->revision->getTitle();
+ }
+
+ public function getHeraldTypeName() {
+ return HeraldContentTypeConfig::CONTENT_TYPE_DIFFERENTIAL;
+ }
+
+ protected function loadChangesets() {
+ if ($this->changesets) {
+ return $this->changesets;
+ }
+ $diff = $this->loadDiff();
+ $changes = $diff->getChangesets();
+ return ($this->changesets = $changes);
+ }
+
+ protected function loadDiff() {
+ if ($this->diff === null) {
+ $this->diff = $this->revision->getActiveDiff();
+ }
+ return $this->diff;
+ }
+
+ protected function getContentDictionary() {
+ $changes = $this->loadChangesets();
+
+ $hunks = array();
+ if ($changes) {
+ $hunks = id(new DifferentialHunk())->loadAllwhere(
+ 'changesetID in (%Ld)',
+ mpull($changes, 'getID'));
+ }
+
+ $dict = array();
+ $hunks = mgroup($hunks, 'getChangesetID');
+ $changes = mpull($changes, null, 'getID');
+ foreach ($changes as $id => $change) {
+ $filename = $change->getFilename();
+ $content = array();
+ foreach (idx($hunks, $id, array()) as $hunk) {
+ $content[] = $hunk->makeChanges();
+ }
+ $dict[$filename] = implode("\n", $content);
+ }
+
+ return $dict;
+ }
+
+ public function getHeraldField($field) {
+ switch ($field) {
+ case HeraldFieldConfig::FIELD_TITLE:
+ return $this->revision->getTitle();
+ break;
+ case HeraldFieldConfig::FIELD_BODY:
+ return $this->revision->getSummary()."\n".
+ $this->revision->getTestPlan();
+ break;
+ case HeraldFieldConfig::FIELD_AUTHOR:
+ return $this->revision->getAuthorPHID();
+ break;
+ case HeraldFieldConfig::FIELD_DIFF_FILE:
+ $changes = $this->loadChangesets();
+ return array_values(mpull($changes, 'getFilename'));
+ case HeraldFieldConfig::FIELD_CC:
+ if (isset($this->explicitCCs)) {
+ return array_keys($this->explicitCCs);
+ } else {
+ return $this->revision->getCCPHIDs();
+ }
+ case HeraldFieldConfig::FIELD_REVIEWERS:
+ if (isset($this->explicitReviewers)) {
+ return array_keys($this->explicitReviewers);
+ } else {
+ return $this->revision->getReviewers();
+ }
+/* TODO
+ case HeraldFieldConfig::FIELD_REPOSITORY:
+ $id = $this->revision->getRepositoryID();
+ if (!$id) {
+ return null;
+ }
+ require_module_lazy('intern/repository');
+ $repository = RepositoryRef::getByID($id);
+ if (!$repository) {
+ return null;
+ }
+ return $repository->getFBID();
+*/
+ case HeraldFieldConfig::FIELD_DIFF_CONTENT:
+ return $this->getContentDictionary();
+/* TODO
+ case HeraldFieldConfig::FIELD_AFFECTED_PACKAGE:
+ return mpull(
+ DiffOwners::getPackages($this->loadDiff()),
+ 'getFBID');
+*/
+/* TODO
+ case HeraldFieldConfig::FIELD_AFFECTED_PACKAGE_OWNER:
+ return DiffOwners::getOwners($this->loadDiff());
+*/
+ default:
+ throw new Exception("Invalid field '{$field}'.");
+ }
+ }
+
+ public function applyHeraldEffects(array $effects) {
+ $result = array();
+ if ($this->explicitCCs) {
+ $effect = new HeraldEffect();
+ $effect->setAction(HeraldActionConfig::ACTION_ADD_CC);
+ $effect->setTarget(array_keys($this->explicitCCs));
+ $effect->setReason(
+ 'CCs provided explicitly by revision author or carried over from a '.
+ 'previous version of the revision.');
+ $result[] = new HeraldApplyTranscript(
+ $effect,
+ true,
+ 'Added addresses to CC list.');
+ }
+
+ $forbidden_ccs = array_fill_keys(
+ nonempty($this->forbiddenCCs, array()),
+ true);
+
+ foreach ($effects as $effect) {
+ $action = $effect->getAction();
+ switch ($action) {
+ case HeraldActionConfig::ACTION_NOTHING:
+ $result[] = new HeraldApplyTranscript(
+ $effect,
+ true,
+ 'OK, did nothing.');
+ break;
+ case HeraldActionConfig::ACTION_ADD_CC:
+ $base_target = $effect->getTarget();
+ $forbidden = array();
+ foreach ($base_target as $key => $fbid) {
+ if (isset($forbidden_ccs[$fbid])) {
+ $forbidden[] = $fbid;
+ unset($base_target[$key]);
+ } else {
+ $this->newCCs[$fbid] = true;
+ }
+ }
+
+ if ($forbidden) {
+ $failed = clone $effect;
+ $failed->setTarget($forbidden);
+ if ($base_target) {
+ $effect->setTarget($base_target);
+ $result[] = new HeraldApplyTranscript(
+ $effect,
+ true,
+ 'Added these addresses to CC list. Others could not be added.');
+ }
+ $result[] = new HeraldApplyTranscript(
+ $failed,
+ false,
+ 'CC forbidden, these addresses have unsubscribed.');
+ } else {
+ $result[] = new HeraldApplyTranscript(
+ $effect,
+ true,
+ 'Added addresses to CC list.');
+ }
+ break;
+ case HeraldActionConfig::ACTION_REMOVE_CC:
+ foreach ($effect->getTarget() as $fbid) {
+ $this->remCCs[$fbid] = true;
+ }
+ $result[] = new HeraldApplyTranscript(
+ $effect,
+ true,
+ 'Removed addresses from CC list.');
+ break;
+ default:
+ throw new Exception("No rules to handle action '{$action}'.");
+ }
+ }
+ return $result;
+ }
+}
diff --git a/src/applications/herald/adapter/differential/__init__.php b/src/applications/herald/adapter/differential/__init__.php
new file mode 100644
index 0000000000..432d8502fd
--- /dev/null
+++ b/src/applications/herald/adapter/differential/__init__.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'applications/differential/storage/hunk');
+phutil_require_module('phabricator', 'applications/herald/adapter/base');
+phutil_require_module('phabricator', 'applications/herald/config/action');
+phutil_require_module('phabricator', 'applications/herald/config/contenttype');
+phutil_require_module('phabricator', 'applications/herald/config/field');
+phutil_require_module('phabricator', 'applications/herald/engine/effect');
+phutil_require_module('phabricator', 'applications/herald/storage/transcript/apply');
+
+phutil_require_module('phutil', 'utils');
+
+
+phutil_require_source('HeraldDifferentialRevisionAdapter.php');
diff --git a/src/applications/herald/controller/rule/HeraldRuleController.php b/src/applications/herald/controller/rule/HeraldRuleController.php
index b53d91deb6..1ddfe5b9e4 100644
--- a/src/applications/herald/controller/rule/HeraldRuleController.php
+++ b/src/applications/herald/controller/rule/HeraldRuleController.php
@@ -1,457 +1,457 @@
<?php
/*
* Copyright 2011 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.
*/
class HeraldRuleController extends HeraldController {
private $id;
public function willProcessRequest(array $data) {
$this->id = (int)idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$content_type_map = HeraldContentTypeConfig::getContentTypeMap();
if ($this->id) {
$rule = id(new HeraldRule())->load($this->id);
if (!$rule) {
return new Aphront404Response();
}
if ($rule->getAuthorPHID() != $user->getPHID()) {
throw new Exception("You don't own this rule and can't edit it.");
}
} else {
$rule = new HeraldRule();
$rule->setAuthorPHID($user->getPHID());
$rule->setMustMatchAll(true);
$type = $request->getStr('type');
if (!isset($content_type_map[$type])) {
$type = HeraldContentTypeConfig::CONTENT_TYPE_DIFFERENTIAL;
}
$rule->setContentType($type);
}
$local_version = id(new HeraldRule())->getConfigVersion();
if ($rule->getConfigVersion() > $local_version) {
throw new Exception(
"This rule was created with a newer version of Herald. You can not ".
"view or edit it in this older version. Try dev or wait for a push.");
}
// Upgrade rule version to our version, since we might add newly-defined
// conditions, etc.
$rule->setConfigVersion($local_version);
$rule_conditions = $rule->loadConditions();
$rule_actions = $rule->loadActions();
$rule->attachConditions($rule_conditions);
$rule->attachActions($rule_actions);
$e_name = true;
$errors = array();
if ($request->isFormPost() && $request->getStr('save')) {
$rule->setName($request->getStr('name'));
$rule->setMustMatchAll(($request->getStr('must_match') == 'all'));
if (!strlen($rule->getName())) {
$e_name = "Required";
$errors[] = "Rule must have a name.";
}
$data = json_decode($request->getStr('rule'), true);
if (!is_array($data) ||
!$data['conditions'] ||
!$data['actions']) {
throw new Exception("Failed to decode rule data.");
}
$conditions = array();
foreach ($data['conditions'] as $condition) {
$obj = new HeraldCondition();
$obj->setFieldName($condition[0]);
$obj->setFieldCondition($condition[1]);
if (is_array($condition[2])) {
$obj->setValue(array_keys($condition[2]));
} else {
$obj->setValue($condition[2]);
}
$cond_type = $obj->getFieldCondition();
if ($cond_type == HeraldConditionConfig::CONDITION_REGEXP) {
if (@preg_match($obj->getValue(), '') === false) {
$errors[] =
'The regular expression "'.$obj->getValue().'" is not valid. '.
'Regular expressions must have enclosing characters (e.g. '.
'"@/path/to/file@", not "/path/to/file") and be syntactically '.
'correct.';
}
}
if ($cond_type == HeraldConditionConfig::CONDITION_REGEXP_PAIR) {
$json = json_decode($obj->getValue(), true);
if (!is_array($json)) {
$errors[] =
'The regular expression pair "'.$obj->getValue().'" is not '.
'valid JSON. Enter a valid JSON array with two elements.';
} else {
if (count($json) != 2) {
$errors[] =
'The regular expression pair "'.$obj->getValue().'" must have '.
'exactly two elements.';
} else {
$key_regexp = array_shift($json);
$val_regexp = array_shift($json);
if (@preg_match($key_regexp, '') === false) {
$errors[] =
'The first regexp, "'.$key_regexp.'" in the regexp pair '.
'is not a valid regexp.';
}
if (@preg_match($val_regexp, '') === false) {
$errors[] =
'The second regexp, "'.$val_regexp.'" in the regexp pair '.
'is not a valid regexp.';
}
}
}
}
$conditions[] = $obj;
}
$actions = array();
foreach ($data['actions'] as $action) {
$obj = new HeraldAction();
$obj->setAction($action[0]);
if (!isset($action[1])) {
// Legitimate for any action which doesn't need a target, like
// "Do nothing".
$action[1] = null;
}
if (is_array($action[1])) {
$obj->setTarget(array_keys($action[1]));
} else {
$obj->setTarget($action[1]);
}
$actions[] = $obj;
}
$rule->attachConditions($conditions);
$rule->attachActions($actions);
if (!$errors) {
try {
// TODO
// $rule->openTransaction();
$rule->save();
$rule->saveConditions($conditions);
$rule->saveActions($actions);
// $rule->saveTransaction();
$uri = '/herald/view/'.$rule->getContentType().'/';
return id(new AphrontRedirectResponse())
->setURI($uri);
} catch (AphrontQueryDuplicateKeyException $ex) {
$e_name = "Not Unique";
$errors[] = "Rule name is not unique. Choose a unique name.";
}
}
}
$phids = array();
$phids[] = $rule->getAuthorPHID();
foreach ($rule->getActions() as $action) {
foreach ($action->getTarget() as $target) {
$target = (array)$target;
foreach ($target as $phid) {
$phids[] = $phid;
}
}
}
foreach ($rule->getConditions() as $condition) {
$value = $condition->getValue();
if (is_array($value)) {
foreach ($value as $phid) {
$phids[] = $phid;
}
}
}
$handles = id(new PhabricatorObjectHandleData($phids))
->loadHandles();
if ($errors) {
$error_view = new AphrontErrorView();
$error_view->setTitle('Form Errors');
$error_view->setErrors($errors);
} else {
$error_view = null;
}
$options = array(
'all' => 'all of',
'any' => 'any of',
);
$selected = $rule->getMustMatchAll() ? 'all' : 'any';
$must_match = array();
foreach ($options as $key => $option) {
$must_match[] = phutil_render_tag(
'option',
array(
'selected' => ($selected == $key) ? 'selected' : null,
'value' => $key,
),
phutil_escape_html($option));
}
$must_match =
'<select name="must_match">'.
implode("\n", $must_match).
'</select>';
if ($rule->getID()) {
$action = '/herald/rule/'.$rule->getID().'/';
} else {
$action = '/herald/rule/'.$rule->getID().'/';
}
require_celerity_resource('herald-css');
$type_name = $content_type_map[$rule->getContentType()];
$form = id(new AphrontFormView())
->setUser($user)
->setID('herald-rule-edit-form')
->addHiddenInput('type', $rule->getContentType())
->addHiddenInput('save', 1)
->appendChild(
// Build this explicitly so we can add a sigil to it.
javelin_render_tag(
'input',
array(
'type' => 'hidden',
'name' => 'rule',
'sigil' => 'rule',
)))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Rule Name')
->setName('name')
->setError($e_name)
->setValue($rule->getName()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Author')
->setValue($handles[$rule->getAuthorPHID()]->getName()))
->appendChild(
id(new AphrontFormMarkupControl())
->setValue(
"This rule triggers for <strong>{$type_name}</strong>."))
->appendChild(
'<h1>Conditions</h1>'.
'<div class="aphront-form-inset">'.
'<div style="float: right;">'.
javelin_render_tag(
'a',
array(
'href' => '#',
'class' => 'button green',
'sigil' => 'create-condition',
'mustcapture' => true,
),
'Create New Condition').
'</div>'.
'<p>When '.$must_match.' these conditions are met:</p>'.
'<div style="clear: both;"></div>'.
javelin_render_tag(
'table',
array(
'sigil' => 'rule-conditions',
'class' => 'herald-condition-table',
),
'').
'</div>')
->appendChild(
'<h1>Action</h1>'.
'<div class="aphront-form-inset">'.
'<div style="float: right;">'.
javelin_render_tag(
'a',
array(
'href' => '#',
'class' => 'button green',
'sigil' => 'create-action',
'mustcapture' => true,
),
'Create New Action').
'</div>'.
'<p>Take these actions:</p>'.
'<div style="clear: both;"></div>'.
javelin_render_tag(
'table',
array(
'sigil' => 'rule-actions',
'class' => 'herald-action-table',
),
'').
'</div>')
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Save Rule')
->addCancelButton('/herald/view/'.$rule->getContentType().'/'));
$serial_conditions = array(
array('default', 'default', ''),
);
if ($rule->getConditions()) {
$serial_conditions = array();
foreach ($rule->getConditions() as $condition) {
$value = $condition->getValue();
if (is_array($value)) {
$value_map = array();
foreach ($value as $k => $fbid) {
$value_map[$fbid] = $handles[$fbid]->getName();
}
$value = $value_map;
}
$serial_conditions[] = array(
$condition->getFieldName(),
$condition->getFieldCondition(),
$value,
);
}
}
$serial_actions = array(
array('default', ''),
);
if ($rule->getActions()) {
$serial_actions = array();
foreach ($rule->getActions() as $action) {
$target_map = array();
foreach ((array)$action->getTarget() as $fbid) {
$target_map[$fbid] = $handles[$fbid]->getName();
}
$serial_actions[] = array(
$action->getAction(),
$target_map,
);
}
}
$all_rules = id(new HeraldRule())->loadAllWhere(
'authorPHID = %d AND contentType = %s',
$rule->getAuthorPHID(),
$rule->getContentType());
$all_rules = mpull($all_rules, 'getName', 'getID');
asort($all_rules);
unset($all_rules[$rule->getID()]);
$config_info = array();
$config_info['fields']
= HeraldFieldConfig::getFieldMapForContentType($rule->getContentType());
$config_info['conditions'] = HeraldConditionConfig::getConditionMap();
foreach ($config_info['fields'] as $field => $name) {
$config_info['conditionMap'][$field] = array_keys(
HeraldConditionConfig::getConditionMapForField($field));
}
foreach ($config_info['fields'] as $field => $fname) {
foreach ($config_info['conditions'] as $condition => $cname) {
$config_info['values'][$field][$condition] =
HeraldValueTypeConfig::getValueTypeForFieldAndCondition(
$field,
$condition);
}
}
$config_info['actions'] =
HeraldActionConfig::getActionMapForContentType($rule->getContentType());
foreach ($config_info['actions'] as $action => $name) {
$config_info['targets'][$action] =
HeraldValueTypeConfig::getValueTypeForAction($action);
}
Javelin::initBehavior(
'herald-rule-editor',
array(
'root' => 'herald-rule-edit-form',
'conditions' => (object) $serial_conditions,
'actions' => (object) $serial_actions,
'template' => $this->buildTokenizerTemplates() + array(
'rules' => $all_rules,
),
'info' => $config_info,
));
$panel = new AphrontPanelView();
$panel->setHeader('Edit Herald Rule');
$panel->setWidth(AphrontPanelView::WIDTH_WIDE);
$panel->appendChild($form);
return $this->buildStandardPageResponse(
array(
$error_view,
$panel,
),
array(
'title' => 'Edit Rule',
));
}
protected function buildTokenizerTemplates() {
$template = new AphrontTokenizerTemplateView();
$template = $template->render();
return array(
'source' => array(
'email' => '/typeahead/common/mailable/',
- 'user' => '/typeahead/common/user/',
+ 'user' => '/typeahead/common/users/',
'repository' => '/typeahead/common/repository/',
/*
'tag' => '/datasource/tag/',
'package' => '/datasource/package/',
*/
),
'markup' => $template,
);
}
}
diff --git a/src/applications/herald/controller/test/__init__.php b/src/applications/herald/controller/test/__init__.php
index 79ae6a414b..72d995cbaf 100644
--- a/src/applications/herald/controller/test/__init__.php
+++ b/src/applications/herald/controller/test/__init__.php
@@ -1,25 +1,26 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/differential/storage/revision');
+phutil_require_module('phabricator', 'applications/herald/adapter/differential');
phutil_require_module('phabricator', 'applications/herald/adapter/dryrun');
phutil_require_module('phabricator', 'applications/herald/controller/base');
phutil_require_module('phabricator', 'applications/herald/engine/engine');
phutil_require_module('phabricator', 'applications/herald/storage/rule');
phutil_require_module('phabricator', 'applications/repository/storage/commit');
phutil_require_module('phabricator', 'applications/repository/storage/repository');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/form/error');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phutil', 'utils');
phutil_require_source('HeraldTestConsoleController.php');
diff --git a/src/applications/herald/controller/transcript/HeraldTranscriptController.php b/src/applications/herald/controller/transcript/HeraldTranscriptController.php
new file mode 100644
index 0000000000..aac094d05f
--- /dev/null
+++ b/src/applications/herald/controller/transcript/HeraldTranscriptController.php
@@ -0,0 +1,543 @@
+<?php
+
+/*
+ * Copyright 2011 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.
+ */
+
+class HeraldTranscriptController extends HeraldController {
+
+ const FILTER_AFFECTED = 'affected';
+ const FILTER_OWNED = 'owned';
+ const FILTER_ALL = 'all';
+
+ private $id;
+ private $filter;
+ private $handles;
+
+ public function willProcessRequest(array $data) {
+ $this->id = $data['id'];
+ $map = $this->getFilterMap();
+ $this->filter = idx($data, 'filter');
+ if (empty($map[$this->filter])) {
+ $this->filter = self::FILTER_AFFECTED;
+ }
+ }
+
+ public function processRequest() {
+
+ $xscript = id(new HeraldTranscript())->load($this->id);
+ if (!$xscript) {
+ throw new Exception('Uknown transcript!');
+ }
+
+ $field_names = HeraldFieldConfig::getFieldMap();
+ $condition_names = HeraldConditionConfig::getConditionMap();
+ $action_names = HeraldActionConfig::getActionMap();
+
+ require_celerity_resource('herald-test-css');
+
+ $filter = $this->getFilterPHIDs();
+ $this->filterTranscript($xscript, $filter);
+ $phids = array_merge($filter, $this->getTranscriptPHIDs($xscript));
+ $phids = array_unique($phids);
+ $phids = array_filter($phids);
+
+ $handles = id(new PhabricatorObjectHandleData($phids))
+ ->loadHandles();
+ $this->handles = $handles;
+
+ $object_xscript = $xscript->getObjectTranscript();
+
+ $nav = $this->buildSideNav();
+
+ $apply_xscript_panel = $this->buildApplyTranscriptPanel(
+ $xscript);
+ $nav->appendChild($apply_xscript_panel);
+
+ $action_xscript_panel = $this->buildActionTranscriptPanel(
+ $xscript);
+ $nav->appendChild($action_xscript_panel);
+
+ $object_xscript_panel = $this->buildObjectTranscriptPanel(
+ $xscript);
+ $nav->appendChild($object_xscript_panel);
+
+/*
+
+
+ $notice = null;
+ if ($xscript->getDryRun()) {
+ $notice =
+ <tools:notice title="Dry Run">
+ This was a dry run to test Herald rules, no actions were executed.
+ </tools:notice>;
+ }
+
+ if (!$object_xscript) {
+ $notice =
+ <x:frag>
+ <tools:notice title="Old Transcript">
+ Details of this transcript have been discarded. Full transcripts
+ are retained for 30 days.
+ </tools:notice>
+ {$notice}
+ </x:frag>;
+ }
+
+
+ return
+ <herald:standard-page title="Transcript">
+ <div style="padding: 1em;">
+ <tools:side-nav items={$this->renderNavItems()}>
+ {$notice}
+ {$apply_xscript_markup}
+ {$rule_table}
+ {$object_xscript_table}
+ </tools:side-nav>
+ </div>
+ </herald:standard-page>;
+*/
+
+ return $this->buildStandardPageResponse(
+ $nav,
+ array(
+ 'title' => 'Transcript',
+ ));
+ }
+
+ protected function renderConditionTestValue($condition, $handles) {
+ $value = $condition->getTestValue();
+ if (!is_scalar($value) && $value !== null) {
+ foreach ($value as $key => $phid) {
+ $handle = idx($handles, $phid);
+ if ($handle) {
+ $value[$key] = $handle->getName();
+ } else {
+ // This shouldn't ever really happen as we are supposed to have
+ // grabbed handles for everything, but be super liberal in what
+ // we accept here since we expect all sorts of weird issues as we
+ // version the system.
+ $value[$key] = 'Unknown Object #'.$phid;
+ }
+ }
+ sort($value);
+ $value = implode(', ', $value);
+ }
+
+ return
+ '<span class="condition-test-value">'.
+ phutil_escape_html($value).
+ '</span>';
+ }
+
+ private function buildSideNav() {
+ $nav = new AphrontSideNavView();
+
+ $items = array();
+ $filters = $this->getFilterMap();
+ foreach ($filters as $key => $name) {
+ $nav->addNavItem(
+ phutil_render_tag(
+ 'a',
+ array(
+ 'href' => '/herald/transcript/'.$this->id.'/'.$key.'/',
+ 'class' =>
+ ($key == $this->filter)
+ ? 'aphront-side-nav-selected'
+ : null,
+ ),
+ phutil_escape_html($name)));
+ }
+
+ return $nav;
+ }
+
+ protected function getFilterMap() {
+ return array(
+ self::FILTER_AFFECTED => 'Rules that Affected Me',
+ self::FILTER_OWNED => 'Rules I Own',
+ self::FILTER_ALL => 'All Rules',
+ );
+ }
+
+
+ protected function getFilterPHIDs() {
+ return array($this->getRequest()->getUser()->getPHID());
+
+/* TODO
+ $viewer_id = $this->getRequest()->getUser()->getPHID();
+
+ $fbids = array();
+ if ($this->filter == self::FILTER_AFFECTED) {
+ $fbids[] = $viewer_id;
+ require_module_lazy('intern/subscriptions');
+ $datastore = new SubscriberDatabaseStore();
+ $lists = $datastore->getUserMailmanLists($viewer_id);
+ foreach ($lists as $list) {
+ $fbids[] = $list;
+ }
+ }
+ return $fbids;
+*/
+ }
+
+ protected function getTranscriptPHIDs($xscript) {
+ $phids = array();
+
+ $object_xscript = $xscript->getObjectTranscript();
+ if (!$object_xscript) {
+ return array();
+ }
+
+ $phids[] = $object_xscript->getPHID();
+
+ foreach ($xscript->getApplyTranscripts() as $apply_xscript) {
+ // TODO: This is total hacks. Add another amazing layer of abstraction.
+ $target = (array)$apply_xscript->getTarget();
+ foreach ($target as $phid) {
+ if ($phid) {
+ $phids[] = $phid;
+ }
+ }
+ }
+
+ foreach ($xscript->getRuleTranscripts() as $rule_xscript) {
+ $phids[] = $rule_xscript->getRuleOwner();
+ }
+
+ $condition_xscripts = $xscript->getConditionTranscripts();
+ if ($condition_xscripts) {
+ $condition_xscripts = call_user_func_array(
+ 'array_merge',
+ $condition_xscripts);
+ }
+ foreach ($condition_xscripts as $condition_xscript) {
+ $value = $condition_xscript->getTestValue();
+ // TODO: Also total hacks.
+ if (is_array($value)) {
+ foreach ($value as $phid) {
+ if ($phid) { // TODO: Probably need to make sure this "looks like" a
+ // PHID or decrease the level of hacks here; this used
+ // to be an is_numeric() check in Facebook land.
+ $phids[] = $phid;
+ }
+ }
+ }
+ }
+
+ return $phids;
+ }
+
+ protected function filterTranscript($xscript, $filter_phids) {
+ $filter_owned = ($this->filter == self::FILTER_OWNED);
+ $filter_affected = ($this->filter == self::FILTER_AFFECTED);
+
+ if (!$filter_owned && !$filter_affected) {
+ // No filtering to be done.
+ return;
+ }
+
+ if (!$xscript->getObjectTranscript()) {
+ return;
+ }
+
+ $user_phid = $this->getRequest()->getUser()->getPHID();
+
+ $keep_apply_xscripts = array();
+ $keep_rule_xscripts = array();
+
+ $filter_phids = array_fill_keys($filter_phids, true);
+
+ $rule_xscripts = $xscript->getRuleTranscripts();
+ foreach ($xscript->getApplyTranscripts() as $id => $apply_xscript) {
+ $rule_id = $apply_xscript->getRuleID();
+ if ($filter_owned) {
+ if (!$rule_xscripts[$rule_id]) {
+ // No associated rule so you can't own this effect.
+ continue;
+ }
+ if ($rule_xscripts[$rule_id]->getRuleOwner() != $user_phid) {
+ continue;
+ }
+ } else if ($filter_affected) {
+ $targets = (array)$apply_xscript->getTarget();
+ if (!array_select_keys($filter_phids, $targets)) {
+ continue;
+ }
+ }
+ $keep_apply_xscripts[$id] = true;
+ if ($rule_id) {
+ $keep_rule_xscripts[$rule_id] = true;
+ }
+ }
+
+ foreach ($rule_xscripts as $rule_id => $rule_xscript) {
+ if ($filter_owned && $rule_xscript->getRuleOwner() == $user_phid) {
+ $keep_rule_xscripts[$rule_id] = true;
+ }
+ }
+
+ $xscript->setRuleTranscripts(
+ array_intersect_key(
+ $xscript->getRuleTranscripts(),
+ $keep_rule_xscripts));
+
+ $xscript->setApplyTranscripts(
+ array_intersect_key(
+ $xscript->getApplyTranscripts(),
+ $keep_apply_xscripts));
+
+ $xscript->setConditionTranscripts(
+ array_intersect_key(
+ $xscript->getConditionTranscripts(),
+ $keep_rule_xscripts));
+ }
+
+ private function buildApplyTranscriptPanel($xscript) {
+ $handles = $this->handles;
+
+ $action_names = HeraldActionConfig::getActionMap();
+
+ $rows = array();
+ foreach ($xscript->getApplyTranscripts() as $apply_xscript) {
+ // TODO: Hacks, this is an approximate guess at the target type.
+ $target = (array)$apply_xscript->getTarget();
+ if (!$target) {
+ if ($apply_xscript->getAction() == HeraldActionConfig::ACTION_NOTHING) {
+ $target = '';
+ } else {
+ $target = '<empty>';
+ }
+ } else {
+ foreach ($target as $k => $phid) {
+ $target[$k] = $handles[$phid]->getName();
+ }
+ $target = implode("\n", $target);
+ }
+ $target = phutil_escape_html($target);
+
+ if ($apply_xscript->getApplied()) {
+ $outcome = '<span class="outcome-success">SUCCESS</span>';
+ } else {
+ $outcome = '<span class="outcome-failure">FAILURE</span>';
+ }
+ $outcome .= ' '.phutil_escape_html($apply_xscript->getAppliedReason());
+
+ $rows[] = array(
+ phutil_escape_html($action_names[$apply_xscript->getAction()]),
+ $target,
+ '<strong>Taken because:</strong> '.
+ phutil_escape_html($apply_xscript->getReason()).
+ '<br />'.
+ '<strong>Outcome:</strong> '.$outcome,
+ );
+ }
+
+ $table = new AphrontTableView($rows);
+ $table->setNoDataString('No actions were taken.');
+ $table->setHeaders(
+ array(
+ 'Action',
+ 'Target',
+ 'Details',
+ ));
+ $table->setColumnClasses(
+ array(
+ '',
+ '',
+ 'wide',
+ ));
+
+ $panel = new AphrontPanelView();
+ $panel->setHeader('Actions Taken');
+ $panel->appendChild($table);
+
+ return $panel;
+ }
+
+ private function buildActionTranscriptPanel($xscript) {
+ $action_xscript = mgroup($xscript->getApplyTranscripts(), 'getRuleID');
+
+ $field_names = HeraldFieldConfig::getFieldMap();
+ $condition_names = HeraldConditionConfig::getConditionMap();
+ $action_names = HeraldActionConfig::getActionMap();
+
+ $handles = $this->handles;
+
+ $rule_markup = array();
+ foreach ($xscript->getRuleTranscripts() as $rule_id => $rule) {
+ $cond_markup = array();
+ foreach ($xscript->getConditionTranscriptsForRule($rule_id) as $cond) {
+ if ($cond->getNote()) {
+ $note =
+ '<div class="herald-condition-note">'.
+ phutil_escape_html($cond->getNote()).
+ '</div>';
+ } else {
+ $note = null;
+ }
+
+ if ($cond->getResult()) {
+ $result =
+ '<span class="herald-outcome condition-pass">'.
+ "\xE2\x9C\x93".
+ '</span>';
+ } else {
+ $result =
+ '<span class="herald-outcome condition-fail">'.
+ "\xE2\x9C\x98".
+ '</span>';
+ }
+
+ $cond_markup[] =
+ '<li>'.
+ $result.' Condition: '.
+ phutil_escape_html($field_names[$cond->getFieldName()]).
+ ' '.
+ phutil_escape_html($condition_names[$cond->getCondition()]).
+ ' '.
+ $this->renderConditionTestValue($cond, $handles).
+ $note.
+ '</li>';
+ }
+
+ if ($rule->getResult()) {
+ $result = '<span class="herald-outcome rule-pass">PASS</span>';
+ $class = 'herald-rule-pass';
+ } else {
+ $result = '<span class="herald-outcome rule-fail">FAIL</span>';
+ $class = 'herald-rule-fail';
+ }
+
+ $cond_markup[] =
+ '<li>'.$result.' '.phutil_escape_html($rule->getReason()).'</li>';
+
+/*
+ if ($rule->getResult()) {
+ $actions = idx($action_xscript, $rule_id, array());
+ if ($actions) {
+ $cond_markup[] = <li><div class="action-header">Actions</div></li>;
+ foreach ($actions as $action) {
+
+ $target = $action->getTarget();
+ if ($target) {
+ foreach ((array)$target as $k => $phid) {
+ $target[$k] = $handles[$phid]->getName();
+ }
+ $target = <strong>: {implode(', ', $target)}</strong>;
+ }
+
+ $cond_markup[] =
+ <li>
+ {$action_names[$action->getAction()]}
+ {$target}
+ </li>;
+ }
+ }
+ }
+*/
+ $user_phid = $this->getRequest()->getUser()->getPHID();
+
+ $name = $rule->getRuleName();
+ if ($rule->getRuleOwner() == $user_phid) {
+// $name = <a href={"/herald/rule/".$rule->getRuleID()."/"}>{$name}</a>;
+ }
+
+ $rule_markup[] =
+ phutil_render_tag(
+ 'li',
+ array(
+ 'class' => $class,
+ ),
+ '<div class="rule-name">'.
+ '<strong>'.phutil_escape_html($name).'</strong> '.
+ phutil_escape_html($handles[$rule->getRuleOwner()]->getName()).
+ '</div>'.
+ '<ul>'.implode("\n", $cond_markup).'</ul>');
+ }
+
+ $panel = new AphrontPanelView();
+ $panel->setHeader('Rule Details');
+ $panel->appendChild(
+ '<ul class="herald-explain-list">'.
+ implode("\n", $rule_markup).
+ '</ul>');
+
+ return $panel;
+ }
+
+ private function buildObjectTranscriptPanel($xscript) {
+
+ $field_names = HeraldFieldConfig::getFieldMap();
+
+ $object_xscript = $xscript->getObjectTranscript();
+
+ $data = array();
+ if ($object_xscript) {
+ $data += array(
+ 'Object Name' => $object_xscript->getName(),
+ 'Object Type' => $object_xscript->getType(),
+ 'Object PHID' => $object_xscript->getPHID(),
+ );
+ }
+
+ $data += $xscript->getMetadataMap();
+
+ if ($object_xscript) {
+ foreach ($object_xscript->getFields() as $field => $value) {
+ $field = idx($field_names, $field, '['.$field.'?]');
+ $data['Field: '.$field] = $value;
+ }
+ }
+
+ $rows = array();
+ foreach ($data as $name => $value) {
+ if (!is_scalar($value) && !is_null($value)) {
+ $value = implode("\n", $value);
+ }
+
+ if (strlen($value) > 256) {
+ $value = phutil_render_tag(
+ 'textarea',
+ array(
+ 'class' => 'herald-field-value-transcript',
+ ),
+ phutil_escape_html($value));
+ } else {
+ $value = phutil_escape_html($value);
+ }
+
+ $rows[] = array(
+ phutil_escape_html($name),
+ $value,
+ );
+ }
+
+ $table = new AphrontTableView($rows);
+ $table->setColumnClasses(
+ array(
+ 'header',
+ 'wide',
+ ));
+
+ $panel = new AphrontPanelView();
+ $panel->setHeader('Object Transcript');
+ $panel->appendChild($table);
+
+ return $panel;
+ }
+
+
+}
diff --git a/src/applications/herald/controller/transcript/__init__.php b/src/applications/herald/controller/transcript/__init__.php
new file mode 100644
index 0000000000..81bfe3385f
--- /dev/null
+++ b/src/applications/herald/controller/transcript/__init__.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'applications/herald/config/action');
+phutil_require_module('phabricator', 'applications/herald/config/condition');
+phutil_require_module('phabricator', 'applications/herald/config/field');
+phutil_require_module('phabricator', 'applications/herald/controller/base');
+phutil_require_module('phabricator', 'applications/herald/storage/transcript/base');
+phutil_require_module('phabricator', 'applications/phid/handle/data');
+phutil_require_module('phabricator', 'infrastructure/celerity/api');
+phutil_require_module('phabricator', 'view/control/table');
+phutil_require_module('phabricator', 'view/layout/panel');
+phutil_require_module('phabricator', 'view/layout/sidenav');
+
+phutil_require_module('phutil', 'markup');
+phutil_require_module('phutil', 'utils');
+
+
+phutil_require_source('HeraldTranscriptController.php');
diff --git a/src/applications/herald/controller/transcriptlist/HeraldTranscriptListController.php b/src/applications/herald/controller/transcriptlist/HeraldTranscriptListController.php
new file mode 100644
index 0000000000..bf409bb307
--- /dev/null
+++ b/src/applications/herald/controller/transcriptlist/HeraldTranscriptListController.php
@@ -0,0 +1,120 @@
+<?php
+
+/*
+ * Copyright 2011 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.
+ */
+
+class HeraldTranscriptListController extends HeraldController {
+
+ public function processRequest() {
+
+ $request = $this->getRequest();
+
+ // Pull these objects manually since the serialized fields are gigantic.
+ $transcript = new HeraldTranscript();
+ $data = queryfx_all(
+ $transcript->establishConnection('r'),
+ 'SELECT id, objectPHID, time, duration, dryRun FROM %T
+ ORDER BY id DESC
+ LIMIT 100',
+ $transcript->getTableName());
+
+ /*
+
+ $conn_r = smc_get_db('cdb.herald', 'r');
+
+ $page_size = 100;
+
+ $pager = new SimplePager();
+ $pager->setPageSize($page_size);
+ $pager->setOffset((((int)$request->getInt('page')) - 1) * $page_size);
+ $pager->order('id', array('id'));
+
+
+ $fbid = $request->getInt('fbid');
+ if ($fbid) {
+ $filter = qsprintf(
+ $conn_r,
+ 'WHERE objectID = %d',
+ $fbid);
+ } else {
+ $filter = '';
+ }
+
+ $data = $pager->select(
+ $conn_r,
+ 'id, objectID, time, duration, dryRun FROM transcript %Q',
+ $filter);
+*/
+
+ $handles = array();
+ if ($data) {
+ $phids = ipull($data, 'objectPHID', 'objectPHID');
+ $handles = id(new PhabricatorObjectHandleData($phids))
+ ->loadHandles();
+ }
+
+ $rows = array();
+ foreach ($data as $xscript) {
+ $rows[] = array(
+ date('F jS', $xscript['time']),
+ date('g:i:s A', $xscript['time']),
+ $handles[$xscript['objectPHID']]->renderLink(),
+ $xscript['dryRun'] ? 'Yes' : '',
+ number_format((int)(1000 * $xscript['duration'])).' ms',
+ phutil_render_tag(
+ 'a',
+ array(
+ 'href' => '/herald/transcript/'.$xscript['id'].'/',
+ 'class' => 'button small grey',
+ ),
+ 'View Transcript'),
+ );
+ }
+
+ $table = new AphrontTableView($rows);
+ $table->setHeaders(
+ array(
+ 'Date',
+ 'Time',
+ 'Object',
+ 'Dry Run',
+ 'Duration',
+ 'View',
+ ));
+ $table->setColumnClasses(
+ array(
+ '',
+ 'right',
+ 'wide wrap',
+ '',
+ '',
+ 'action',
+ ));
+
+
+ $panel = new AphrontPanelView();
+ $panel->setHeader('Herald Transcripts');
+ $panel->appendChild($table);
+
+ return $this->buildStandardPageResponse(
+ $panel,
+ array(
+ 'title' => 'Herald Transcripts',
+ 'tab' => 'transcripts',
+ ));
+ }
+
+}
diff --git a/src/applications/herald/controller/transcriptlist/__init__.php b/src/applications/herald/controller/transcriptlist/__init__.php
new file mode 100644
index 0000000000..5a799e96f2
--- /dev/null
+++ b/src/applications/herald/controller/transcriptlist/__init__.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'applications/herald/controller/base');
+phutil_require_module('phabricator', 'applications/herald/storage/transcript/base');
+phutil_require_module('phabricator', 'applications/phid/handle/data');
+phutil_require_module('phabricator', 'storage/queryfx');
+phutil_require_module('phabricator', 'view/control/table');
+phutil_require_module('phabricator', 'view/layout/panel');
+
+phutil_require_module('phutil', 'markup');
+phutil_require_module('phutil', 'utils');
+
+
+phutil_require_source('HeraldTranscriptListController.php');
diff --git a/src/applications/herald/engine/effect/HeraldEffect.php b/src/applications/herald/engine/effect/HeraldEffect.php
index 074f473d70..510e5f3b8a 100644
--- a/src/applications/herald/engine/effect/HeraldEffect.php
+++ b/src/applications/herald/engine/effect/HeraldEffect.php
@@ -1,85 +1,85 @@
<?php
/*
* Copyright 2011 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.
*/
class HeraldEffect {
- protected $objectID;
+ protected $objectPHID;
protected $action;
protected $target;
protected $ruleID;
protected $effector;
protected $reason;
- public function setObjectID($object_id) {
- $this->objectID = $object_id;
+ public function setObjectPHID($object_phid) {
+ $this->objectPHID = $object_phid;
return $this;
}
- public function getObjectID() {
- return $this->objectID;
+ public function getObjectPHID() {
+ return $this->objectPHID;
}
public function setAction($action) {
$this->action = $action;
return $this;
}
public function getAction() {
return $this->action;
}
public function setTarget($target) {
$this->target = $target;
return $this;
}
public function getTarget() {
return $this->target;
}
public function setRuleID($rule_id) {
$this->ruleID = $rule_id;
return $this;
}
public function getRuleID() {
return $this->ruleID;
}
public function setEffector($effector) {
$this->effector = $effector;
return $this;
}
public function getEffector() {
return $this->effector;
}
public function setReason($reason) {
$this->reason = $reason;
return $this;
}
public function getReason() {
return $this->reason;
}
}
diff --git a/src/applications/herald/engine/engine/HeraldEngine.php b/src/applications/herald/engine/engine/HeraldEngine.php
index 96f283e977..5cc8f8338c 100644
--- a/src/applications/herald/engine/engine/HeraldEngine.php
+++ b/src/applications/herald/engine/engine/HeraldEngine.php
@@ -1,447 +1,453 @@
<?php
/*
* Copyright 2011 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.
*/
class HeraldEngine {
protected $rules = array();
protected $results = array();
protected $stack = array();
protected $activeRule = null;
protected $fieldCache = array();
protected $object = null;
- public static function loadAndApplyRules(IHeraldable $object) {
+ public static function loadAndApplyRules(HeraldObjectAdapter $object) {
$content_type = $object->getHeraldTypeName();
$rules = HeraldRule::loadAllByContentTypeWithFullData($content_type);
$engine = new HeraldEngine();
$effects = $engine->applyRules($rules, $object);
$engine->applyEffects($effects, $object);
return $engine->getTranscript();
}
- public function applyRules(array $rules, IHeraldable $object) {
+ public function applyRules(array $rules, HeraldObjectAdapter $object) {
$t_start = microtime(true);
$rules = mpull($rules, null, 'getID');
$this->transcript = new HeraldTranscript();
- $this->transcript->setObjectID((string)$object->getFBID());
+ $this->transcript->setObjectPHID((string)$object->getPHID());
$this->fieldCache = array();
$this->results = array();
$this->rules = $rules;
$this->object = $object;
$effects = array();
foreach ($rules as $id => $rule) {
$this->stack = array();
try {
$rule_matches = $this->doesRuleMatch($rule, $object);
} catch (HeraldRecursiveConditionsException $ex) {
$names = array();
foreach ($this->stack as $rule_id => $ignored) {
$names[] = '"'.$rules[$rule_id]->getName().'"';
}
$names = implode(', ', $names);
foreach ($this->stack as $rule_id => $ignored) {
$xscript = new HeraldRuleTranscript();
$xscript->setRuleID($rule_id);
$xscript->setResult(false);
$xscript->setReason(
"Rules {$names} are recursively dependent upon one another! ".
"Don't do this! You have formed an unresolvable cycle in the ".
"dependency graph!");
$xscript->setRuleName($rules[$rule_id]->getName());
- $xscript->setRuleOwner($rules[$rule_id]->getOwnerID());
+ $xscript->setRuleOwner($rules[$rule_id]->getAuthorPHID());
$this->transcript->addRuleTranscript($xscript);
}
$rule_matches = false;
}
$this->results[$id] = $rule_matches;
if ($rule_matches) {
foreach ($this->getRuleEffects($rule, $object) as $effect) {
$effects[] = $effect;
}
}
}
$object_transcript = new HeraldObjectTranscript();
- $object_transcript->setFBID($object->getFBID());
+ $object_transcript->setPHID($object->getPHID());
$object_transcript->setName($object->getHeraldName());
$object_transcript->setType($object->getHeraldTypeName());
$object_transcript->setFields($this->fieldCache);
$this->transcript->setObjectTranscript($object_transcript);
$t_end = microtime(true);
$this->transcript->setDuration($t_end - $t_start);
return $effects;
}
- public function applyEffects(array $effects, IHeraldable $object) {
+ public function applyEffects(array $effects, HeraldObjectAdapter $object) {
if ($object instanceof DryRunHeraldable) {
$this->transcript->setDryRun(true);
} else {
$this->transcript->setDryRun(false);
}
foreach ($object->applyHeraldEffects($effects) as $apply_xscript) {
if (!($apply_xscript instanceof HeraldApplyTranscript)) {
throw new Exception(
"Heraldable must return HeraldApplyTranscripts from ".
"applyHeraldEffect().");
}
$this->transcript->addApplyTranscript($apply_xscript);
}
}
public function getTranscript() {
$this->transcript->save();
return $this->transcript;
}
- protected function doesRuleMatch(HeraldRule $rule, IHeraldable $object) {
+ protected function doesRuleMatch(
+ HeraldRule $rule,
+ HeraldObjectAdapter $object) {
+
$id = $rule->getID();
if (isset($this->results[$id])) {
// If we've already evaluated this rule because another rule depends
// on it, we don't need to reevaluate it.
return $this->results[$id];
}
if (isset($this->stack[$id])) {
// We've recursed, fail all of the rules on the stack. This happens when
// there's a dependency cycle with "Rule conditions match for rule ..."
// conditions.
foreach ($this->stack as $rule_id => $ignored) {
$this->results[$rule_id] = false;
}
throw new HeraldRecursiveConditionsException();
}
$this->stack[$id] = true;
$all = $rule->getMustMatchAll();
$conditions = $rule->getConditions();
$result = null;
$local_version = id(new HeraldRule())->getConfigVersion();
if ($rule->getConfigVersion() > $local_version) {
$reason = "Rule could not be processed, it was created with a newer ".
"version of Herald.";
$result = false;
} else if (!$conditions) {
$reason = "Rule failed automatically because it has no conditions.";
$result = false;
/* TOOD: Restore this in some form?
- } else if (!is_fb_employee($rule->getOwnerID())) {
+ } else if (!is_fb_employee($rule->getAuthorPHID())) {
$reason = "Rule failed automatically because its owner is not an ".
"active employee.";
$result = false;
*/
} else {
foreach ($conditions as $condition) {
$match = $this->doesConditionMatch($rule, $condition, $object);
if (!$all && $match) {
$reason = "Any condition matched.";
$result = true;
break;
}
if ($all && !$match) {
$reason = "Not all conditions matched.";
$result = false;
break;
}
}
if ($result === null) {
if ($all) {
$reason = "All conditions matched.";
$result = true;
} else {
$reason = "No conditions matched.";
$result = false;
}
}
}
$rule_transcript = new HeraldRuleTranscript();
$rule_transcript->setRuleID($rule->getID());
$rule_transcript->setResult($result);
$rule_transcript->setReason($reason);
$rule_transcript->setRuleName($rule->getName());
- $rule_transcript->setRuleOwner($rule->getOwnerID());
+ $rule_transcript->setRuleOwner($rule->getAuthorPHID());
$this->transcript->addRuleTranscript($rule_transcript);
return $result;
}
protected function doesConditionMatch(
HeraldRule $rule,
HeraldCondition $condition,
- IHeraldable $object) {
+ HeraldObjectAdapter $object) {
$object_value = $this->getConditionObjectValue($condition, $object);
$test_value = $condition->getValue();
- $cond = $condition->getCondition();
+ $cond = $condition->getFieldCondition();
$transcript = new HeraldConditionTranscript();
$transcript->setRuleID($rule->getID());
$transcript->setConditionID($condition->getID());
$transcript->setFieldName($condition->getFieldName());
$transcript->setCondition($cond);
$transcript->setTestValue($test_value);
$result = null;
switch ($cond) {
case HeraldConditionConfig::CONDITION_CONTAINS:
// "Contains" can take an array of strings, as in "Any changed
// filename" for diffs.
foreach ((array)$object_value as $value) {
$result = (stripos($value, $test_value) !== false);
if ($result) {
break;
}
}
break;
case HeraldConditionConfig::CONDITION_NOT_CONTAINS:
$result = (stripos($object_value, $test_value) === false);
break;
case HeraldConditionConfig::CONDITION_IS:
$result = ($object_value == $test_value);
break;
case HeraldConditionConfig::CONDITION_IS_NOT:
$result = ($object_value != $test_value);
break;
case HeraldConditionConfig::CONDITION_IS_ME:
- $result = ($object_value == $rule->getOwnerID());
+ $result = ($object_value == $rule->getAuthorPHID());
break;
case HeraldConditionConfig::CONDITION_IS_NOT_ME:
- $result = ($object_value != $rule->getOwnerID());
+ $result = ($object_value != $rule->getAuthorPHID());
break;
case HeraldConditionConfig::CONDITION_IS_ANY:
$test_value = array_flip($test_value);
$result = isset($test_value[$object_value]);
break;
case HeraldConditionConfig::CONDITION_IS_NOT_ANY:
$test_value = array_flip($test_value);
$result = !isset($test_value[$object_value]);
break;
case HeraldConditionConfig::CONDITION_INCLUDE_ALL:
if (!is_array($object_value)) {
$transcript->setNote('Object produced bad value!');
$result = false;
} else {
$have = array_select_keys(array_flip($object_value),
$test_value);
$result = (count($have) == count($test_value));
}
break;
case HeraldConditionConfig::CONDITION_INCLUDE_ANY:
$result = (bool)array_select_keys(array_flip($object_value),
$test_value);
break;
case HeraldConditionConfig::CONDITION_INCLUDE_NONE:
$result = !array_select_keys(array_flip($object_value),
$test_value);
break;
case HeraldConditionConfig::CONDITION_EXISTS:
$result = (bool)$object_value;
break;
case HeraldConditionConfig::CONDITION_NOT_EXISTS:
$result = !$object_value;
break;
case HeraldConditionConfig::CONDITION_REGEXP:
foreach ((array)$object_value as $value) {
$result = @preg_match($test_value, $value);
if ($result === false) {
$transcript->setNote(
"Regular expression is not valid!");
break;
}
if ($result) {
break;
}
}
$result = (bool)$result;
break;
case HeraldConditionConfig::CONDITION_REGEXP_PAIR:
// Match a JSON-encoded pair of regular expressions against a
// dictionary. The first regexp must match the dictionary key, and the
// second regexp must match the dictionary value. If any key/value pair
// in the dictionary matches both regexps, the condition is satisfied.
$regexp_pair = json_decode($test_value, true);
if (!is_array($regexp_pair)) {
$result = false;
$transcript->setNote("Regular expression pair is not valid JSON!");
break;
}
if (count($regexp_pair) != 2) {
$result = false;
$transcript->setNote("Regular expression pair is not a pair!");
break;
}
$key_regexp = array_shift($regexp_pair);
$value_regexp = array_shift($regexp_pair);
foreach ((array)$object_value as $key => $value) {
$key_matches = @preg_match($key_regexp, $key);
if ($key_matches === false) {
$result = false;
$transcript->setNote("First regular expression is invalid!");
break 2;
}
if ($key_matches) {
$value_matches = @preg_match($value_regexp, $value);
if ($value_matches === false) {
$result = false;
$transcript->setNote("Second regular expression is invalid!");
break 2;
}
if ($value_matches) {
$result = true;
break 2;
}
}
}
$result = false;
break;
case HeraldConditionConfig::CONDITION_RULE:
case HeraldConditionConfig::CONDITION_NOT_RULE:
$rule = idx($this->rules, $test_value);
if (!$rule) {
$transcript->setNote(
"Condition references a rule which does not exist!");
$result = false;
} else {
$is_not = ($cond == HeraldConditionConfig::CONDITION_NOT_RULE);
$result = $this->doesRuleMatch($rule, $object);
if ($is_not) {
$result = !$result;
}
}
break;
default:
throw new HeraldInvalidConditionException(
"Unknown condition '{$cond}'.");
}
$transcript->setResult($result);
$this->transcript->addConditionTranscript($transcript);
return $result;
}
protected function getConditionObjectValue(
HeraldCondition $condition,
- IHeraldable $object) {
+ HeraldObjectAdapter $object) {
$field = $condition->getFieldName();
return $this->getObjectFieldValue($field);
}
public function getObjectFieldValue($field) {
if (isset($this->fieldCache[$field])) {
return $this->fieldCache[$field];
}
$result = null;
switch ($field) {
case HeraldFieldConfig::FIELD_RULE:
$result = null;
break;
case HeraldFieldConfig::FIELD_TITLE:
case HeraldFieldConfig::FIELD_BODY:
case HeraldFieldConfig::FIELD_DIFF_FILE:
case HeraldFieldConfig::FIELD_DIFF_CONTENT:
// TODO: Type should be string.
$result = $this->object->getHeraldField($field);
break;
case HeraldFieldConfig::FIELD_AUTHOR:
case HeraldFieldConfig::FIELD_REPOSITORY:
case HeraldFieldConfig::FIELD_MERGE_REQUESTER:
- // TODO: Type should be fbid.
+ // TODO: Type should be PHID.
$result = $this->object->getHeraldField($field);
break;
case HeraldFieldConfig::FIELD_TAGS:
case HeraldFieldConfig::FIELD_REVIEWER:
case HeraldFieldConfig::FIELD_REVIEWERS:
case HeraldFieldConfig::FIELD_CC:
case HeraldFieldConfig::FIELD_DIFFERENTIAL_REVIEWERS:
case HeraldFieldConfig::FIELD_DIFFERENTIAL_CCS:
// TODO: Type should be list.
$result = $this->object->getHeraldField($field);
break;
case HeraldFieldConfig::FIELD_AFFECTED_PACKAGE:
case HeraldFieldConfig::FIELD_AFFECTED_PACKAGE_OWNER:
$result = $this->object->getHeraldField($field);
if (!is_array($result)) {
throw new HeraldInvalidFieldException(
"Value of field type {$field} is not an array!");
}
break;
case HeraldFieldConfig::FIELD_DIFFERENTIAL_REVISION:
// TODO: Type should be boolean I guess.
$result = $this->object->getHeraldField($field);
break;
default:
throw new HeraldInvalidConditionException(
"Unknown field type '{$field}'!");
}
$this->fieldCache[$field] = $result;
return $result;
}
- protected function getRuleEffects(HeraldRule $rule, IHeraldable $object) {
+ protected function getRuleEffects(
+ HeraldRule $rule,
+ HeraldObjectAdapter $object) {
+
$effects = array();
foreach ($rule->getActions() as $action) {
$effect = new HeraldEffect();
- $effect->setObjectID($object->getFBID());
+ $effect->setObjectPHID($object->getPHID());
$effect->setAction($action->getAction());
$effect->setTarget($action->getTarget());
$effect->setRuleID($rule->getID());
$name = $rule->getName();
$id = $rule->getID();
$effect->setReason(
'Conditions were met for Herald rule "'.$name.'" (#'.$id.').');
$effects[] = $effect;
}
return $effects;
}
}
diff --git a/src/applications/herald/storage/transcript/base/HeraldTranscript.php b/src/applications/herald/storage/transcript/base/HeraldTranscript.php
index 48df40ebec..7928ff0726 100644
--- a/src/applications/herald/storage/transcript/base/HeraldTranscript.php
+++ b/src/applications/herald/storage/transcript/base/HeraldTranscript.php
@@ -1,144 +1,143 @@
<?php
/*
* Copyright 2011 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.
*/
class HeraldTranscript extends HeraldDAO {
protected $id;
- protected $fbid;
+ protected $phid;
protected $objectTranscript;
protected $ruleTranscripts = array();
protected $conditionTranscripts = array();
protected $applyTranscripts = array();
protected $time;
protected $host;
- protected $path;
protected $duration;
- protected $objectID;
+ protected $objectPHID;
protected $dryRun;
public function getXHeraldRulesHeader() {
$ids = array();
foreach ($this->applyTranscripts as $xscript) {
if ($xscript->getApplied()) {
if ($xscript->getRuleID()) {
$ids[] = $xscript->getRuleID();
}
}
}
if (!$ids) {
return 'none';
}
// A rule may have multiple effects, which will cause it to be listed
// multiple times.
$ids = array_unique($ids);
foreach ($ids as $k => $id) {
$ids[$k] = '<'.$id.'>';
}
return implode(', ', $ids);
}
protected function getConfiguration() {
// Ugh. Too much of a mess to deal with.
return array(
- self::CONFIG_AUX_FBID => 'HERALD_TRANSCRIPT',
+ self::CONFIG_AUX_PHID => true,
+ self::CONFIG_TIMESTAMPS => false,
self::CONFIG_SERIALIZATION => array(
'objectTranscript' => self::SERIALIZATION_PHP,
'ruleTranscripts' => self::SERIALIZATION_PHP,
'conditionTranscripts' => self::SERIALIZATION_PHP,
'applyTranscripts' => self::SERIALIZATION_PHP,
),
) + parent::getConfiguration();
}
public function __construct() {
$this->time = time();
$this->host = php_uname('n');
- $this->path = realpath($_SERVER['PHP_ROOT']);
}
public function addApplyTranscript(HeraldApplyTranscript $transcript) {
$this->applyTranscripts[] = $transcript;
return $this;
}
public function getApplyTranscripts() {
return nonempty($this->applyTranscripts, array());
}
public function setDuration($duration) {
$this->duration = $duration;
return $this;
}
public function setObjectTranscript(HeraldObjectTranscript $transcript) {
$this->objectTranscript = $transcript;
return $this;
}
public function getObjectTranscript() {
return $this->objectTranscript;
}
public function addRuleTranscript(HeraldRuleTranscript $transcript) {
$this->ruleTranscripts[$transcript->getRuleID()] = $transcript;
return $this;
}
public function discardDetails() {
$this->applyTranscripts = null;
$this->ruleTranscripts = null;
$this->objectTranscript = null;
$this->conditionTranscripts = null;
}
public function getRuleTranscripts() {
return nonempty($this->ruleTranscripts, array());
}
public function addConditionTranscript(
HeraldConditionTranscript $transcript) {
$rule_id = $transcript->getRuleID();
$cond_id = $transcript->getConditionID();
$this->conditionTranscripts[$rule_id][$cond_id] = $transcript;
return $this;
}
public function getConditionTranscriptsForRule($rule_id) {
return idx($this->conditionTranscripts, $rule_id, array());
}
public function getMetadataMap() {
return array(
- 'Run At Epoch' => date('F jS, g:i A', $this->time),
- 'Run On Host' => $this->host.':'.$this->path,
+ 'Run At Epoch' => date('F jS, g:i:s A', $this->time),
+ 'Run On Host' => $this->host,
'Run Duration' => (int)(1000 * $this->duration).' ms',
);
}
- public function getURI() {
- return 'http://tools.facebook.com/herald/transcript/'.$this->getID().'/';
+ public function generatePHID() {
+ return PhabricatorPHID::generateNewPHID('HLXS');
}
}
diff --git a/src/applications/herald/storage/transcript/base/__init__.php b/src/applications/herald/storage/transcript/base/__init__.php
index dd3d4f4993..3b9b170090 100644
--- a/src/applications/herald/storage/transcript/base/__init__.php
+++ b/src/applications/herald/storage/transcript/base/__init__.php
@@ -1,14 +1,15 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/herald/storage/base');
+phutil_require_module('phabricator', 'applications/phid/storage/phid');
phutil_require_module('phutil', 'utils');
phutil_require_source('HeraldTranscript.php');
diff --git a/src/applications/herald/storage/transcript/object/HeraldObjectTranscript.php b/src/applications/herald/storage/transcript/object/HeraldObjectTranscript.php
index 4e9fc1e712..432921eefb 100644
--- a/src/applications/herald/storage/transcript/object/HeraldObjectTranscript.php
+++ b/src/applications/herald/storage/transcript/object/HeraldObjectTranscript.php
@@ -1,61 +1,61 @@
<?php
/*
* Copyright 2011 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.
*/
class HeraldObjectTranscript {
- protected $fbid;
+ protected $phid;
protected $type;
protected $name;
protected $fields;
- public function setFBID($fbid) {
- $this->fbid = $fbid;
+ public function setPHID($phid) {
+ $this->phid = $phid;
return $this;
}
- public function getFBID() {
- return $this->fbid;
+ public function getPHID() {
+ return $this->phid;
}
public function setType($type) {
$this->type = $type;
return $this;
}
public function getType() {
return $this->type;
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function getName() {
return $this->name;
}
public function setFields(array $fields) {
$this->fields = $fields;
return $this;
}
public function getFields() {
return $this->fields;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Nov 30, 10:25 PM (7 h, 5 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
429922
Default Alt Text
(169 KB)

Event Timeline