Page MenuHomestyx hydra

No OneTemporary

diff --git a/resources/sql/patches/015.preferences.sql b/resources/sql/patches/015.preferences.sql
new file mode 100644
index 0000000000..08a6c71d8a
--- /dev/null
+++ b/resources/sql/patches/015.preferences.sql
@@ -0,0 +1,6 @@
+CREATE TABLE phabricator_user.user_preferences (
+ id int unsigned not null auto_increment primary key,
+ userPHID varchar(64) binary not null,
+ preferences longblob not null,
+ unique key (userPHID)
+);
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index 49289e0c21..7e96b82ef7 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1,735 +1,741 @@
<?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',
'DiffusionDiffController' => 'applications/diffusion/controller/diff',
'DiffusionDiffQuery' => 'applications/diffusion/query/diff/base',
'DiffusionFileContent' => 'applications/diffusion/data/filecontent',
'DiffusionFileContentQuery' => 'applications/diffusion/query/filecontent/base',
'DiffusionGitBranchQuery' => 'applications/diffusion/query/branch/git',
'DiffusionGitBrowseQuery' => 'applications/diffusion/query/browse/git',
'DiffusionGitDiffQuery' => 'applications/diffusion/query/diff/git',
'DiffusionGitFileContentQuery' => 'applications/diffusion/query/filecontent/git',
'DiffusionGitHistoryQuery' => 'applications/diffusion/query/history/git',
'DiffusionGitLastModifiedQuery' => 'applications/diffusion/query/lastmodified/git',
'DiffusionGitPathIDQuery' => 'applications/diffusion/query/pathid/base',
'DiffusionGitRequest' => 'applications/diffusion/request/git',
'DiffusionHistoryController' => 'applications/diffusion/controller/history',
'DiffusionHistoryQuery' => 'applications/diffusion/query/history/base',
'DiffusionHistoryTableView' => 'applications/diffusion/view/historytable',
'DiffusionHomeController' => 'applications/diffusion/controller/home',
'DiffusionLastModifiedController' => 'applications/diffusion/controller/lastmodified',
'DiffusionLastModifiedQuery' => 'applications/diffusion/query/lastmodified/base',
'DiffusionPathChange' => 'applications/diffusion/data/pathchange',
'DiffusionPathChangeQuery' => 'applications/diffusion/query/pathchange/base',
'DiffusionRepositoryController' => 'applications/diffusion/controller/repository',
'DiffusionRepositoryPath' => 'applications/diffusion/data/repositorypath',
'DiffusionRequest' => 'applications/diffusion/request/base',
'DiffusionSvnBrowseQuery' => 'applications/diffusion/query/browse/svn',
'DiffusionSvnDiffQuery' => 'applications/diffusion/query/diff/svn',
'DiffusionSvnFileContentQuery' => 'applications/diffusion/query/filecontent/svn',
'DiffusionSvnHistoryQuery' => 'applications/diffusion/query/history/svn',
'DiffusionSvnLastModifiedQuery' => 'applications/diffusion/query/lastmodified/svn',
'DiffusionSvnRequest' => 'applications/diffusion/request/svn',
'DiffusionView' => 'applications/diffusion/view/base',
'HeraldAction' => 'applications/herald/storage/action',
'HeraldActionConfig' => 'applications/herald/config/action',
'HeraldApplyTranscript' => 'applications/herald/storage/transcript/apply',
'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',
+ 'PhabricatorEditPreferencesController' => 'applications/preferences/controller/edit',
'PhabricatorEmailLoginController' => 'applications/auth/controller/email',
'PhabricatorEmailTokenController' => 'applications/auth/controller/emailtoken',
'PhabricatorEnv' => 'infrastructure/env',
'PhabricatorFile' => 'applications/files/storage/file',
'PhabricatorFileController' => 'applications/files/controller/base',
'PhabricatorFileDAO' => 'applications/files/storage/base',
'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',
+ 'PhabricatorPreferencesController' => 'applications/preferences/controller/base',
'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',
'PhabricatorRepositoryShortcut' => 'applications/repository/storage/shortcut',
'PhabricatorRepositorySvnCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/svn',
'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/svn',
'PhabricatorRepositorySvnCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/svn',
'PhabricatorRepositoryType' => 'applications/repository/constants/repositorytype',
'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',
+ 'PhabricatorUserPreferences' => 'applications/people/storage/preferences',
'PhabricatorUserProfile' => 'applications/people/storage/profile',
'PhabricatorUserSettingsController' => 'applications/people/controller/settings',
'PhabricatorWorker' => 'infrastructure/daemon/workers/worker',
'PhabricatorWorkerDAO' => 'infrastructure/daemon/workers/storage/base',
'PhabricatorWorkerTask' => 'infrastructure/daemon/workers/storage/task',
'PhabricatorWorkerTaskData' => 'infrastructure/daemon/workers/storage/taskdata',
'PhabricatorWorkerTaskDetailController' => 'applications/daemon/controller/workertaskdetail',
'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',
'DiffusionDiffController' => 'DiffusionController',
'DiffusionGitBranchQuery' => 'DiffusionBranchQuery',
'DiffusionGitBrowseQuery' => 'DiffusionBrowseQuery',
'DiffusionGitDiffQuery' => 'DiffusionDiffQuery',
'DiffusionGitFileContentQuery' => 'DiffusionFileContentQuery',
'DiffusionGitHistoryQuery' => 'DiffusionHistoryQuery',
'DiffusionGitLastModifiedQuery' => 'DiffusionLastModifiedQuery',
'DiffusionGitRequest' => 'DiffusionRequest',
'DiffusionHistoryController' => 'DiffusionController',
'DiffusionHistoryTableView' => 'DiffusionView',
'DiffusionHomeController' => 'DiffusionController',
'DiffusionLastModifiedController' => 'DiffusionController',
'DiffusionRepositoryController' => 'DiffusionController',
'DiffusionSvnBrowseQuery' => 'DiffusionBrowseQuery',
'DiffusionSvnDiffQuery' => 'DiffusionDiffQuery',
'DiffusionSvnFileContentQuery' => 'DiffusionFileContentQuery',
'DiffusionSvnHistoryQuery' => 'DiffusionHistoryQuery',
'DiffusionSvnLastModifiedQuery' => 'DiffusionLastModifiedQuery',
'DiffusionSvnRequest' => 'DiffusionRequest',
'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',
+ 'PhabricatorEditPreferencesController' => 'PhabricatorPreferencesController',
'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',
+ 'PhabricatorPreferencesController' => 'PhabricatorController',
'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',
'PhabricatorRepositoryShortcut' => 'PhabricatorRepositoryDAO',
'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',
+ 'PhabricatorUserPreferences' => 'PhabricatorUserDAO',
'PhabricatorUserProfile' => 'PhabricatorUserDAO',
'PhabricatorUserSettingsController' => 'PhabricatorPeopleController',
'PhabricatorWorkerDAO' => 'PhabricatorLiskDAO',
'PhabricatorWorkerTask' => 'PhabricatorWorkerDAO',
'PhabricatorWorkerTaskData' => 'PhabricatorWorkerDAO',
'PhabricatorWorkerTaskDetailController' => 'PhabricatorDaemonController',
'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 0ddfdcf971..aed566c439 100755
--- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
+++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
@@ -1,350 +1,353 @@
<?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',
'repository/'.
'(?P<path>[^/]+)/'.
'$'
=> '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',
'diff/'.
'(?P<path>.*?)'.
'(?:[;](?P<commit>[a-z0-9]+))?'.
'$'
=> 'DiffusionDiffController',
'lastmodified/'.
'(?P<path>.*?)'.
'(?:[;](?P<commit>[a-z0-9]+))?'.
'$'
=> 'DiffusionLastModifiedController',
),
),
'/daemon/' => array(
'task/(?P<id>\d+)/$' => 'PhabricatorWorkerTaskDetailController',
'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+)/(?:(?P<filter>\w+)/)?$'
=> 'HeraldTranscriptController',
),
+ '/preferences/' => array(
+ '$' => 'PhabricatorEditPreferencesController'
+ ),
);
}
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/parser/changeset/DifferentialChangesetParser.php b/src/applications/differential/parser/changeset/DifferentialChangesetParser.php
index 58ba4810e4..121347568f 100644
--- a/src/applications/differential/parser/changeset/DifferentialChangesetParser.php
+++ b/src/applications/differential/parser/changeset/DifferentialChangesetParser.php
@@ -1,1347 +1,1347 @@
<?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 DifferentialChangesetParser {
protected $visible = array();
protected $new = array();
protected $old = array();
protected $intra = array();
protected $newRender = null;
protected $oldRender = null;
protected $parsedHunk = false;
protected $filename = null;
protected $filetype = null;
protected $changesetID = null;
protected $missingOld = array();
protected $missingNew = array();
protected $comments = array();
protected $specialAttributes = array();
protected $changeset;
protected $whitespaceMode = null;
protected $subparser;
protected $oldChangesetID = null;
protected $noHighlight;
private $handles;
private $user;
const CACHE_VERSION = 4;
const ATTR_GENERATED = 'attr:generated';
const ATTR_DELETED = 'attr:deleted';
const ATTR_UNCHANGED = 'attr:unchanged';
const ATTR_WHITELINES = 'attr:white';
const LINES_CONTEXT = 8;
const WHITESPACE_SHOW_ALL = 'show-all';
const WHITESPACE_IGNORE_TRAILING = 'ignore-trailing';
const WHITESPACE_IGNORE_ALL = 'ignore-all';
public function setRightSideCommentMapping($id, $is_new) {
}
public function setLeftSideCommentMapping($id, $is_new) {
}
public function setChangeset($changeset) {
$this->changeset = $changeset;
$this->setFilename($changeset->getFilename());
$this->setChangesetID($changeset->getID());
return $this;
}
public function setWhitespaceMode($whitespace_mode) {
$this->whitespaceMode = $whitespace_mode;
return $this;
}
public function setOldChangesetID($old_changeset_id) {
$this->oldChangesetID = $old_changeset_id;
return $this;
}
public function setChangesetID($changeset_id) {
$this->changesetID = $changeset_id;
return $this;
}
public function getChangeset() {
return $this->changeset;
}
public function getChangesetID() {
return $this->changesetID;
}
public function setFilename($filename) {
$this->filename = $filename;
if (strpos($filename, '.', 1) !== false) {
$parts = explode('.', $filename);
$this->filetype = end($parts);
}
}
public function setHandles(array $handles) {
$this->handles = $handles;
return $this;
}
public function setMarkupEngine(PhutilMarkupEngine $engine) {
$this->markupEngine = $engine;
return $this;
}
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function parseHunk(DifferentialHunk $hunk) {
$this->parsedHunk = true;
$lines = $hunk->getChanges();
// Flatten UTF-8 into "\0". We don't support UTF-8 because the diffing
// algorithms are byte-oriented (not character oriented) and everyone seems
// to be in agreement that it's fairly reasonable not to allow UTF-8 in
// source files. These bytes will later be replaced with a "?" glyph, but
// in the meantime we replace them with "\0" since Pygments is happy to
// deal with that.
$lines = preg_replace('/[\x80-\xFF]/', "\0", $lines);
$lines = str_replace(
array("\t", "\r\n", "\r"),
array(' ', "\n", "\n"),
$lines);
$lines = explode("\n", $lines);
$types = array();
foreach ($lines as $line_index => $line) {
$lines[$line_index] = $line;
if (isset($line[0])) {
$char = $line[0];
if ($char == ' ') {
$types[$line_index] = null;
} else if ($char == '\\' && $line_index > 0) {
$types[$line_index] = $types[$line_index - 1];
} else {
$types[$line_index] = $char;
}
} else {
$types[$line_index] = null;
}
}
$old_line = $hunk->getOldOffset();
$new_line = $hunk->getNewOffset();
$num_lines = count($lines);
if ($old_line > 1) {
$this->missingOld[$old_line] = true;
} else if ($new_line > 1) {
$this->missingNew[$new_line] = true;
}
for ($cursor = 0; $cursor < $num_lines; $cursor++) {
$type = $types[$cursor];
$data = array(
'type' => $type,
'text' => (string)substr($lines[$cursor], 1),
'line' => $new_line,
);
switch ($type) {
case '+':
$this->new[] = $data;
++$new_line;
break;
case '-':
$data['line'] = $old_line;
$this->old[] = $data;
++$old_line;
break;
default:
$this->new[] = $data;
$data['line'] = $old_line;
$this->old[] = $data;
++$new_line;
++$old_line;
break;
}
}
}
public function getDisplayLine($offset, $length) {
$start = 1;
for ($ii = $offset; $ii > 0; $ii--) {
if ($this->new[$ii] && $this->new[$ii]['line']) {
$start = $this->new[$ii]['line'];
break;
}
}
$end = $start;
for ($ii = $offset + $length; $ii < count($this->new); $ii++) {
if ($this->new[$ii] && $this->new[$ii]['line']) {
$end = $this->new[$ii]['line'];
break;
}
}
return "{$start},{$end}";
}
public function parseInlineComment(DifferentialInlineComment $comment) {
$this->comments[] = $comment;
return $this;
}
public function process() {
$old = array();
$new = array();
$n = 0;
$this->old = array_reverse($this->old);
$this->new = array_reverse($this->new);
$whitelines = false;
$changed = false;
$skip_intra = array();
while (count($this->old) || count($this->new)) {
$o_desc = array_pop($this->old);
$n_desc = array_pop($this->new);
$oend = end($this->old);
if ($oend) {
$o_next = $oend['type'];
} else {
$o_next = null;
}
$nend = end($this->new);
if ($nend) {
$n_next = $nend['type'];
} else {
$n_next = null;
}
if ($o_desc) {
$o_type = $o_desc['type'];
} else {
$o_type = null;
}
if ($n_desc) {
$n_type = $n_desc['type'];
} else {
$n_type = null;
}
if (($o_type != null) && ($n_type == null)) {
$old[] = $o_desc;
$new[] = null;
if ($n_desc) {
array_push($this->new, $n_desc);
}
$changed = true;
continue;
}
if (($n_type != null) && ($o_type == null)) {
$old[] = null;
$new[] = $n_desc;
if ($o_desc) {
array_push($this->old, $o_desc);
}
$changed = true;
continue;
}
if ($this->whitespaceMode != self::WHITESPACE_SHOW_ALL) {
$similar = false;
switch ($this->whitespaceMode) {
case self::WHITESPACE_IGNORE_TRAILING:
if (rtrim($o_desc['text']) == rtrim($n_desc['text'])) {
$similar = true;
}
break;
}
if ($similar) {
$o_desc['type'] = null;
$n_desc['type'] = null;
$skip_intra[count($old)] = true;
$whitelines = true;
} else {
$changed = true;
}
} else {
$changed = true;
}
$old[] = $o_desc;
$new[] = $n_desc;
}
$this->old = $old;
$this->new = $new;
if ($this->subparser && false) { // TODO: This is bugged
// Use the subparser's side-by-side line information -- notably, the
// change types -- but replace all the line text with ours. This lets us
// render whitespace-only changes without marking them as different.
$old = $this->subparser->old;
$new = $this->subparser->new;
$old_text = ipull($this->old, 'text', 'line');
$new_text = ipull($this->new, 'text', 'line');
foreach ($old as $k => $desc) {
if (empty($desc)) {
continue;
}
$old[$k]['text'] = idx($old_text, $desc['line']);
}
foreach ($new as $k => $desc) {
if (empty($desc)) {
continue;
}
$new[$k]['text'] = idx($new_text, $desc['line']);
}
$this->old = $old;
$this->new = $new;
}
$min_length = min(count($this->old), count($this->new));
for ($ii = 0; $ii < $min_length; $ii++) {
if ($this->old[$ii] || $this->new[$ii]) {
if (isset($this->old[$ii]['text'])) {
$otext = $this->old[$ii]['text'];
} else {
$otext = '';
}
if (isset($this->new[$ii]['text'])) {
$ntext = $this->new[$ii]['text'];
} else {
$ntext = '';
}
if ($otext != $ntext && empty($skip_intra[$ii])) {
$this->intra[$ii] = ArcanistDiffUtils::generateIntralineDiff(
$otext,
$ntext);
}
}
}
$lines_context = self::LINES_CONTEXT;
$max_length = max(count($this->old), count($this->new));
$old = $this->old;
$new = $this->new;
$visible = false;
$last = 0;
for ($cursor = -$lines_context; $cursor < $max_length; $cursor++) {
$offset = $cursor + $lines_context;
if ((isset($old[$offset]) && $old[$offset]['type']) ||
(isset($new[$offset]) && $new[$offset]['type'])) {
$visible = true;
$last = $offset;
} else if ($cursor > $last + $lines_context) {
$visible = false;
}
if ($visible && $cursor > 0) {
$this->visible[$cursor] = 1;
}
}
$old_corpus = ipull($this->old, 'text');
$old_corpus_block = implode("\n", $old_corpus);
$new_corpus = ipull($this->new, 'text');
$new_corpus_block = implode("\n", $new_corpus);
if ($this->noHighlight) {
$this->oldRender = explode("\n", phutil_escape_html($old_corpus_block));
$this->newRender = explode("\n", phutil_escape_html($new_corpus_block));
} else {
$this->oldRender = $this->sourceHighlight($this->old, $old_corpus_block);
$this->newRender = $this->sourceHighlight($this->new, $new_corpus_block);
}
$this->applyIntraline(
$this->oldRender,
ipull($this->intra, 0),
$old_corpus);
$this->applyIntraline(
$this->newRender,
ipull($this->intra, 1),
$new_corpus);
$this->tokenHighlight($this->oldRender);
$this->tokenHighlight($this->newRender);
$unchanged = false;
if ($this->subparser && false) {
$unchanged = $this->subparser->isUnchanged();
$whitelines = $this->subparser->isWhitespaceOnly();
} else if (!$changed) {
$filetype = $this->changeset->getFileType();
if ($filetype == DifferentialChangeType::FILE_TEXT ||
$filetype == DifferentialChangeType::FILE_SYMLINK) {
$unchanged = true;
}
}
$generated = (strpos($new_corpus_block, '@'.'generated') !== false);
$this->specialAttributes = array(
self::ATTR_GENERATED => $generated,
self::ATTR_UNCHANGED => $unchanged,
self::ATTR_DELETED => array_filter($this->old) &&
!array_filter($this->new),
self::ATTR_WHITELINES => $whitelines
);
}
public function loadCache() {
if (!$this->changesetID) {
return false;
}
$data = null;
$changeset = new DifferentialChangeset();
$conn_r = $changeset->establishConnection('r');
$data = queryfx_one(
$conn_r,
'SELECT * FROM %T WHERE id = %d',
$changeset->getTableName().'_parse_cache',
$this->changesetID);
if (!$data) {
return false;
}
$data = json_decode($data['cache'], true);
if (!is_array($data) || !$data) {
return false;
}
foreach (self::getCacheableProperties() as $cache_key) {
if (!array_key_exists($cache_key, $data)) {
// If we're missing a cache key, assume we're looking at an old cache
// and ignore it.
return false;
}
}
if ($data['cacheVersion'] !== self::CACHE_VERSION) {
return false;
}
unset($data['cacheVersion'], $data['cacheHost']);
$cache_prop = array_select_keys($data, self::getCacheableProperties());
foreach ($cache_prop as $cache_key => $v) {
$this->$cache_key = $v;
}
return true;
}
protected static function getCacheableProperties() {
return array(
'visible',
'new',
'old',
'intra',
'newRender',
'oldRender',
'specialAttributes',
'missingOld',
'missingNew',
'cacheVersion',
'cacheHost',
);
}
public function saveCache() {
if (!$this->changesetID) {
return false;
}
$cache = array();
foreach (self::getCacheableProperties() as $cache_key) {
switch ($cache_key) {
case 'cacheVersion':
$cache[$cache_key] = self::CACHE_VERSION;
break;
case 'cacheHost':
$cache[$cache_key] = php_uname('n');
break;
default:
$cache[$cache_key] = $this->$cache_key;
break;
}
}
$cache = json_encode($cache);
try {
$changeset = new DifferentialChangeset();
$conn_w = $changeset->establishConnection('w');
queryfx(
$conn_w,
'INSERT INTO %T (id, cache) VALUES (%d, %s)
ON DUPLICATE KEY UPDATE cache = VALUES(cache)',
$changeset->getTableName().'_parse_cache',
$this->changesetID,
$cache);
} catch (AphrontQueryException $ex) {
// TODO: uhoh
}
}
public function isGenerated() {
return idx($this->specialAttributes, self::ATTR_GENERATED, false);
}
public function isDeleted() {
return idx($this->specialAttributes, self::ATTR_DELETED, false);
}
public function isUnchanged() {
return idx($this->specialAttributes, self::ATTR_UNCHANGED, false);
}
public function isWhitespaceOnly() {
return idx($this->specialAttributes, self::ATTR_WHITELINES, false);
}
public function getLength() {
return max(count($this->old), count($this->new));
}
protected function applyIntraline(&$render, $intra, $corpus) {
foreach ($render as $key => $text) {
if (isset($intra[$key])) {
$render[$key] = ArcanistDiffUtils::applyIntralineDiff(
$text,
$intra[$key]);
}
if (isset($corpus[$key]) && strlen($corpus[$key]) > 80) {
$render[$key] = $this->lineWrap($render[$key]);
}
}
}
protected function lineWrap($l) {
$c = 0;
$len = strlen($l);
$ins = array();
for ($ii = 0; $ii < $len; ++$ii) {
if ($l[$ii] == '&') {
do {
++$ii;
} while ($l[$ii] != ';');
++$c;
} else if ($l[$ii] == '<') {
do {
++$ii;
} while ($l[$ii] != '>');
} else {
++$c;
}
if ($c == 80) {
$ins[] = ($ii + 1);
$c = 0;
}
}
while (($pos = array_pop($ins))) {
$l = substr_replace(
$l,
"<span class=\"over-the-line\">\xE2\xAC\x85</span><br />",
$pos,
0);
}
return $l;
}
protected function tokenHighlight(&$render) {
foreach ($render as $key => $text) {
$render[$key] = str_replace(
"\0",
'<span class="uu">'."\xEF\xBF\xBD".'</span>',
$text);
}
}
protected function sourceHighlight($data, $corpus) {
$result = $this->highlightEngine->highlightSource(
$this->filetype,
$corpus);
$result_lines = explode("\n", $result);
foreach ($data as $key => $info) {
if (!$info) {
unset($result_lines[$key]);
}
}
return $result_lines;
}
private function tryCacheStuff() {
$whitespace_mode = $this->whitespaceMode;
switch ($whitespace_mode) {
case self::WHITESPACE_SHOW_ALL:
case self::WHITESPACE_IGNORE_TRAILING:
break;
default:
$whitespace_mode = self::WHITESPACE_IGNORE_ALL;
break;
}
$skip_cache = ($whitespace_mode != self::WHITESPACE_IGNORE_ALL);
$this->whitespaceMode = $whitespace_mode;
$changeset = $this->changeset;
if ($changeset->getFileType() == DifferentialChangeType::FILE_TEXT ||
$changeset->getFileType() == DifferentialChangeType::FILE_SYMLINK) {
if ($skip_cache || !$this->loadCache()) {
if ($this->whitespaceMode == self::WHITESPACE_IGNORE_ALL) {
// Huge mess. Generate a "-bw" (ignore all whitespace changes) diff,
// parse it out, and then play a shell game with the parsed format
// in process() so we highlight only changed lines but render
// whitespace differences. If we don't do this, we either fail to
// render whitespace changes (which is incredibly confusing,
// especially for python) or often produce a much larger set of
// differences than necessary.
$old_tmp = new TempFile();
$new_tmp = new TempFile();
Filesystem::writeFile($old_tmp, $changeset->makeOldFile());
Filesystem::writeFile($new_tmp, $changeset->makeNewFile());
list($err, $diff) = exec_manual(
'diff -bw -U65535 %s %s',
$old_tmp,
$new_tmp);
if (!strlen($diff)) {
// If there's no diff text, that means the files are identical
// except for whitespace changes. Build a synthetic, changeless
// diff. TODO: this is incredibly hacky.
$entire_file = explode("\n", $changeset->makeOldFile());
foreach ($entire_file as $k => $line) {
$entire_file[$k] = ' '.$line;
}
$len = count($entire_file);
$entire_file = implode("\n", $entire_file);
$diff = <<<EOSYNTHETIC
--- ignored 9999-99-99
+++ ignored 9999-99-99
@@ -{$len},{$len} +{$len},{$len} @@
{$entire_file}
EOSYNTHETIC;
}
$changes = id(new ArcanistDiffParser())->parseDiff($diff);
$diff = DifferentialDiff::newFromRawChanges($changes);
$changesets = $diff->getChangesets();
$alt_changeset = reset($changesets);
$this->subparser = new DifferentialChangesetParser();
$this->subparser->setChangeset($alt_changeset);
$this->subparser->setWhitespaceMode(self::WHITESPACE_IGNORE_TRAILING);
}
foreach ($changeset->getHunks() as $hunk) {
$this->parseHunk($hunk);
}
$this->process();
if (!$skip_cache) {
$this->saveCache();
}
}
}
}
public function render(
$range_start = null,
$range_len = null,
$mask_force = array()) {
$this->highlightEngine = new PhutilDefaultSyntaxHighlighterEngine();
$this->tryCacheStuff();
$changeset_id = $this->changesetID;
$feedback_mask = array();
switch ($this->changeset->getFileType()) {
case DifferentialChangeType::FILE_IMAGE:
$old = null;
$cur = null;
$metadata = $this->changeset->getMetadata();
$data = idx($metadata, 'attachment-data');
$old_phid = idx($metadata, 'old:binary-phid');
$new_phid = idx($metadata, 'new:binary-phid');
if ($old_phid || $new_phid) {
if ($old_phid) {
$old_uri = PhabricatorFileURI::getViewURIForPHID($old_phid);
$old = phutil_render_tag(
'img',
array(
'src' => $old_uri,
));
}
if ($new_phid) {
$new_uri = PhabricatorFileURI::getViewURIForPHID($new_phid);
$cur = phutil_render_tag(
'img',
array(
'src' => $new_uri,
));
}
}
$output = $this->renderChangesetTable(
$this->changeset,
'<tr>'.
'<th></th>'.
'<td class="differential-old-image">'.
'<div class="differential-image-stage">'.
$old.
'</div>'.
'</td>'.
'<th></th>'.
'<td class="differential-new-image">'.
'<div class="differential-image-stage">'.
$cur.
'</div>'.
'</td>'.
'</tr>');
return $output;
case DifferentialChangeType::FILE_DIRECTORY:
case DifferentialChangeType::FILE_BINARY:
$output = $this->renderChangesetTable($this->changeset, null);
return $output;
}
$shield = null;
if ($range_start === null && $range_len === null) {
if ($this->isGenerated()) {
$shield = $this->renderShield(
"This file contains generated code, which does not normally need ".
"to be reviewed.",
true);
} else if ($this->isUnchanged()) {
if ($this->isWhitespaceOnly()) {
$shield = $this->renderShield(
"This file was changed only by adding or removing trailing ".
"whitespace.",
false);
} else {
$shield = $this->renderShield(
"The contents of this file were not changed.",
false);
}
} else if ($this->isDeleted()) {
$shield = $this->renderShield(
"This file was completely deleted.",
true);
} else if ($this->changeset->getAffectedLineCount() > 2500) {
$lines = number_format($this->changeset->getAffectedLineCount());
$shield = $this->renderShield(
"This file has a very large number of changes ({$lines} lines).",
true);
} else if (preg_match('/\.sql3$/', $this->changeset->getFilename())) {
$shield = $this->renderShield(
".sql3 files are hidden by default.",
true);
}
}
if ($shield) {
return $this->renderChangesetTable($this->changeset, $shield);
}
$old_comments = array();
$new_comments = array();
$old_mask = array();
$new_mask = array();
$feedback_mask = array();
if ($this->comments) {
foreach ($this->comments as $comment) {
$start = max($comment->getLineNumber() - self::LINES_CONTEXT, 0);
$end = $comment->getLineNumber() +
$comment->getLineLength() +
self::LINES_CONTEXT;
$new = $this->isCommentInNewFile($comment);
for ($ii = $start; $ii <= $end; $ii++) {
if ($new) {
$new_mask[$ii] = true;
} else {
$old_mask[$ii] = true;
}
}
}
foreach ($this->old as $ii => $old) {
if (isset($old['line']) && isset($old_mask[$old['line']])) {
$feedback_mask[$ii] = true;
}
}
foreach ($this->new as $ii => $new) {
if (isset($new['line']) && isset($new_mask[$new['line']])) {
$feedback_mask[$ii] = true;
}
}
$this->comments = msort($this->comments, 'getID');
foreach ($this->comments as $comment) {
$final = $comment->getLineNumber() +
$comment->getLineLength();
if ($this->isCommentInNewFile($comment)) {
$new_comments[$final][] = $comment;
} else {
$old_comments[$final][] = $comment;
}
}
}
$html = $this->renderTextChange(
$range_start,
$range_len,
$mask_force,
$feedback_mask,
$old_comments,
$new_comments);
return $this->renderChangesetTable($this->changeset, $html);
}
private function isCommentInNewFile(DifferentialInlineComment $comment) {
if ($this->oldChangesetID) {
return ($comment->getChangesetID() != $this->oldChangesetID);
} else {
return $comment->getIsNewFile();
}
}
protected function renderShield($message, $more) {
if ($more) {
$end = $this->getLength();
$reference = $this->getChangeset()->getRenderingReference();
$more =
' '.
javelin_render_tag(
'a',
array(
'mustcapture' => true,
'sigil' => 'show-more',
'class' => 'complete',
'href' => '#',
'meta' => array(
'id' => $reference,
'range' => "0-{$end}",
),
),
'Show File Contents');
} else {
$more = null;
}
return javelin_render_tag(
'tr',
array(
'sigil' => 'context-target',
),
'<td class="differential-shield" colspan="4">'.
phutil_escape_html($message).
$more.
'</td>');
}
protected function renderTextChange(
$range_start,
$range_len,
$mask_force,
$feedback_mask,
array $old_comments,
array $new_comments) {
$context_not_available = null;
if ($this->missingOld || $this->missingNew) {
$context_not_available = javelin_render_tag(
'tr',
array(
'sigil' => 'context-target',
),
'<td colspan="4" class="show-more">'.
'Context not available.'.
'</td>');
$context_not_available = $context_not_available;
}
$html = array();
$rows = max(
count($this->old),
count($this->new));
if ($range_start === null) {
$range_start = 0;
}
if ($range_len === null) {
$range_len = $rows;
}
$range_len = min($range_len, $rows - $range_start);
$gaps = array();
$gap_start = 0;
$in_gap = false;
$mask = $this->visible + $mask_force + $feedback_mask;
$mask[$range_start + $range_len] = true;
for ($ii = $range_start; $ii <= $range_start + $range_len; $ii++) {
if (isset($mask[$ii])) {
if ($in_gap) {
$gap_length = $ii - $gap_start;
if ($gap_length <= self::LINES_CONTEXT) {
for ($jj = $gap_start; $jj <= $gap_start + $gap_length; $jj++) {
$mask[$jj] = true;
}
} else {
$gaps[] = array($gap_start, $gap_length);
}
$in_gap = false;
}
} else {
if (!$in_gap) {
$gap_start = $ii;
$in_gap = true;
}
}
}
$gaps = array_reverse($gaps);
$changeset = $this->changesetID;
$reference = $this->getChangeset()->getRenderingReference();
for ($ii = $range_start; $ii < $range_start + $range_len; $ii++) {
if (empty($mask[$ii])) {
$gap = array_pop($gaps);
$top = $gap[0];
$len = $gap[1];
$end = $top + $len - 20;
$contents = array();
if ($len > 40) {
$contents[] = javelin_render_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
'sigil' => 'show-more',
'meta' => array(
'id' => $reference,
'range' => "{$top}-{$len}/{$top}-20",
),
),
"\xE2\x96\xB2 Show 20 Lines");
}
$contents[] = javelin_render_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
'sigil' => 'show-more',
'meta' => array(
'id' => $reference,
'range' => "{$top}-{$len}/{$top}-{$len}",
),
),
'Show All '.$len.' Lines');
if ($len > 40) {
$contents[] = javelin_render_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
'sigil' => 'show-more',
'meta' => array(
'id' => $reference,
'range' => "{$top}-{$len}/{$end}-20",
),
),
"\xE2\x96\xBC Show 20 Lines");
};
$container = javelin_render_tag(
'tr',
array(
'sigil' => 'context-target',
),
'<td colspan="4" class="show-more">'.
implode(' &bull; ', $contents).
'</td>');
$html[] = $container;
$ii += ($len - 1);
continue;
}
if (isset($this->old[$ii])) {
$o_num = $this->old[$ii]['line'];
$o_text = isset($this->oldRender[$ii]) ? $this->oldRender[$ii] : null;
$o_attr = null;
if ($this->old[$ii]['type']) {
if (empty($this->new[$ii])) {
$o_attr = ' class="old old-full"';
} else {
$o_attr = ' class="old"';
}
}
} else {
$o_num = null;
$o_text = null;
$o_attr = null;
}
if (isset($this->new[$ii])) {
$n_num = $this->new[$ii]['line'];
$n_text = isset($this->newRender[$ii]) ? $this->newRender[$ii] : null;
$n_attr = null;
if ($this->new[$ii]['type']) {
if (empty($this->old[$ii])) {
$n_attr = ' class="new new-full"';
} else {
$n_attr = ' class="new"';
}
}
} else {
$n_num = null;
$n_text = null;
$n_attr = null;
}
if (($o_num && !empty($this->missingOld[$o_num])) ||
($n_num && !empty($this->missingNew[$n_num]))) {
$html[] = $context_not_available;
}
if ($o_num && $changeset) {
$o_id = ' id="C'.$changeset.'OL'.$o_num.'"';
} else {
$o_id = null;
}
if ($n_num && $changeset) {
$n_id = ' id="C'.$changeset.'NL'.$n_num.'"';
} else {
$n_id = null;
}
// NOTE: The Javascript is sensitive to whitespace changes in this
// block!
$html[] =
'<tr>'.
'<th'.$o_id.'>'.$o_num.'</th>'.
'<td'.$o_attr.'>'.$o_text.'</td>'.
'<th'.$n_id.'>'.$n_num.'</th>'.
'<td'.$n_attr.'>'.$n_text.'</td>'.
'</tr>';
if ($context_not_available && ($ii == $rows - 1)) {
$html[] = $context_not_available;
}
if ($o_num && isset($old_comments[$o_num])) {
foreach ($old_comments[$o_num] as $comment) {
$xhp = $this->renderInlineComment($comment);
$html[] =
'<tr class="inline"><th /><td>'.
$xhp.
'</td><th /><td /></tr>';
}
}
if ($n_num && isset($new_comments[$n_num])) {
foreach ($new_comments[$n_num] as $comment) {
$xhp = $this->renderInlineComment($comment);
$html[] =
'<tr class="inline"><th /><td /><th /><td>'.
$xhp.
'</td></tr>';
}
}
}
return implode('', $html);
}
private function renderInlineComment(DifferentialInlineComment $comment) {
$user = $this->user;
$edit = $user &&
($comment->getAuthorPHID() == $user->getPHID()) &&
(!$comment->getCommentID());
$on_right = $this->isCommentInNewFile($comment);
return id(new DifferentialInlineCommentView())
->setInlineComment($comment)
->setOnRight($on_right)
->setHandles($this->handles)
->setMarkupEngine($this->markupEngine)
->setEditable($edit)
->render();
}
protected function renderPropertyChangeHeader($changeset) {
$old = $changeset->getOldProperties();
$new = $changeset->getNewProperties();
if ($old === $new) {
return null;
}
if ($changeset->getChangeType() == DifferentialChangeType::TYPE_ADD &&
$new == array('unix:filemode' => '100644')) {
return null;
}
if ($changeset->getChangeType() == DifferentialChangeType::TYPE_DELETE &&
$old == array('unix:filemode' => '100644')) {
return null;
}
return null;
/*
TODO
$table = <table class="differential-property-table" />;
$table->appendChild(
<tr class="property-table-header">
<th>Property Changes</th>
<td class="oval">Old Value</td>
<td class="nval">New Value</td>
</tr>);
$keys = array_keys($old + $new);
sort($keys);
foreach ($keys as $key) {
$oval = idx($old, $key);
$nval = idx($new, $key);
if ($oval !== $nval) {
if ($oval === null) {
$oval = <em>null</em>;
}
if ($nval === null) {
$nval = <em>null</em>;
}
$table->appendChild(
<tr>
<th>{$key}</th>
<td class="oval">{$oval}</td>
<td class="nval">{$nval}</td>
</tr>);
}
}
return $table;
*/
}
protected function renderChangesetTable($changeset, $contents) {
$props = $this->renderPropertyChangeHeader($this->changeset);
$table = null;
if ($contents) {
$table =
- '<table class="differential-diff remarkup-code">'.
+ '<table class="differential-diff remarkup-code PhabricatorMonospaced">'.
$contents.
'</table>';
}
if (!$table && !$props) {
$notice = $this->renderChangeTypeHeader($this->changeset, true);
} else {
$notice = $this->renderChangeTypeHeader($this->changeset, false);
}
return implode(
"\n",
array(
$notice,
$props,
$table,
));
}
protected function renderChangeTypeHeader($changeset, $force) {
static $articles = array(
DifferentialChangeType::FILE_IMAGE => 'an',
);
static $files = array(
DifferentialChangeType::FILE_TEXT => 'file',
DifferentialChangeType::FILE_IMAGE => 'image',
DifferentialChangeType::FILE_DIRECTORY => 'directory',
DifferentialChangeType::FILE_BINARY => 'binary file',
DifferentialChangeType::FILE_SYMLINK => 'symlink',
);
static $changes = array(
DifferentialChangeType::TYPE_ADD => 'added',
DifferentialChangeType::TYPE_CHANGE => 'changed',
DifferentialChangeType::TYPE_DELETE => 'deleted',
DifferentialChangeType::TYPE_MOVE_HERE => 'moved from',
DifferentialChangeType::TYPE_COPY_HERE => 'copied from',
DifferentialChangeType::TYPE_MOVE_AWAY => 'moved to',
DifferentialChangeType::TYPE_COPY_AWAY => 'copied to',
DifferentialChangeType::TYPE_MULTICOPY
=> 'deleted after being copied to',
);
$change = $changeset->getChangeType();
$file = $changeset->getFileType();
$message = null;
if ($change == DifferentialChangeType::TYPE_CHANGE &&
$file == DifferentialChangeType::FILE_TEXT) {
if ($force) {
// We have to force something to render because there were no changes
// of other kinds.
$message = "This {$files[$file]} was not modified.";
} else {
// Default case of changes to a text file, no metadata.
return null;
}
} else {
$verb = idx($changes, $change, 'changed');
switch ($change) {
default:
$message = "This {$files[$file]} was <strong>{$verb}</strong>.";
break;
case DifferentialChangeType::TYPE_MOVE_HERE:
case DifferentialChangeType::TYPE_COPY_HERE:
$message =
"This {$files[$file]} was {$verb} ".
"<strong>{$changeset->getOldFile()}</strong>.";
break;
case DifferentialChangeType::TYPE_MOVE_AWAY:
case DifferentialChangeType::TYPE_COPY_AWAY:
case DifferentialChangeType::TYPE_MULTICOPY:
$paths = $changeset->getAwayPaths();
if (count($paths) > 1) {
$message =
"This {$files[$file]} was {$verb}: ".
"<strong>".implode(', ', $paths)."</strong>.";
} else {
$message =
"This {$files[$file]} was {$verb} ".
"<strong>".reset($paths)."</strong>.";
}
break;
case DifferentialChangeType::TYPE_CHANGE:
$message = "This is ".idx($articles, $file, 'a')." {$files[$file]}.";
break;
}
}
return
'<div class="differential-meta-notice">'.
$message.
'</div>';
}
public function renderForEmail() {
$ret = '';
$min = min(count($this->old), count($this->new));
for ($i = 0; $i < $min; $i++) {
$o = $this->old[$i];
$n = $this->new[$i];
if (!isset($this->visible[$i])) {
continue;
}
if ($o['line'] && $n['line']) {
// It is quite possible there are better ways to achieve this. For
// example, "white-space: pre;" can do a better job, WERE IT NOT for
// broken email clients like OWA which use newlines to do weird
// wrapping. So dont give them newlines.
if (isset($this->intra[$i])) {
$ret .= sprintf(
"<font color=\"red\">-&nbsp;%s</font><br/>",
str_replace(" ", "&nbsp;", phutil_escape_html($o['text']))
);
$ret .= sprintf(
"<font color=\"green\">+&nbsp;%s</font><br/>",
str_replace(" ", "&nbsp;", phutil_escape_html($n['text']))
);
} else {
$ret .= sprintf("&nbsp;&nbsp;%s<br/>",
str_replace(" ", "&nbsp;", phutil_escape_html($n['text']))
);
}
} else if ($o['line'] && !$n['line']) {
$ret .= sprintf(
"<font color=\"red\">-&nbsp;%s</font><br/>",
str_replace(" ", "&nbsp;", phutil_escape_html($o['text']))
);
} else {
$ret .= sprintf(
"<font color=\"green\">+&nbsp;%s</font><br/>",
str_replace(" ", "&nbsp;", phutil_escape_html($n['text']))
);
}
}
return $ret;
}
}
diff --git a/src/applications/diffusion/controller/file/DiffusionBrowseFileController.php b/src/applications/diffusion/controller/file/DiffusionBrowseFileController.php
index b7f4a161cd..a6f5ca6322 100644
--- a/src/applications/diffusion/controller/file/DiffusionBrowseFileController.php
+++ b/src/applications/diffusion/controller/file/DiffusionBrowseFileController.php
@@ -1,231 +1,231 @@
<?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 DiffusionBrowseFileController extends DiffusionController {
public function processRequest() {
// Build the view selection form.
$select_map = array(
'highlighted' => 'View as Highlighted Text',
'blame' => 'View as Highlighted Text with Blame',
'plain' => 'View as Plain Text',
'plainblame' => 'View as Plain Text with Blame',
);
$request = $this->getRequest();
$selected = $request->getStr('view');
$select = '<select name="view">';
foreach ($select_map as $k => $v) {
$option = phutil_render_tag(
'option',
array(
'value' => $k,
'selected' => ($k == $selected) ? 'selected' : null,
),
phutil_escape_html($v));
$select .= $option;
}
$select .= '</select>';
$view_select_panel = new AphrontPanelView();
$view_select_form = phutil_render_tag(
'form',
array(
'action' => $request->getRequestURI(),
'method' => 'get',
'style' => 'display: inline',
),
$select.
'<button>view</button>');
$view_select_panel->appendChild($view_select_form);
// Build the content of the file.
$corpus = $this->buildCorpus($selected);
// Render the page.
$content = array();
$content[] = $this->buildCrumbs(
array(
'branch' => true,
'path' => true,
'view' => 'browse',
));
$content[] = $view_select_panel;
$content[] = $corpus;
$nav = $this->buildSideNav('browse', true);
$nav->appendChild($content);
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Browse',
));
}
public static function renderRevision(
DiffusionRequest $drequest,
$revision) {
$callsign = $drequest->getCallsign();
$name = 'r'.$callsign.$revision;
return phutil_render_tag(
'a',
array(
'href' => '/'.$name,
),
$name
);
}
private function buildCorpus($selected) {
$blame = ($selected == 'blame' || $selected == 'plainblame');
$file_query = DiffusionFileContentQuery::newFromDiffusionRequest(
$this->diffusionRequest);
$file_query->setNeedsBlame($blame);
// TODO: image
// TODO: blame of blame.
switch ($selected) {
case 'plain':
case 'plainblame':
$style =
"margin: 1em 2em; width: 90%; height: 80em; font-family: monospace";
$corpus = phutil_render_tag(
'textarea',
array(
'style' => $style,
),
phutil_escape_html($file_query->getRawData()));
break;
case 'highlighted':
case 'blame':
default:
require_celerity_resource('syntax-highlighting-css');
require_celerity_resource('diffusion-source-css');
list($data, $blamedata, $revs) = $file_query->getTokenizedData();
$drequest = $this->getDiffusionRequest();
$path = $drequest->getPath();
$highlightEngine = new PhutilDefaultSyntaxHighlighterEngine();
$data = $highlightEngine->highlightSource($path, $data);
$data = explode("\n", rtrim($data));
$rows = $this->buildDisplayRows($data, $blame, $blamedata, $drequest,
$revs);
$corpus_table = phutil_render_tag(
'table',
array(
- 'class' => "diffusion-source remarkup-code",
+ 'class' => "diffusion-source remarkup-code PhabricatorMonospaced",
),
implode("\n", $rows));
$corpus = phutil_render_tag(
'div',
array(
'style' => 'padding: 0pt 2em;',
),
$corpus_table);
break;
}
return $corpus;
}
private static function buildDisplayRows($data, $blame, $blamedata, $drequest,
$revs) {
$last = null;
$color = null;
$rows = array();
$n = 1;
foreach ($data as $k => $line) {
if ($blame) {
if ($last == $blamedata[$k][0]) {
$blameinfo =
'<th style="background: '.$color.'; width: 9em;"></th>'.
'<th style="background: '.$color.'"></th>';
} else {
switch ($drequest->getRepository()->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
// TODO: better color for git.
$color = '#dddddd';
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$color = sprintf(
'#%02xee%02x',
$revs[$blamedata[$k][0]],
$revs[$blamedata[$k][0]]);
break;
default:
throw new Exception('repository type not supported');
}
$revision_link = self::renderRevision(
$drequest,
$blamedata[$k][0]);
$author_link = $blamedata[$k][1];
$blameinfo =
'<th style="background: '.$color.
'; width: 9em;">'.$revision_link.'</th>'.
'<th style="background: '.$color.
'; font-weight: normal; color: #333;">'.$author_link.'</th>';
$last = $blamedata[$k][0];
}
} else {
$blameinfo = null;
}
if ($n == $drequest->getLine()) {
$tr = '<tr style="background: #ffff00;">';
$targ = '<a id="scroll_target"></a>';
Javelin::initBehavior('diffusion-jump-to',
array('target' => 'scroll_target'));
} else {
$tr = '<tr>';
$targ = null;
}
$uri_path = $drequest->getUriPath();
$uri_rev = $drequest->getCommit();
$l = phutil_render_tag(
'a',
array(
'href' => $uri_path.';'.$uri_rev.'$'.$n,
),
$n);
$rows[] = $tr.$blameinfo.'<th>'.$l.'</th><td>'.$targ.$line.'</td></tr>';
++$n;
}
return $rows;
}
}
diff --git a/src/applications/people/storage/preferences/PhabricatorUserPreferences.php b/src/applications/people/storage/preferences/PhabricatorUserPreferences.php
new file mode 100644
index 0000000000..42438a3ada
--- /dev/null
+++ b/src/applications/people/storage/preferences/PhabricatorUserPreferences.php
@@ -0,0 +1,39 @@
+<?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 PhabricatorUserPreferences extends PhabricatorUserDAO {
+
+ const PREFERENCE_MONOSPACED = 'monospaced';
+ const PREFERENCE_TITLES = 'titles';
+
+ protected $userPHID;
+ protected $preferences;
+
+ public function getConfiguration() {
+ return array(
+ self::CONFIG_SERIALIZATION => array(
+ 'preferences' => self::SERIALIZATION_JSON,
+ ),
+ self::CONFIG_TIMESTAMPS => false,
+ ) + parent::getConfiguration();
+ }
+
+ public function getPreference($key) {
+ return $this->getPreferences()[$key];
+ }
+}
diff --git a/src/applications/people/storage/preferences/__init__.php b/src/applications/people/storage/preferences/__init__.php
new file mode 100644
index 0000000000..642ad05c2c
--- /dev/null
+++ b/src/applications/people/storage/preferences/__init__.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'applications/people/storage/base');
+
+
+phutil_require_source('PhabricatorUserPreferences.php');
diff --git a/src/applications/people/storage/user/PhabricatorUser.php b/src/applications/people/storage/user/PhabricatorUser.php
index bccb7d565d..ed32f92ef8 100644
--- a/src/applications/people/storage/user/PhabricatorUser.php
+++ b/src/applications/people/storage/user/PhabricatorUser.php
@@ -1,164 +1,190 @@
<?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 PhabricatorUser extends PhabricatorUserDAO {
const SESSION_TABLE = 'phabricator_session';
protected $phid;
protected $userName;
protected $realName;
protected $email;
protected $passwordSalt;
protected $passwordHash;
protected $profileImagePHID;
protected $consoleEnabled = 0;
protected $consoleVisible = 0;
protected $consoleTab = '';
protected $conduitCertificate;
+ protected $preferences = null;
+
public function getProfileImagePHID() {
return nonempty(
$this->profileImagePHID,
PhabricatorEnv::getEnvConfig('user.default-profile-image-phid'));
}
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorPHIDConstants::PHID_TYPE_USER);
}
public function setPassword($password) {
$this->setPasswordSalt(md5(mt_rand()));
$hash = $this->hashPassword($password);
$this->setPasswordHash($hash);
return $this;
}
public function save() {
if (!$this->conduitCertificate) {
$this->conduitCertificate = $this->generateConduitCertificate();
}
return parent::save();
}
private function generateConduitCertificate() {
$entropy = Filesystem::readRandomBytes(256);
$entropy = base64_encode($entropy);
$entropy = substr($entropy, 0, 255);
return $entropy;
}
public function comparePassword($password) {
$password = $this->hashPassword($password);
return ($password === $this->getPasswordHash());
}
private function hashPassword($password) {
$password = $this->getUsername().
$password.
$this->getPHID().
$this->getPasswordSalt();
for ($ii = 0; $ii < 1000; $ii++) {
$password = md5($password);
}
return $password;
}
const CSRF_CYCLE_FREQUENCY = 3600;
const CSRF_TOKEN_LENGTH = 16;
const EMAIL_CYCLE_FREQUENCY = 86400;
const EMAIL_TOKEN_LENGTH = 24;
public function getCSRFToken($offset = 0) {
return $this->generateToken(
time() + (self::CSRF_CYCLE_FREQUENCY * $offset),
self::CSRF_CYCLE_FREQUENCY,
PhabricatorEnv::getEnvConfig('phabricator.csrf-key'),
self::CSRF_TOKEN_LENGTH);
}
public function validateCSRFToken($token) {
for ($ii = -1; $ii <= 1; $ii++) {
$valid = $this->getCSRFToken($ii);
if ($token == $valid) {
return true;
}
}
return false;
}
private function generateToken($epoch, $frequency, $key, $len) {
$time_block = floor($epoch / $frequency);
$vec = $this->getPHID().$this->passwordHash.$key.$time_block;
return substr(sha1($vec), 0, $len);
}
public function establishSession($session_type) {
$conn_w = $this->establishConnection('w');
$entropy = Filesystem::readRandomBytes(20);
$session_key = sha1($entropy);
queryfx(
$conn_w,
'INSERT INTO %T '.
'(userPHID, type, sessionKey, sessionStart)'.
' VALUES '.
'(%s, %s, %s, UNIX_TIMESTAMP()) '.
'ON DUPLICATE KEY UPDATE '.
'sessionKey = VALUES(sessionKey), '.
'sessionStart = VALUES(sessionStart)',
self::SESSION_TABLE,
$this->getPHID(),
$session_type,
$session_key);
$this->sessionKey = $session_key;
return $session_key;
}
public function generateEmailToken($offset = 0) {
return $this->generateToken(
time() + ($offset * self::EMAIL_CYCLE_FREQUENCY),
self::EMAIL_CYCLE_FREQUENCY,
PhabricatorEnv::getEnvConfig('phabricator.csrf-key').$this->getEmail(),
self::EMAIL_TOKEN_LENGTH);
}
public function validateEmailToken($token) {
for ($ii = -1; $ii <= 1; $ii++) {
$valid = $this->generateEmailToken($ii);
if ($token == $valid) {
return true;
}
}
return false;
}
+ public function loadPreferences() {
+ if ($this->preferences) {
+ return $this->preferences;
+ }
+
+ $preferences = id(new PhabricatorUserPreferences())->loadOneWhere(
+ 'userPHID = %s',
+ $this->getPHID());
+
+ if (!$preferences) {
+ $preferences = new PhabricatorUserPreferences();
+ $preferences->setUserPHID($this->getPHID());
+
+ $default_dict = array(
+ PhabricatorUserPreferences::PREFERENCE_TITLES => 'glyph',
+ PhabricatorUserPreferences::PREFERENCE_MONOSPACED => '');
+
+ $preferences->setPreferences($default_dict);
+ }
+
+ $this->preferences = $preferences;
+ return $preferences;
+ }
+
}
diff --git a/src/applications/people/storage/user/__init__.php b/src/applications/people/storage/user/__init__.php
index efe13374b1..8a7b435549 100644
--- a/src/applications/people/storage/user/__init__.php
+++ b/src/applications/people/storage/user/__init__.php
@@ -1,19 +1,20 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/people/storage/base');
+phutil_require_module('phabricator', 'applications/people/storage/preferences');
phutil_require_module('phabricator', 'applications/phid/constants');
phutil_require_module('phabricator', 'applications/phid/storage/phid');
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phabricator', 'storage/queryfx');
phutil_require_module('phutil', 'filesystem');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorUser.php');
diff --git a/src/applications/preferences/controller/base/PhabricatorPreferencesController.php b/src/applications/preferences/controller/base/PhabricatorPreferencesController.php
new file mode 100644
index 0000000000..af203647d1
--- /dev/null
+++ b/src/applications/preferences/controller/base/PhabricatorPreferencesController.php
@@ -0,0 +1,34 @@
+<?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.
+ */
+
+abstract class PhabricatorPreferencesController extends PhabricatorController {
+
+ public function buildStandardPageResponse($view, array $data) {
+
+ $page = $this->buildStandardPageView();
+
+ $page->setApplicationName('Preferences');
+ $page->setBaseURI('/preferences/');
+ $page->setTitle(idx($data, 'title'));
+ $page->setGlyph("\xE2\x9A\x92");
+ $page->appendChild($view);
+
+ $response = new AphrontWebpageResponse();
+ return $response->setContent($page->render());
+ }
+}
diff --git a/src/applications/preferences/controller/base/__init__.php b/src/applications/preferences/controller/base/__init__.php
new file mode 100644
index 0000000000..6b30c6011c
--- /dev/null
+++ b/src/applications/preferences/controller/base/__init__.php
@@ -0,0 +1,15 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'aphront/response/webpage');
+phutil_require_module('phabricator', 'applications/base/controller/base');
+
+phutil_require_module('phutil', 'utils');
+
+
+phutil_require_source('PhabricatorPreferencesController.php');
diff --git a/src/applications/preferences/controller/edit/PhabricatorEditPreferencesController.php b/src/applications/preferences/controller/edit/PhabricatorEditPreferencesController.php
new file mode 100644
index 0000000000..cf10592b58
--- /dev/null
+++ b/src/applications/preferences/controller/edit/PhabricatorEditPreferencesController.php
@@ -0,0 +1,111 @@
+<?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 PhabricatorEditPreferencesController
+extends PhabricatorPreferencesController {
+
+ public function processRequest() {
+
+ $request = $this->getRequest();
+ $user = $request->getUser();
+ $preferences = $user->loadPreferences();
+
+ if ($request->isFormPost()) {
+ $monospaced = $request->getStr(
+ PhabricatorUserPreferences::PREFERENCE_MONOSPACED);
+
+ // Prevent the user from doing stupid things.
+ $monospaced = preg_replace('/[^a-z0-9 ,"]+/i', '', $monospaced);
+
+ $pref_dict = array(
+ PhabricatorUserPreferences::PREFERENCE_TITLES =>
+ $request->getStr(PhabricatorUserPreferences::PREFERENCE_TITLES),
+ PhabricatorUserPreferences::PREFERENCE_MONOSPACED =>
+ $monospaced);
+
+ $preferences->setPreferences($pref_dict);
+ $preferences->save();
+ return id(new AphrontRedirectResponse())
+ ->setURI('/preferences/?saved=true');
+ }
+
+ $example_string = <<<EXAMPLE
+// This is what your monospaced font currently looks like.
+function helloWorld() {
+ alert("Hello world!");
+}
+EXAMPLE;
+
+ $form = id(new AphrontFormView())
+ ->setAction('/preferences/')
+ ->setUser($user)
+ ->appendChild(
+ id(new AphrontFormSelectControl())
+ ->setLabel('Page Titles')
+ ->setName(PhabricatorUserPreferences::PREFERENCE_TITLES)
+ ->setValue($preferences->getPreference(
+ PhabricatorUserPreferences::PREFERENCE_TITLES))
+ ->setOptions(
+ array(
+ 'glyph' =>
+ "In page titles, show Tool names as unicode glyphs: \xE2\x9A\x99",
+ 'text' =>
+ 'In page titles, show Tool names as plain text: [Differential]',
+ )))
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setLabel('Monospaced Font')
+ ->setName(PhabricatorUserPreferences::PREFERENCE_MONOSPACED)
+ ->setCaption(
+ 'Overrides default fonts in tools like Differential. '.
+ '(Default: 10px "Menlo", "Consolas", "Monaco", '.
+ 'monospace)')
+ ->setValue($preferences->getPreference(
+ PhabricatorUserPreferences::PREFERENCE_MONOSPACED)))
+ ->appendChild(
+ id(new AphrontFormMarkupControl())
+ ->setValue(
+ '<pre class="PhabricatorMonospaced">'.
+ phutil_escape_html($example_string).
+ '</pre>'))
+ ->appendChild(
+ id(new AphrontFormSubmitControl())
+ ->setValue('Save Preferences'));
+
+ $panel = new AphrontPanelView();
+ $panel->appendChild($form);
+
+ $error_view = null;
+ if ($request->getStr('saved') === 'true') {
+ $error_view = id(new AphrontErrorView())
+ ->setTitle('Preferences Saved')
+ ->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
+ ->setErrors(array('Your preferences have been saved.'));
+ }
+
+ return $this->buildStandardPageResponse(
+ array(
+ $error_view,
+ $panel,
+ ),
+ array(
+ 'title' => 'Edit Preferences',
+ ));
+
+ }
+}
+
diff --git a/src/applications/preferences/controller/edit/__init__.php b/src/applications/preferences/controller/edit/__init__.php
new file mode 100644
index 0000000000..d91533583a
--- /dev/null
+++ b/src/applications/preferences/controller/edit/__init__.php
@@ -0,0 +1,20 @@
+<?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/people/storage/preferences');
+phutil_require_module('phabricator', 'applications/preferences/controller/base');
+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('PhabricatorEditPreferencesController.php');
diff --git a/src/view/page/standard/PhabricatorStandardPageView.php b/src/view/page/standard/PhabricatorStandardPageView.php
index 8e9103bced..b2c739b22a 100755
--- a/src/view/page/standard/PhabricatorStandardPageView.php
+++ b/src/view/page/standard/PhabricatorStandardPageView.php
@@ -1,267 +1,300 @@
<?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 PhabricatorStandardPageView extends AphrontPageView {
private $baseURI;
private $applicationName;
private $tabs = array();
private $selectedTab;
private $glyph;
private $bodyContent;
private $request;
public function setRequest($request) {
$this->request = $request;
return $this;
}
public function getRequest() {
return $this->request;
}
public function setApplicationName($application_name) {
$this->applicationName = $application_name;
return $this;
}
public function getApplicationName() {
return $this->applicationName;
}
public function setBaseURI($base_uri) {
$this->baseURI = $base_uri;
return $this;
}
public function getBaseURI() {
return $this->baseURI;
}
public function setTabs(array $tabs, $selected_tab) {
$this->tabs = $tabs;
$this->selectedTab = $selected_tab;
return $this;
}
public function getTitle() {
- return $this->getGlyph().' '.parent::getTitle();
+ $use_glyph = true;
+ $request = $this->getRequest();
+ if ($request) {
+ $user = $request->getUser();
+ if ($user && $user->loadPreferences()->getPreference(
+ PhabricatorUserPreferences::PREFERENCE_TITLES) !== 'glyph') {
+ $use_glyph = false;
+ }
+ }
+
+ return ($use_glyph ?
+ $this->getGlyph() : '['.$this->getApplicationName().']').
+ ' '.parent::getTitle();
}
protected function willRenderPage() {
if (!$this->getRequest()) {
throw new Exception(
"You must set the Request to render a PhabricatorStandardPageView.");
}
$console = $this->getRequest()->getApplicationConfiguration()->getConsole();
require_celerity_resource('phabricator-core-css');
require_celerity_resource('phabricator-core-buttons-css');
require_celerity_resource('phabricator-standard-page-view');
require_celerity_resource('javelin-lib-dev');
require_celerity_resource('javelin-workflow-dev');
Javelin::initBehavior('workflow', array());
if ($console) {
require_celerity_resource('aphront-dark-console-css');
Javelin::initBehavior(
'dark-console',
array(
'uri' => '/~/',
));
}
$this->bodyContent = $this->renderChildren();
}
protected function getHead() {
$response = CelerityAPI::getStaticResourceResponse();
- return
+ $head =
'<script type="text/javascript">'.
'(top != self) && top.location.replace(self.location.href);'.
'window.__DEV__=1;'.
'</script>'.
$response->renderResourcesOfType('css').
'<script type="text/javascript" src="/rsrc/js/javelin/init.dev.js">'.
'</script>';
+
+ $request = $this->getRequest();
+ if ($request) {
+ $user = $request->getUser();
+ if ($user) {
+ $monospaced = $user->loadPreferences()->getPreference(
+ PhabricatorUserPreferences::PREFERENCE_MONOSPACED
+ );
+
+ if (strlen($monospaced)) {
+ $head .=
+ '<style type="text/css">'.
+ '.PhabricatorMonospaced { font: '.
+ $monospaced.
+ ' !important; }'.
+ '</style>';
+ }
+ }
+ }
+
+ return $head;
}
public function setGlyph($glyph) {
$this->glyph = $glyph;
return $this;
}
public function getGlyph() {
return $this->glyph;
}
protected function willSendResponse($response) {
$console = $this->getRequest()->getApplicationConfiguration()->getConsole();
if ($console) {
$response = str_replace(
'<darkconsole />',
$console->render($this->getRequest()),
$response);
}
return $response;
}
protected function getBody() {
$console = $this->getRequest()->getApplicationConfiguration()->getConsole();
$tabs = array();
foreach ($this->tabs as $name => $tab) {
$tab_markup = phutil_render_tag(
'a',
array(
'href' => idx($tab, 'href'),
),
phutil_escape_html(idx($tab, 'name')));
$tab_markup = phutil_render_tag(
'td',
array(
'class' => ($name == $this->selectedTab)
? 'phabricator-selected-tab'
: null,
),
$tab_markup);
$tabs[] = $tab_markup;
}
$tabs = implode('', $tabs);
$login_stuff = null;
$request = $this->getRequest();
$user = null;
if ($request) {
$user = $request->getUser();
// NOTE: user may not be set here if we caught an exception early
// in the execution workflow.
if ($user && $user->getPHID()) {
$login_stuff =
'Logged in as '.phutil_render_tag(
'a',
array(
'href' => '/p/'.$user->getUsername().'/',
),
phutil_escape_html($user->getUsername())).
' &middot; '.
'<a href="/settings/">Settings</a>'.
' &middot; '.
phabricator_render_form(
$user,
array(
'action' => '/search/',
'method' => 'post',
'style' => 'display: inline',
),
'<input type="text" name="query" />'.
'<button>Search</button>');
}
}
$foot_links = array();
$version = PhabricatorEnv::getEnvConfig('phabricator.version');
$foot_links[] = phutil_escape_html('Phabricator '.$version);
if (PhabricatorEnv::getEnvConfig('darkconsole.enabled') &&
!PhabricatorEnv::getEnvConfig('darkconsole.always-on')) {
if ($console) {
$link = javelin_render_tag(
'a',
array(
'href' => '/~/',
'sigil' => 'workflow',
),
'Disable DarkConsole');
} else {
$link = javelin_render_tag(
'a',
array(
'href' => '/~/',
'sigil' => 'workflow',
),
'Enable DarkConsole');
}
$foot_links[] = $link;
}
if ($user && $user->getPHID()) {
// This ends up very early in tab order at the top of the page and there's
// a bunch of junk up there anyway, just shove it down here.
$foot_links[] = phabricator_render_form(
$user,
array(
'action' => '/logout/',
'method' => 'post',
'style' => 'display: inline',
),
'<button class="link">Logout</button>');
}
$foot_links = implode(' &middot; ', $foot_links);
return
($console ? '<darkconsole />' : null).
'<div class="phabricator-standard-page">'.
'<div class="phabricator-standard-header">'.
'<div class="phabricator-login-details">'.
$login_stuff.
'</div>'.
'<table class="phabricator-primary-navigation">'.
'<tr>'.
'<th class="phabricator-logo">'.
'<a href="/">'.
"Phabricat\xE2\x9A\x99r".
'</a> '.
'</th>'.
'<th>'.
phutil_render_tag(
'a',
array(
'href' => $this->getBaseURI(),
'class' => 'phabricator-head-appname',
),
phutil_escape_html($this->getApplicationName())).
'</th>'.
$tabs.
'</tr>'.
'</table>'.
'</div>'.
$this->bodyContent.
'<div style="clear: both;"></div>'.
'</div>'.
'<div class="phabricator-page-foot">'.
$foot_links.
'</div>';
}
protected function getTail() {
$response = CelerityAPI::getStaticResourceResponse();
return
$response->renderResourcesOfType('js').
$response->renderHTMLFooter();
}
}
diff --git a/src/view/page/standard/__init__.php b/src/view/page/standard/__init__.php
index 5b4e699da5..7db98f4535 100644
--- a/src/view/page/standard/__init__.php
+++ b/src/view/page/standard/__init__.php
@@ -1,19 +1,20 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
+phutil_require_module('phabricator', 'applications/people/storage/preferences');
phutil_require_module('phabricator', 'infrastructure/celerity/api');
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phabricator', 'infrastructure/javelin/api');
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
phutil_require_module('phabricator', 'view/page/base');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorStandardPageView.php');

File Metadata

Mime Type
text/x-diff
Expires
Tue, Dec 2, 8:45 AM (1 d, 1 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
431754
Default Alt Text
(134 KB)

Event Timeline