Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php
index 652e0d697f..9129bbf39d 100644
--- a/src/__celerity_resource_map__.php
+++ b/src/__celerity_resource_map__.php
@@ -1,197 +1,213 @@
<?php
/**
* This file is automatically generated. Use 'celerity_mapper.php' to rebuild
* it.
* @generated
*/
celerity_register_resource_map(array(
'aphront-dialog-view-css' =>
array(
'path' => '/res/771b987d/rsrc/css/aphront/dialog-view.css',
'type' => 'css',
'requires' =>
array(
),
),
'aphront-form-view-css' =>
array(
'path' => '/res/785ac1c6/rsrc/css/aphront/form-view.css',
'type' => 'css',
'requires' =>
array(
),
),
'aphront-panel-view-css' =>
array(
'path' => '/res/fe62e634/rsrc/css/aphront/panel-view.css',
'type' => 'css',
'requires' =>
array(
),
),
'aphront-side-nav-view-css' =>
array(
'path' => '/res/0fc0545c/rsrc/css/aphront/side-nav-view.css',
'type' => 'css',
'requires' =>
array(
),
),
'aphront-table-view-css' =>
array(
'path' => '/res/52b0191f/rsrc/css/aphront/table-view.css',
'type' => 'css',
'requires' =>
array(
),
),
'aphront-tokenizer-control-css' =>
array(
'path' => '/res/a3d23074/rsrc/css/aphront/tokenizer.css',
'type' => 'css',
'requires' =>
array(
0 => 'aphront-typeahead-control-css',
),
),
'aphront-typeahead-control-css' =>
array(
'path' => '/res/928df9f0/rsrc/css/aphront/typeahead.css',
'type' => 'css',
'requires' =>
array(
),
),
'phabricator-standard-page-view' =>
array(
'path' => '/res/1f93ada7/rsrc/css/application/base/standard-page-view.css',
'type' => 'css',
'requires' =>
array(
),
),
'differential-changeset-view-css' =>
array(
'path' => '/res/658d181a/rsrc/css/application/differential/changeset-view.css',
'type' => 'css',
'requires' =>
array(
),
),
'differential-core-view-css' =>
array(
- 'path' => '/res/f750b85d/rsrc/css/application/differential/core.css',
+ 'path' => '/res/525d1a12/rsrc/css/application/differential/core.css',
+ 'type' => 'css',
+ 'requires' =>
+ array(
+ ),
+ ),
+ 'differential-revision-detail-css' =>
+ array(
+ 'path' => '/res/11a36dad/rsrc/css/application/differential/revision-detail.css',
+ 'type' => 'css',
+ 'requires' =>
+ array(
+ ),
+ ),
+ 'differential-revision-history-css' =>
+ array(
+ 'path' => '/res/755f3da3/rsrc/css/application/differential/revision-history.css',
'type' => 'css',
'requires' =>
array(
),
),
'differential-table-of-contents-css' =>
array(
- 'path' => '/res/ebf6641c/rsrc/css/application/differential/table-of-contents.css',
+ 'path' => '/res/a4a7b2b5/rsrc/css/application/differential/table-of-contents.css',
'type' => 'css',
'requires' =>
array(
),
),
'phabricator-directory-css' =>
array(
'path' => '/res/6a000601/rsrc/css/application/directory/phabricator-directory.css',
'type' => 'css',
'requires' =>
array(
),
),
'phabricator-core-buttons-css' =>
array(
'path' => '/res/6e348ba4/rsrc/css/core/buttons.css',
'type' => 'css',
'requires' =>
array(
),
),
'phabricator-core-css' =>
array(
'path' => '/res/39ce37c2/rsrc/css/core/core.css',
'type' => 'css',
'requires' =>
array(
),
),
'syntax-highlighting-css' =>
array(
'path' => '/res/fb673ece/rsrc/css/core/syntax.css',
'type' => 'css',
'requires' =>
array(
),
),
'javelin-behavior-aphront-basic-tokenizer' =>
array(
'path' => '/res/8317d761/rsrc/js/application/core/behavior-tokenizer.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-lib-dev',
),
),
'javelin-behavior-differential-populate' =>
array(
'path' => '/res/9982573c/rsrc/js/application/differential/behavior-populate.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-lib-dev',
),
),
'javelin-init-dev' =>
array(
'path' => '/res/c57a9e89/rsrc/js/javelin/init.dev.js',
'type' => 'js',
'requires' =>
array(
),
),
'javelin-init-prod' =>
array(
'path' => '/res/f0172c54/rsrc/js/javelin/init.min.js',
'type' => 'js',
'requires' =>
array(
),
),
'javelin-lib-dev' =>
array(
'path' => '/res/3e747182/rsrc/js/javelin/javelin.dev.js',
'type' => 'js',
'requires' =>
array(
),
),
'javelin-lib-prod' =>
array(
'path' => '/res/9438670e/rsrc/js/javelin/javelin.min.js',
'type' => 'js',
'requires' =>
array(
),
),
'javelin-typeahead-dev' =>
array(
'path' => '/res/c81c0f01/rsrc/js/javelin/typeahead.dev.js',
'type' => 'js',
'requires' =>
array(
),
),
'javelin-typeahead-prod' =>
array(
'path' => '/res/1da2d984/rsrc/js/javelin/typeahead.min.js',
'type' => 'js',
'requires' =>
array(
),
),
));
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index cca9234785..94cf87f8f7 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1,298 +1,304 @@
<?php
/**
* This file is automatically generated. Use 'phutil_mapper.php' to rebuild it.
* @generated
*/
phutil_register_library_map(array(
'class' =>
array(
'Aphront404Response' => 'aphront/response/404',
'AphrontAjaxResponse' => 'aphront/response/ajax',
'AphrontApplicationConfiguration' => 'aphront/applicationconfiguration',
'AphrontController' => 'aphront/controller',
'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',
'AphrontFormFileControl' => 'view/form/control/file',
'AphrontFormMarkupControl' => 'view/form/control/markup',
'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',
'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',
'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',
'AphrontResponse' => 'aphront/response/base',
'AphrontSideNavView' => 'view/layout/sidenav',
'AphrontTableView' => 'view/control/table',
'AphrontURIMapper' => 'aphront/mapper',
'AphrontView' => 'view/base',
'AphrontWebpageResponse' => 'aphront/response/webpage',
'CelerityAPI' => 'infratructure/celerity/api',
'CelerityResourceController' => 'infratructure/celerity/controller',
'CelerityResourceMap' => 'infratructure/celerity/map',
'CelerityStaticResourceResponse' => 'infratructure/celerity/response',
'ConduitAPIMethod' => 'applications/conduit/method/base',
'ConduitAPIRequest' => 'applications/conduit/protocol/request',
'ConduitAPI_conduit_connect_Method' => 'applications/conduit/method/conduit/connect',
'ConduitAPI_differential_creatediff_Method' => 'applications/conduit/method/differential/creatediff',
'ConduitAPI_differential_setdiffproperty_Method' => 'applications/conduit/method/differential/setdiffproperty',
'ConduitAPI_file_upload_Method' => 'applications/conduit/method/file/upload',
'ConduitAPI_user_find_Method' => 'applications/conduit/method/user/find',
'ConduitException' => 'applications/conduit/protocol/exception',
'DifferentialAction' => 'applications/differential/constants/action',
'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',
'DifferentialController' => 'applications/differential/controller/base',
'DifferentialDAO' => 'applications/differential/storage/base',
'DifferentialDiff' => 'applications/differential/storage/diff',
'DifferentialDiffContentMail' => 'applications/differential/mail/diffcontent',
'DifferentialDiffProperty' => 'applications/differential/storage/diffproperty',
'DifferentialDiffTableOfContentsView' => 'applications/differential/view/difftableofcontents',
'DifferentialDiffViewController' => 'applications/differential/controller/diffview',
'DifferentialFeedbackMail' => 'applications/differential/mail/feedback',
'DifferentialHunk' => 'applications/differential/storage/hunk',
'DifferentialLintStatus' => 'applications/differential/constants/lintstatus',
'DifferentialMail' => 'applications/differential/mail/base',
'DifferentialNewDiffMail' => 'applications/differential/mail/newdiff',
'DifferentialReviewRequestMail' => 'applications/differential/mail/reviewrequest',
'DifferentialRevision' => 'applications/differential/storage/revision',
'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',
'DifferentialUnitStatus' => 'applications/differential/constants/unitstatus',
'Javelin' => 'infratructure/javelin/api',
'LiskDAO' => 'storage/lisk/dao',
'PhabricatorAuthController' => 'applications/auth/controlller/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',
'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',
'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',
'PhabricatorLiskDAO' => 'applications/base/storage/lisk',
'PhabricatorLoginController' => 'applications/auth/controlller/login',
'PhabricatorMailImplementationAdapter' => 'applications/metamta/adapter/base',
'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'applications/metamta/adapter/phpmailerlite',
'PhabricatorMetaMTAController' => 'applications/metamta/controller/base',
'PhabricatorMetaMTADAO' => 'applications/metamta/storage/base',
'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',
'PhabricatorObjectHandle' => 'applications/phid/handle',
'PhabricatorObjectHandleData' => 'applications/phid/handle/data',
'PhabricatorPHID' => 'applications/phid/storage/phid',
'PhabricatorPHIDAllocateController' => 'applications/phid/controller/allocate',
'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',
'PhabricatorStandardPageView' => 'view/page/standard',
'PhabricatorTypeaheadCommonDatasourceController' => 'applications/typeahead/controller/common',
'PhabricatorTypeaheadDatasourceController' => 'applications/typeahead/controller/base',
'PhabricatorUser' => 'applications/people/storage/user',
'PhabricatorUserDAO' => 'applications/people/storage/base',
),
'function' =>
array(
'_qsprintf_check_scalar_type' => 'storage/qsprintf',
'_qsprintf_check_type' => 'storage/qsprintf',
'celerity_generate_unique_node_id' => 'infratructure/celerity/api',
'celerity_register_resource_map' => 'infratructure/celerity/map',
'javelin_render_tag' => 'infratructure/javelin/markup',
'qsprintf' => 'storage/qsprintf',
'queryfx' => 'storage/queryfx',
'queryfx_all' => 'storage/queryfx',
'queryfx_one' => 'storage/queryfx',
'require_celerity_resource' => 'infratructure/celerity/api',
'vqsprintf' => 'storage/qsprintf',
'vqueryfx' => 'storage/queryfx',
'vqueryfx_all' => 'storage/queryfx',
'xsprintf_query' => 'storage/qsprintf',
),
'requires_class' =>
array(
'Aphront404Response' => 'AphrontResponse',
'AphrontAjaxResponse' => 'AphrontResponse',
'AphrontDefaultApplicationConfiguration' => 'AphrontApplicationConfiguration',
'AphrontDefaultApplicationController' => 'AphrontController',
'AphrontDialogResponse' => 'AphrontResponse',
'AphrontDialogView' => 'AphrontView',
'AphrontErrorView' => 'AphrontView',
'AphrontFileResponse' => 'AphrontResponse',
'AphrontFormCheckboxControl' => 'AphrontFormControl',
'AphrontFormControl' => 'AphrontView',
'AphrontFormFileControl' => 'AphrontFormControl',
'AphrontFormMarkupControl' => 'AphrontFormControl',
'AphrontFormSelectControl' => 'AphrontFormControl',
'AphrontFormStaticControl' => 'AphrontFormControl',
'AphrontFormSubmitControl' => 'AphrontFormControl',
'AphrontFormTextAreaControl' => 'AphrontFormControl',
'AphrontFormTextControl' => 'AphrontFormControl',
'AphrontFormTokenizerControl' => 'AphrontFormControl',
'AphrontFormView' => 'AphrontView',
'AphrontMySQLDatabaseConnection' => 'AphrontDatabaseConnection',
'AphrontNullView' => 'AphrontView',
'AphrontPageView' => 'AphrontView',
'AphrontPanelView' => 'AphrontView',
'AphrontQueryConnectionException' => 'AphrontQueryException',
'AphrontQueryConnectionLostException' => 'AphrontQueryRecoverableException',
'AphrontQueryCountException' => 'AphrontQueryException',
'AphrontQueryObjectMissingException' => 'AphrontQueryException',
'AphrontQueryParameterException' => 'AphrontQueryException',
'AphrontQueryRecoverableException' => 'AphrontQueryException',
'AphrontRedirectException' => 'AphrontException',
'AphrontRedirectResponse' => 'AphrontResponse',
'AphrontSideNavView' => 'AphrontView',
'AphrontTableView' => 'AphrontView',
'AphrontWebpageResponse' => 'AphrontResponse',
'CelerityResourceController' => 'AphrontController',
'ConduitAPI_conduit_connect_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_creatediff_Method' => 'ConduitAPIMethod',
'ConduitAPI_differential_setdiffproperty_Method' => 'ConduitAPIMethod',
'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod',
'ConduitAPI_user_find_Method' => 'ConduitAPIMethod',
'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail',
'DifferentialChangeset' => 'DifferentialDAO',
'DifferentialChangesetDetailView' => 'AphrontView',
'DifferentialChangesetListView' => 'AphrontView',
'DifferentialChangesetViewController' => 'DifferentialController',
'DifferentialController' => 'PhabricatorController',
'DifferentialDAO' => 'PhabricatorLiskDAO',
'DifferentialDiff' => 'DifferentialDAO',
'DifferentialDiffContentMail' => 'DifferentialMail',
'DifferentialDiffProperty' => 'DifferentialDAO',
'DifferentialDiffTableOfContentsView' => 'AphrontView',
'DifferentialDiffViewController' => 'DifferentialController',
'DifferentialFeedbackMail' => 'DifferentialMail',
'DifferentialHunk' => 'DifferentialDAO',
'DifferentialNewDiffMail' => 'DifferentialReviewRequestMail',
'DifferentialReviewRequestMail' => 'DifferentialMail',
'DifferentialRevision' => 'DifferentialDAO',
+ 'DifferentialRevisionDetailView' => 'AphrontView',
'DifferentialRevisionEditController' => 'DifferentialController',
'DifferentialRevisionListController' => 'DifferentialController',
+ 'DifferentialRevisionUpdateHistoryView' => 'AphrontView',
+ 'DifferentialRevisionViewController' => 'DifferentialController',
'PhabricatorAuthController' => 'PhabricatorController',
'PhabricatorConduitAPIController' => 'PhabricatorConduitController',
'PhabricatorConduitConnectionLog' => 'PhabricatorConduitDAO',
'PhabricatorConduitConsoleController' => 'PhabricatorConduitController',
'PhabricatorConduitController' => 'PhabricatorController',
'PhabricatorConduitDAO' => 'PhabricatorLiskDAO',
'PhabricatorConduitLogController' => 'PhabricatorConduitController',
'PhabricatorConduitMethodCallLog' => 'PhabricatorConduitDAO',
'PhabricatorController' => 'AphrontController',
'PhabricatorDirectoryCategory' => 'PhabricatorDirectoryDAO',
'PhabricatorDirectoryCategoryDeleteController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryCategoryEditController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryCategoryListController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryController' => 'PhabricatorController',
'PhabricatorDirectoryDAO' => 'PhabricatorLiskDAO',
'PhabricatorDirectoryItem' => 'PhabricatorDirectoryDAO',
'PhabricatorDirectoryItemDeleteController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryItemEditController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryItemListController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryMainController' => 'PhabricatorDirectoryController',
'PhabricatorFile' => 'PhabricatorFileDAO',
'PhabricatorFileController' => 'PhabricatorController',
'PhabricatorFileDAO' => 'PhabricatorLiskDAO',
'PhabricatorFileListController' => 'PhabricatorFileController',
'PhabricatorFileStorageBlob' => 'PhabricatorFileDAO',
'PhabricatorFileUploadController' => 'PhabricatorFileController',
'PhabricatorFileViewController' => 'PhabricatorFileController',
'PhabricatorLiskDAO' => 'LiskDAO',
'PhabricatorLoginController' => 'PhabricatorAuthController',
'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'PhabricatorMailImplementationAdapter',
'PhabricatorMetaMTAController' => 'PhabricatorController',
'PhabricatorMetaMTADAO' => 'PhabricatorLiskDAO',
'PhabricatorMetaMTAListController' => 'PhabricatorMetaMTAController',
'PhabricatorMetaMTAMail' => 'PhabricatorMetaMTADAO',
'PhabricatorMetaMTAMailingList' => 'PhabricatorMetaMTADAO',
'PhabricatorMetaMTAMailingListEditController' => 'PhabricatorMetaMTAController',
'PhabricatorMetaMTAMailingListsController' => 'PhabricatorMetaMTAController',
'PhabricatorMetaMTASendController' => 'PhabricatorMetaMTAController',
'PhabricatorMetaMTAViewController' => 'PhabricatorMetaMTAController',
'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',
'PhabricatorStandardPageView' => 'AphrontPageView',
'PhabricatorTypeaheadCommonDatasourceController' => 'PhabricatorTypeaheadDatasourceController',
'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController',
'PhabricatorUser' => 'PhabricatorUserDAO',
'PhabricatorUserDAO' => 'PhabricatorLiskDAO',
),
'requires_interface' =>
array(
),
));
diff --git a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
index 3c76065a09..9f482deb13 100644
--- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
+++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
@@ -1,157 +1,159 @@
<?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 getApplicationName() {
return 'aphront-default';
}
public function getURIMap() {
return array(
'/repository/' => array(
'$' => 'RepositoryListController',
'new/$' => 'RepositoryEditController',
'edit/(?<id>\d+)/$' => 'RepositoryEditController',
'delete/(?<id>\d+)/$' => 'RepositoryDeleteController',
),
'/' => array(
'$' => 'PhabricatorDirectoryMainController',
),
'/directory/' => array(
'item/$'
=> 'PhabricatorDirectoryItemListController',
'item/edit/(?:(?<id>\d+)/)?$'
=> 'PhabricatorDirectoryItemEditController',
'item/delete/(?<id>\d+)/'
=> 'PhabricatorDirectoryItemDeleteController',
'category/$'
=> 'PhabricatorDirectoryCategoryListController',
'category/edit/(?:(?<id>\d+)/)?$'
=> 'PhabricatorDirectoryCategoryEditController',
'category/delete/(?<id>\d+)/'
=> 'PhabricatorDirectoryCategoryDeleteController',
),
'/file/' => array(
'$' => 'PhabricatorFileListController',
'upload/$' => 'PhabricatorFileUploadController',
'(?<view>info)/(?<phid>[^/]+)/' => 'PhabricatorFileViewController',
'(?<view>view)/(?<phid>[^/]+)/' => 'PhabricatorFileViewController',
'(?<view>download)/(?<phid>[^/]+)/' => 'PhabricatorFileViewController',
),
'/phid/' => array(
'$' => 'PhabricatorPHIDLookupController',
'list/$' => 'PhabricatorPHIDListController',
'type/$' => 'PhabricatorPHIDTypeListController',
'type/edit/(?:(?<id>\d+)/)?$' => 'PhabricatorPHIDTypeEditController',
'new/$' => 'PhabricatorPHIDAllocateController',
),
'/people/' => array(
'$' => 'PhabricatorPeopleListController',
'edit/(?:(?<username>\w+)/)?$' => 'PhabricatorPeopleEditController',
),
'/p/(?<username>\w+)/$' => 'PhabricatorPeopleProfileController',
'/conduit/' => array(
'$' => 'PhabricatorConduitConsoleController',
'method/(?<method>[^/]+)$' => 'PhabricatorConduitConsoleController',
'log/$' => 'PhabricatorConduitLogController',
),
'/api/(?<method>[^/]+)$' => 'PhabricatorConduitAPIController',
+
+ '/D(?<id>\d+)' => 'DifferentialRevisionViewController',
'/differential/' => array(
'$' => 'DifferentialRevisionListController',
'filter/(?<filter>\w+)/$' => 'DifferentialRevisionListController',
'diff/(?<id>\d+)/$' => 'DifferentialDiffViewController',
'changeset/(?<id>\d+)/$' => 'DifferentialChangesetViewController',
'revision/edit/(?:(?<id>\d+)/)?$'
=> 'DifferentialRevisionEditController',
),
'/res/' => array(
'(?<hash>[a-f0-9]{8})/(?<path>.+\.(?:css|js))$'
=> 'CelerityResourceController',
),
'/typeahead/' => array(
'common/(?<type>\w+)/$'
=> 'PhabricatorTypeaheadCommonDatasourceController',
),
'/mail/' => array(
'$' => 'PhabricatorMetaMTAListController',
'send/$' => 'PhabricatorMetaMTASendController',
'view/(?<id>\d+)/$' => 'PhabricatorMetaMTAViewController',
'lists/$' => 'PhabricatorMetaMTAMailingListsController',
'lists/edit/(?:(?<id>\d+)/)?$'
=> 'PhabricatorMetaMTAMailingListEditController',
),
'/login/' => 'PhabricatorLoginController',
);
}
public function buildRequest() {
$request = new AphrontRequest($this->getHost(), $this->getPath());
$request->setRequestData($_GET + $_POST);
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>';
$view = new PhabricatorStandardPageView();
$view->appendChild($content);
$response = new AphrontWebpageResponse();
$response->setContent($view->render());
return $response;
}
public function willSendResponse(AphrontResponse $response) {
$request = $this->getRequest();
if ($response instanceof AphrontDialogResponse) {
if (!$request->isAjax()) {
$view = new PhabricatorStandardPageView();
$view->appendChild(
'<div style="padding: 2em 0;">'.
$response->buildResponseString().
'</div>');
$response = new AphrontWebpageResponse();
$response->setContent($view->render());
return $response;
}
}
return $response;
}
}
diff --git a/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php b/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
new file mode 100644
index 0000000000..cb1bcdff7c
--- /dev/null
+++ b/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
@@ -0,0 +1,1709 @@
+<?php
+
+/*
+ * Copyright 2011 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class DifferentialRevisionViewController extends DifferentialController {
+
+ private $revisionID;
+
+ public function willProcessRequest(array $data) {
+ $this->revisionID = $data['id'];
+ }
+
+ public function processRequest() {
+
+ $request = $this->getRequest();
+
+ $revision = id(new DifferentialRevision())->load($this->revisionID);
+ if (!$revision) {
+ return new Aphront404Response();
+ }
+
+ $revision->loadRelationships();
+
+ $diffs = $revision->loadDiffs();
+
+ $target = end($diffs);
+
+ $changesets = $target->loadChangesets();
+
+ $object_phids = array_merge(
+ $revision->getReviewers(),
+ $revision->getCCPHIDs(),
+ array(
+ $revision->getOwnerPHID(),
+ $request->getUser()->getPHID(),
+ ));
+
+ $handles = id(new PhabricatorObjectHandleData($object_phids))
+ ->loadHandles();
+
+ $diff_history = new DifferentialRevisionUpdateHistoryView();
+ $diff_history->setDiffs($diffs);
+
+ $toc_view = new DifferentialDiffTableOfContentsView();
+ $toc_view->setChangesets($changesets);
+
+ $changeset_view = new DifferentialChangesetListView();
+ $changeset_view->setChangesets($changesets);
+
+ $revision_detail = new DifferentialRevisionDetailView();
+ $revision_detail->setRevision($revision);
+
+ $properties = $this->getRevisionProperties($revision, $target, $handles);
+ $revision_detail->setProperties($properties);
+
+ $actions = $this->getRevisionActions($revision);
+ $revision_detail->setActions($actions);
+
+ return $this->buildStandardPageResponse(
+ '<div class="differential-primary-pane">'.
+ $revision_detail->render().
+ $diff_history->render().
+ $toc_view->render().
+ $changeset_view->render().
+ '</div>',
+ array(
+ 'title' => $revision->getTitle(),
+ ));
+ }
+
+ private function getRevisionProperties(
+ DifferentialRevision $revision,
+ DifferentialDiff $diff,
+ array $handles) {
+
+ $properties = array();
+
+ $status = $revision->getStatus();
+ $status = DifferentialRevisionStatus::getNameForRevisionStatus($status);
+ $properties['Revision Status'] = '<strong>'.$status.'</strong>';
+
+ $author = $handles[$revision->getOwnerPHID()];
+ $properties['Author'] = $author->renderLink();
+
+ $properties['Reviewers'] = $this->renderHandleLinkList(
+ array_select_keys(
+ $handles,
+ $revision->getReviewers()));
+
+ $properties['CCs'] = $this->renderHandleLinkList(
+ array_select_keys(
+ $handles,
+ $revision->getCCPHIDs()));
+
+ $path = $diff->getSourcePath();
+ if ($path) {
+ $branch = $diff->getBranch() ? ' (' . $diff->getBranch() . ')' : '';
+ $host = $diff->getSourceMachine();
+ if ($host) {
+ $host .= ':';
+ }
+ $properties['Path'] = phutil_escape_html("{$host}{$path} {$branch}");
+ }
+
+
+ $properties['Lint'] = 'TODO';
+ $properties['Unit'] = 'TODO';
+
+ return $properties;
+ }
+
+ private function getRevisionActions(DifferentialRevision $revision) {
+ $viewer_phid = $this->getRequest()->getUser()->getPHID();
+ $viewer_is_owner = ($revision->getOwnerPHID() == $viewer_phid);
+ $viewer_is_reviewer = in_array($viewer_phid, $revision->getReviewers());
+ $viewer_is_cc = in_array($viewer_phid, $revision->getCCPHIDs());
+ $status = $revision->getStatus();
+ $revision_id = $revision->getID();
+ $revision_phid = $revision->getPHID();
+
+ $links = array();
+
+ if ($viewer_is_owner) {
+ $links[] = array(
+ 'class' => 'revision-edit',
+ 'href' => "/differential/revision/edit/{$revision_id}/",
+ 'name' => 'Edit Revision',
+ );
+ }
+
+ if (!$viewer_is_owner && !$viewer_is_reviewer) {
+ $action = $viewer_is_cc ? 'rem' : 'add';
+ $links[] = array(
+ 'class' => $viewer_is_cc ? 'subscribe-rem' : 'subscribe-add',
+ 'href' => "/differential/subscribe/{$action}/{$revision_id}/",
+ 'name' => $viewer_is_cc ? 'Unsubscribe' : 'Subscribe',
+ );
+ } else {
+ $links[] = array(
+ 'class' => 'subscribe-rem unavailable',
+ 'name' => 'Automatically Subscribed',
+ );
+ }
+
+ $links[] = array(
+ 'class' => 'transcripts-metamta',
+ 'name' => 'MetaMTA Transcripts',
+ 'href' => "/mail/?phid={$revision_phid}",
+ );
+
+ return $links;
+ }
+
+
+ private function renderHandleLinkList(array $list) {
+ if (empty($list)) {
+ return '<em>None</em>';
+ }
+ return implode(', ', mpull($list, 'renderLink'));
+ }
+
+}
+/*
+
+
+ $viewer_id = $this->getRequest()->getViewerContext()->getUserID();
+ $viewer_is_owner = ($viewer_id == $revision->getOwnerID());
+ $viewer_is_reviewer =
+ ((array_search($viewer_id, $revision->getReviewers())) !== false);
+ $viewer_is_cc =
+ ((array_search($viewer_id, $revision->getCCFBIDs())) !== false);
+ $status = $revision->getStatus();
+
+ $links = array();
+
+ if (!$viewer_is_owner && !$viewer_is_reviewer) {
+ $action = $viewer_is_cc
+ ? 'rem'
+ : 'add';
+ $revision_id = $revision->getID();
+ $href = "/differential/subscribe/{$action}/{$revision_id}";
+ $links[] = array(
+ $viewer_is_cc ? 'subscribe-disabled' : 'subscribe-enabled',
+ <a href={$href}>{$viewer_is_cc ? 'Unsubscribe' : 'Subscribe'}</a>,
+ );
+ } else {
+ $links[] = array(
+ 'subscribe-disabled unavailable',
+ <a>Automatically Subscribed</a>,
+ );
+ }
+
+ $blast_uri = RedirectURI(
+ '/intern/differential/?action=tasks&fbid='.$revision->getFBID())
+ ->setTier('intern');
+ $links[] = array(
+ 'tasks',
+ <a href={$blast_uri}>Edit Tasks</a>,
+ );
+
+ $engineering_repository_id = RepositoryRef::getByCallsign('E')->getID();
+ $svn_revision = $revision->getSVNRevision();
+ if ($status == DifferentialConstants::COMMITTED &&
+ $svn_revision &&
+ $revision->getRepositoryID() == $engineering_repository_id) {
+ $href = '/intern/push/request.php?rev='.$svn_revision;
+ $href = RedirectURI($href)->setTier('intern');
+ $links[] = array(
+ 'merge',
+ <a href={$href} id="ask_for_merge_link">Ask for Merge</a>,
+ );
+ }
+
+ $links[] = array(
+ 'herald-transcript',
+ <a href={"/herald/transcript/?fbid=".$revision->getFBID()}
+ >Herald Transcripts</a>,
+ );
+ $links[] = array(
+ 'metamta-transcript',
+ <a href={"/mail/?view=all&fbid=".$revision->getFBID()}
+ >MetaMTA Transcripts</a>,
+ );
+
+
+ $list = <ul class="differential-actions" />;
+ foreach ($links as $link) {
+ list($class, $tag) = $link;
+ $list->appendChild(<li class={$class}>{$tag}</li>);
+ }
+
+ return $list;
+
+
+
+/*
+// TODO
+// $sandcastle = $this->getSandcastleURI($diff);
+// if ($sandcastle) {
+// $fields['Sandcastle'] = <a href={$sandcastle}>{$sandcastle}</a>;
+// }
+
+ $path = $diff->getSourcePath();
+ if ($path) {
+ $host = $diff->getSourceMachine();
+ $branch = $diff->getGitBranch() ? ' (' . $diff->getGitBranch() . ')' : '';
+
+ if ($host) {
+// TODO
+// $user = $handles[$this->getRequest()->getViewerContext()->getUserID()]
+// ->getName();
+ $user = 'TODO';
+ $fields['Path'] =
+ <x:frag>
+ <a href={"ssh://{$user}@{$host}"}>{$host}</a>:{$path}{$branch}
+ </x:frag>;
+ } else {
+ $fields['Path'] = $path;
+ }
+ }
+
+ $reviewer_links = array();
+ foreach ($revision->getReviewers() as $reviewer) {
+ $reviewer_links[] = <tools:handle handle={$handles[$reviewer]}
+ link={true} />;
+ }
+ if ($reviewer_links) {
+ $fields['Reviewers'] = array_implode(', ', $reviewer_links);
+ } else {
+ $fields['Reviewers'] = <em>None</em>;
+ }
+
+ $ccs = $revision->getCCFBIDs();
+ if ($ccs) {
+ $links = array();
+ foreach ($ccs as $cc) {
+ $links[] = <tools:handle handle={$handles[$cc]}
+ link={true} />;
+ }
+ $fields['CCs'] = array_implode(', ', $links);
+ }
+
+ $blame_rev = $revision->getSvnBlameRevision();
+ if ($blame_rev) {
+ if ($revision->getRepositoryRef() && is_numeric($blame_rev)) {
+ $ref = new RevisionRef($revision->getRepositoryRef(), $blame_rev);
+ $fields['Blame Revision'] =
+ <a href={URI($ref->getDetailURL())}>
+ {$ref->getName()}
+ </a>;
+ } else {
+ $fields['Blame Revision'] = $blame_rev;
+ }
+ }
+
+ $tasks = $revision->getTaskHandles();
+
+ if ($tasks) {
+ $links = array();
+ foreach ($tasks as $task) {
+ $links[] = <tools:handle handle={$task} link={true} />;
+ }
+ $fields['Tasks'] = array_implode(<br />, $links);
+ }
+
+ $bugzilla_id = $revision->getBugzillaID();
+ if ($bugzilla_id) {
+ $href = 'http://bugs.developers.facebook.com/show_bug.cgi?id='.
+ $bugzilla_id;
+ $fields['Bugzilla'] = <a href={$href}>{'#'.$bugzilla_id}</a>;
+ }
+
+ $fields['Apply Patch'] = <tt>arc patch --revision {$revision->getID()}</tt>;
+
+ if ($diff->getParentRevisionID()) {
+ $parent = id(new DifferentialRevision())->load(
+ $diff->getParentRevisionID());
+ if ($parent) {
+ $fields['Depends On'] =
+ <a href={$parent->getURI()}>
+ D{$parent->getID()}: {$parent->getName()}
+ </a>;
+ }
+ }
+
+ $star = <span class="star">{"\xE2\x98\x85"}</span>;
+
+ Javelin::initBehavior('differential-star-more');
+
+ switch ($diff->getLinted()) {
+ case Diff::LINT_FAIL:
+ $more = $this->renderDiffPropertyMoreLink($diff, 'lint');
+ $fields['Lint'] =
+ <x:frag>
+ <span class="star-warn">{$star} Lint Failures</span>
+ {$more}
+ </x:frag>;
+ break;
+ case Diff::LINT_WARNINGS:
+ $more = $this->renderDiffPropertyMoreLink($diff, 'lint');
+ $fields['Lint'] =
+ <x:frag>
+ <span class="star-warn">{$star} Lint Warnings</span>
+ {$more}
+ </x:frag>;
+ break;
+ case Diff::LINT_OKAY:
+ $fields['Lint'] =
+ <span class="star-okay">{$star} Lint Free</span>;
+ break;
+ default:
+ case Diff::LINT_NO:
+ $fields['Lint'] =
+ <span class="star-none">{$star} Not Linted</span>;
+ break;
+ }
+
+ $unit_details = false;
+ switch ($diff->getUnitTested()) {
+ case Diff::UNIT_FAIL:
+ $fields['Unit Tests'] =
+ <span class="star-warn">{$star} Unit Test Failures</span>;
+ $unit_details = true;
+ break;
+ case Diff::UNIT_WARN:
+ $fields['Unit Tests'] =
+ <span class="star-warn">{$star} Unit Test Warnings</span>;
+ $unit_details = true;
+ break;
+ case Diff::UNIT_OKAY:
+ $fields['Unit Tests'] =
+ <span class="star-okay">{$star} Unit Tests Passed</span>;
+ $unit_details = true;
+ break;
+ case Diff::UNIT_NO_TESTS:
+ $fields['Unit Tests'] =
+ <span class="star-none">{$star} No Test Coverage</span>;
+ break;
+ case Diff::UNIT_NO:
+ default:
+ $fields['Unit Tests'] =
+ <span class="star-none">{$star} Not Unit Tested</span>;
+ break;
+ }
+
+ if ($unit_details) {
+ $fields['Unit Tests'] =
+ <x:frag>
+ {$fields['Unit Tests']}
+ {$this->renderDiffPropertyMoreLink($diff, 'unit')}
+ </x:frag>;
+ }
+
+ $platform_impact = $revision->getPlatformImpact();
+ if ($platform_impact) {
+ $fields['Platform Impact'] =
+ <text linebreaks="true">{$platform_impact}</text>;
+ }
+
+ return $fields;
+ }
+
+
+}
+
+/*
+
+
+
+ protected function getSandcastleURI(Diff $diff) {
+ $uri = $this->getDiffProperty($diff, 'facebook:sandcastle_uri');
+ if (!$uri) {
+ $uri = $diff->getSandboxURL();
+ }
+ return $uri;
+ }
+
+ protected function getDiffProperty(Diff $diff, $property, $default = null) {
+ $diff_id = $diff->getID();
+ if (empty($this->diffProperties[$diff_id])) {
+ $props = id(new DifferentialDiffProperty())
+ ->loadAllWhere('diffID = %s', $diff_id);
+ $dict = array_pull($props, 'getData', 'getName');
+ $this->diffProperties[$diff_id] = $dict;
+ }
+ return idx($this->diffProperties[$diff_id], $property, $default);
+ }
+
+ public function process() {
+ $uri = $this->getRequest()->getPath();
+ if (starts_with($uri, '/d')) {
+ return <alite:redirect uri={strtoupper($uri)}/>;
+ }
+
+ $revision = id(new DifferentialRevision())->load($this->revisionID);
+ if (!$revision) {
+ throw new Exception("Bad revision ID.");
+ }
+
+ $diffs = id(new Diff())->loadAllWhere(
+ 'revisionID = %d',
+ $revision->getID());
+ $diffs = array_psort($diffs, 'getID');
+
+ $request = $this->getRequest();
+ $new = $request->getInt('new');
+ $old = $request->getInt('old');
+
+ if (($new || $old) && $new <= $old) {
+ throw new Exception(
+ "You can only view the diff of an older update relative to a newer ".
+ "update.");
+ }
+
+ if ($new && empty($diffs[$new])) {
+ throw new Exception(
+ "The 'new' diff does not exist.");
+ } else if ($new) {
+ $diff = $diffs[$new];
+ } else {
+ $diff = end($diffs);
+ if (!$diff) {
+ throw new Exception("No diff attached to this revision?");
+ }
+ $new = $diff->getID();
+ }
+
+ $target_diff = $diff;
+
+ if ($old && empty($diffs[$old])) {
+ throw new Exception(
+ "The 'old' diff does not exist.");
+ }
+
+ $rows = array(array('Base', '', true, false, null,
+ $diff->getSourceControlBaseRevision()
+ ? $diff->getSourceControlBaseRevision()
+ : <em>Master</em>));
+ $idx = 0;
+ foreach ($diffs as $cdiff) {
+ $rows[] = array(
+ 'Diff '.(++$idx),
+ $cdiff->getID(),
+ $cdiff->getID() != max(array_pull($diffs, 'getID')),
+ true,
+ $cdiff->getDateCreated(),
+ $cdiff->getDescription()
+ ? $cdiff->getDescription()
+ : <em>No description available.</em>,
+ $cdiff->getUnitTested(),
+ $cdiff->getLinted());
+ }
+
+ $diff_table =
+ <table class="differential-diff-differ">
+ <tr>
+ <th>Diff</th>
+ <th>Diff ID</th>
+ <th>Description</th>
+ <th>Age</th>
+ <th>Lint</th>
+ <th>Unit</th>
+ </tr>
+ </table>;
+ $ii = 0;
+
+ $old_ids = array();
+ foreach ($rows as $row) {
+ $xold = null;
+ if ($row[2]) {
+ $lradio = <input name="old" value={$row[1]} type="radio"
+ disabled={$row[1] >= $new}
+ checked={$old == $row[1]} />;
+ if ($old == $row[1]) {
+ $xold = 'old-now';
+ }
+ $old_ids[] = $lradio->requireUniqueID();
+ } else {
+ $lradio = null;
+ }
+ $xnew = null;
+ if ($row[3]) {
+ $rradio = <input name="new" value={$row[1]} type="radio"
+ sigil="new-radio"
+ checked={$new == $row[1]} />;
+ if ($new == $row[1]) {
+ $xnew = 'new-now';
+ }
+ } else {
+ $rradio = null;
+ }
+
+ if ($row[3]) {
+ $unit_star = 'star-none';
+ switch ($row[6]) {
+ case Diff::UNIT_FAIL:
+ case Diff::UNIT_WARN: $unit_star = 'star-warn'; break;
+ case Diff::UNIT_OKAY: $unit_star = 'star-okay'; break;
+ }
+
+ $lint_star = 'star-none';
+ switch ($row[7]) {
+ case Diff::LINT_FAIL:
+ case Diff::LINT_WARNINGS: $lint_star = 'star-warn'; break;
+ case Diff::LINT_OKAY: $lint_star = 'star-okay'; break;
+ }
+
+ $star = "\xE2\x98\x85";
+
+ $unit_star =
+ <span class={$unit_star}>
+ <span class="star">{$star}</span>
+ </span>;
+
+ $lint_star =
+ <span class={$lint_star}>
+ <span class="star">{$star}</span>
+ </span>;
+ } else {
+ $unit_star = null;
+ $lint_star = null;
+ }
+
+ $diff_table->appendChild(
+ <tr class={++$ii % 2 ? 'alt' : null}>
+ <td class="name">{$row[0]}</td>
+ <td class="diffid">{$row[1]}</td>
+ <td class="desc">{$row[5]}</td>
+ <td class="age">{$row[4] ? ago(time() - $row[4]) : null}</td>
+ <td class="star">{$lint_star}</td>
+ <td class="star">{$unit_star}</td>
+ <td class={"old {$xold}"}>{$lradio}</td>
+ <td class={"new {$xnew}"}>{$rradio}</td>
+ </tr>);
+ }
+
+ Javelin::initBehavior('differential-diff-radios', array(
+ 'radios' => $old_ids,
+ ));
+
+ $diff_table->appendChild(
+ <tr>
+ <td colspan="8" class="diff-differ-submit">
+ <label>Whitespace Changes:</label>
+ {id(<select name="whitespace" />)->setOptions(
+ array(
+ 'ignore-all' => 'Ignore All',
+ 'ignore-trailing' => 'Ignore Trailing',
+ 'show-all' => 'Show All',
+ ), $request->getStr('whitespace'))}{' '}
+ <button type="submit">Show Diff</button>
+ </td>
+ </tr>);
+
+ $diff_table =
+ <div class="differential-table-of-contents">
+ <h1>Revision Update History</h1>
+ <form action={URI::getRequestURI()} method="get">
+ {$diff_table}
+ </form>
+ </div>;
+
+
+ $load_ids = array_filter(array($old, $diff->getID()));
+
+ $viewer_id = $this->getRequest()->getViewerContext()->getUserID();
+
+ $raw_objects = queryfx_all(
+ smc_get_db('cdb.differential', 'r'),
+ 'SELECT * FROM changeset WHERE changeset.diffID IN (%Ld)',
+ $load_ids);
+
+ $raw_objects = array_group($raw_objects, 'diffID');
+ $objects = $raw_objects[$diff->getID()];
+
+ if (!$objects) {
+ $changesets = array();
+ } else {
+ $changesets = id(new DifferentialChangeset())->loadAllFromArray($objects);
+ }
+
+ $against_warn = null;
+ $against_map = array();
+ $visible_changesets = array();
+ if ($old) {
+ $old_diff = $diffs[$old];
+ $new_diff = $diff;
+ $old_path = $old_diff->getSourcePath();
+ $new_path = $new_diff->getSourcePath();
+
+ $old_prefix = null;
+ $new_prefix = null;
+ if ((strlen($old_path) < strlen($new_path)) &&
+ (!strncmp($old_path, $new_path, strlen($old_path)))) {
+ $old_prefix = substr($new_path, strlen($old_path));
+ }
+ if ((strlen($new_path) < strlen($old_path)) &&
+ (!strncmp($old_path, $new_path, strlen($new_path)))) {
+ $new_prefix = substr($old_path, strlen($new_path));
+ }
+
+ $old_changesets = id(new DifferentialChangeset())
+ ->loadAllFromArray($raw_objects[$old]);
+ $old_changesets = array_pull($old_changesets, null, 'getFilename');
+ if ($new_prefix) {
+ $rekeyed_map = array();
+ foreach ($old_changesets as $key => $value) {
+ $rekeyed_map[$new_prefix.$key] = $value;
+ }
+ $old_changesets = $rekeyed_map;
+ }
+
+ foreach ($changesets as $key => $changeset) {
+ $file = $old_prefix.$changeset->getFilename();
+ if (isset($old_changesets[$file])) {
+ $checksum = $changeset->getChecksum();
+ if ($checksum !== null &&
+ $checksum == $old_changesets[$file]->getChecksum()) {
+ unset($changesets[$key]);
+ unset($old_changesets[$file]);
+ } else {
+ $against_map[$changeset->getID()] = $old_changesets[$file]->getID();
+ unset($old_changesets[$file]);
+ }
+ }
+ }
+
+ foreach ($old_changesets as $changeset) {
+ $changesets[$changeset->getID()] = $changeset;
+ $against_map[$changeset->getID()] = -1;
+ }
+
+ $against_warn =
+ <tools:notice title="NOTE - Diff of Diffs">
+ You are viewing a synthetic diff between two previous diffs in this
+ revision. You can not add new inline comments (for now).
+ </tools:notice>;
+ } else {
+ $visible_changesets = array_pull($changesets, 'getID');
+ }
+
+ $changesets = array_psort($changesets, 'getSortKey');
+ $all_changesets = $changesets;
+
+ $warning = null;
+ $limit = 100;
+ if (count($changesets) > $limit && !$this->getRequest()->getStr('large')) {
+ $count = number_format(count($changesets));
+ $warning =
+ <tools:notice title="Very Large Diff">
+ This diff is extremely large and affects {$count} files. Only the
+ first {number_format($limit)} files are shown.
+ <strong>
+ <a href={$revision->getURI().'?large=true'}>Show All Files</a>
+ </strong>
+ </tools:notice>;
+ $changesets = array_slice($changesets, 0, $limit);
+ if (!$old) {
+ $visible_changesets = array_pull($changesets, 'getID');
+ }
+ }
+
+ $detail_view =
+ <differential:changeset-detail-view
+ changesets={$changesets}
+ revision={$revision}
+ against={$against_map}
+ edit={empty($against_map)}
+ whitespace={$request->getStr('whitespace')} />;
+
+ $table_of_contents =
+ <differential:changeset-table-of-contents
+ changesets={$all_changesets} />;
+
+ $implied_feedback = array();
+ foreach (array(
+ 'summarize' => $revision->getSummary(),
+ 'testplan' => $revision->getTestPlan(),
+ 'annotate' => $revision->getNotes(),
+ ) as $type => $text) {
+ if (!strlen($text)) {
+ continue;
+ }
+ $implied_feedback[] = id(new DifferentialFeedback())
+ ->setUserID($revision->getOwnerID())
+ ->setAction($type)
+ ->setDateCreated($revision->getDateCreated())
+ ->setContent($text);
+ }
+
+ $feedback = id(new DifferentialFeedback())->loadAllWithRevision($revision);
+ $feedback = array_merge($implied_feedback, $feedback);
+
+ $inline_comments = $this->loadInlineComments($feedback, $changesets);
+
+ $diff_map = array();
+ $diffs = array_psort($diffs, 'getID');
+ foreach ($diffs as $diff) {
+ $diff_map[$diff->getID()] = count($diff_map) + 1;
+ }
+ $visible_changesets = array_fill_keys($visible_changesets, true);
+ $hidden_changesets = array();
+ foreach ($changesets as $changeset) {
+ $id = $changeset->getID();
+ if (isset($visible_changesets[$id])) {
+ continue;
+ }
+ $hidden_changesets[$id] = $diff_map[$changeset->getDiffID()];
+ }
+
+ $revision->loadRelationships();
+ $ccs = $revision->getCCFBIDs();
+ $reviewers = $revision->getReviewers();
+
+ $actors = array_pull($feedback, 'getUserID');
+ $actors[] = $revision->getOwnerID();
+
+ $tasks = array();
+ assoc_get_by_type(
+ $revision->getFBID(),
+ 22284182462, // TODO: include issue, DIFFCAMP_TASK_ASSOC
+ $start = null,
+ $limit = null,
+ $pending = true,
+ $tasks);
+ memcache_dispatch();
+ $tasks = array_keys($tasks);
+
+ $preparer = new Preparer();
+ $fbids = array_merge_fast(
+ array($actors, array($viewer_id), $reviewers, $ccs, $tasks),
+ true);
+ $handles = array();
+ $handle_data = id(new ToolsHandleData($fbids, $handles))
+ ->needNames()
+ ->needAlternateNames()
+ ->needAlternateIDs()
+ ->needThumbnails();
+ $preparer->waitFor($handle_data);
+ $preparer->go();
+
+ $revision->attachTaskHandles(array_select_keys($handles, $tasks));
+
+ $inline_comments = array_group($inline_comments, 'getFeedbackID');
+
+ $engine = new RemarkupEngine();
+ $engine->enableFeature(RemarkupEngine::FEATURE_GUESS_IMAGES);
+ $engine->enableFeature(RemarkupEngine::FEATURE_YOUTUBE);
+ $engine->setCurrentSandcastle($this->getSandcastleURI($target_diff));
+ $feed = array();
+ foreach ($feedback as $comment) {
+ $inlines = null;
+ if (isset($inline_comments[$comment->getID()])) {
+ $inlines = $inline_comments[$comment->getID()];
+ }
+ $feed[] =
+ <differential:feedback
+ feedback={$comment}
+ handle={$handles[$comment->getUserID()]}
+ engine={$engine}
+ inline={$inlines}
+ changesets={$changesets}
+ hidden={$hidden_changesets} />;
+ }
+
+ $feed = $this->renderFeedbackList($feed, $feedback, $viewer_id);
+
+ $fields = $this->getDetailFields($revision, $diff, $handles);
+ $table = <table class="differential-revision-properties" />;
+ foreach ($fields as $key => $value) {
+ $table->appendChild(
+ <tr>
+ <th>{$key}:</th><td>{$value}</td>
+ </tr>);
+ }
+
+ $quick_links = $this->getQuickLinks($revision);
+
+ $edit_link = null;
+ if ($revision->getOwnerID() == $viewer_id) {
+ $edit_link = '/differential/revision/edit/'.$revision->getID().'/';
+ $edit_link =
+ <x:frag>
+ {' '}(<a href={$edit_link}>Edit Revision</a>)
+ </x:frag>;
+ }
+
+ $info =
+ <div class="differential-revision-information">
+ <div class="differential-revision-actions">
+ {$quick_links}
+ </div>
+ <div class="differential-revision-detail">
+ <h1>{$revision->getName()}{$edit_link}</h1>
+ {$table}
+ </div>
+ </div>;
+
+ $actions = $this->getRevisionActions($revision);
+ $revision_id = $revision->getID();
+
+ Javelin::initBehavior(
+ 'differential-feedback-preview',
+ array(
+ 'uri' => '/differential/preview/'.$revision->getFBID().'/',
+ 'preview' => 'overall-feedback-preview',
+ 'action' => 'feedback-action',
+ 'content' => 'feedback-content',
+ ));
+
+ Javelin::initBehavior(
+ 'differential-inline-comment-preview',
+ array(
+ 'uri' => '/differential/inline-preview/'.$revision_id.'/'.$new.'/',
+ 'preview' => 'inline-comment-preview',
+ ));
+
+ $content = SavedCopy::loadData(
+ $viewer_id,
+ SavedCopy::Type_DifferentialRevisionFeedback,
+ $revision->getFBID());
+
+
+ $inline_comment_container =
+ <div id="inline-comment-preview"><p>Loading...</p></div>;
+
+ $feedback = id(new DifferentialFeedback())
+ ->setAction('none')
+ ->setUserID($viewer_id)
+ ->setContent($content);
+
+ $preview =
+ <div class="differential-feedback differential-feedback-preview">
+ <div id="overall-feedback-preview">
+ <differential:feedback
+ feedback={$feedback}
+ engine={$engine}
+ preview={true}
+ handle={$handles[$viewer_id]} />
+ </div>
+ {$inline_comment_container}
+ </div>;
+
+ $syntax_link =
+ <a href={'http://www.intern.facebook.com/intern/wiki/index.php' .
+ '/Articles/Remarkup_Syntax_Reference'}
+ target="_blank"
+ tabindex="4">Remarkup Reference</a>;
+
+ Javelin::initBehavior(
+ 'differential-add-reviewers',
+ array(
+ 'src' => redirect_str('/datasource/employee/', 'tools'),
+ 'tokenizer' => 'reviewer-tokenizer',
+ 'select' => 'feedback-action',
+ 'row' => 'reviewer-tokenizer-row',
+ ));
+
+ $feedback_form =
+ <x:frag>
+ <div class="differential-feedback-form">
+ <tools:form
+ method="post"
+ action={"/differential/revision/feedback/{$revision_id}/"}>
+ <h1>Provide Feedback</h1>
+ <tools:fieldset>
+ <tools:control type="select" label="Action">
+ {id(<select name="action" id="feedback-action"
+ tabindex="1" />)
+ ->setOptions($actions)}
+ </tools:control>
+ <tools:control type="text" label="Reviewers"
+ style="display: none;"
+ id="reviewer-tokenizer-row">
+ <javelin:tokenizer-template
+ id="reviewer-tokenizer"
+ name="reviewers" />
+ </tools:control>
+ <tools:control type="textarea" label="Feedback"
+ caption={$syntax_link}>
+ <tools:droppable-textarea id="feedback-content" name="feedback"
+ tabindex="2">
+ {$content}
+ </tools:droppable-textarea>
+ </tools:control>
+ <tools:control type="submit">
+ <button type="submit"
+ tabindex="3">Clowncopterize</button>
+ </tools:control>
+ </tools:fieldset>
+ </tools:form>
+ </div>
+ {$preview}
+ </x:frag>;
+
+ $notice = null;
+ if ($this->getRequest()->getBool('diff_changed')) {
+ $notice =
+ <tools:notice title="Revision Updated Recently">
+ This revision was updated with a <strong>new diff</strong> while you
+ were providing feedback. Your inline comments appear on the
+ <strong>old diff</strong>.
+ </tools:notice>;
+ }
+
+ return
+ <differential:standard-page title={$revision->getName()}>
+ <div class="differential-primary-pane">
+ {$warning}
+ {$notice}
+ {$info}
+ <div class="differential-feedback">
+ {$feed}
+ </div>
+ {$diff_table}
+ {$table_of_contents}
+ {$against_warn}
+ {$detail_view}
+ {$feedback_form}
+ </div>
+ </differential:standard-page>;
+ }
+
+ protected function getQuickLinks(DifferentialRevision $revision) {
+
+ $viewer_id = $this->getRequest()->getViewerContext()->getUserID();
+ $viewer_is_owner = ($viewer_id == $revision->getOwnerID());
+ $viewer_is_reviewer =
+ ((array_search($viewer_id, $revision->getReviewers())) !== false);
+ $viewer_is_cc =
+ ((array_search($viewer_id, $revision->getCCFBIDs())) !== false);
+ $status = $revision->getStatus();
+
+ $links = array();
+
+ if (!$viewer_is_owner && !$viewer_is_reviewer) {
+ $action = $viewer_is_cc
+ ? 'rem'
+ : 'add';
+ $revision_id = $revision->getID();
+ $href = "/differential/subscribe/{$action}/{$revision_id}";
+ $links[] = array(
+ $viewer_is_cc ? 'subscribe-disabled' : 'subscribe-enabled',
+ <a href={$href}>{$viewer_is_cc ? 'Unsubscribe' : 'Subscribe'}</a>,
+ );
+ } else {
+ $links[] = array(
+ 'subscribe-disabled unavailable',
+ <a>Automatically Subscribed</a>,
+ );
+ }
+
+ $blast_uri = RedirectURI(
+ '/intern/differential/?action=blast&fbid='.$revision->getFBID())
+ ->setTier('intern');
+ $links[] = array(
+ 'blast',
+ <a href={$blast_uri}>Blast Revision</a>,
+ );
+
+ $blast_uri = RedirectURI(
+ '/intern/differential/?action=tasks&fbid='.$revision->getFBID())
+ ->setTier('intern');
+ $links[] = array(
+ 'tasks',
+ <a href={$blast_uri}>Edit Tasks</a>,
+ );
+
+ if ($viewer_is_owner && false) {
+ $perflab_uri = RedirectURI(
+ '/intern/differential/?action=perflab&fbid='.$revision->getFBID())
+ ->setTier('intern');
+ $links[] = array(
+ 'perflab',
+ <a href={$perflab_uri}>Run in Perflab</a>,
+ );
+ }
+
+ $engineering_repository_id = RepositoryRef::getByCallsign('E')->getID();
+ $svn_revision = $revision->getSVNRevision();
+ if ($status == DifferentialConstants::COMMITTED &&
+ $svn_revision &&
+ $revision->getRepositoryID() == $engineering_repository_id) {
+ $href = '/intern/push/request.php?rev='.$svn_revision;
+ $href = RedirectURI($href)->setTier('intern');
+ $links[] = array(
+ 'merge',
+ <a href={$href} id="ask_for_merge_link">Ask for Merge</a>,
+ );
+ }
+
+ $links[] = array(
+ 'herald-transcript',
+ <a href={"/herald/transcript/?fbid=".$revision->getFBID()}
+ >Herald Transcripts</a>,
+ );
+ $links[] = array(
+ 'metamta-transcript',
+ <a href={"/mail/?view=all&fbid=".$revision->getFBID()}
+ >MetaMTA Transcripts</a>,
+ );
+
+
+ $list = <ul class="differential-actions" />;
+ foreach ($links as $link) {
+ list($class, $tag) = $link;
+ $list->appendChild(<li class={$class}>{$tag}</li>);
+ }
+
+ return $list;
+ }
+
+ protected function getDetailFields(
+ DifferentialRevision $revision,
+ Diff $diff,
+ array $handles) {
+
+ $fields = array();
+ $fields['Revision Status'] = $this->getRevisionStatusDisplay($revision);
+
+ $author = $revision->getOwnerID();
+ $fields['Author'] = <tools:handle handle={$handles[$author]}
+ link={true} />;
+
+ $sandcastle = $this->getSandcastleURI($diff);
+ if ($sandcastle) {
+ $fields['Sandcastle'] = <a href={$sandcastle}>{$sandcastle}</a>;
+ }
+
+ $path = $diff->getSourcePath();
+ if ($path) {
+ $host = $diff->getSourceMachine();
+ $branch = $diff->getGitBranch() ? ' (' . $diff->getGitBranch() . ')' : '';
+
+ if ($host) {
+ $user = $handles[$this->getRequest()->getViewerContext()->getUserID()]
+ ->getName();
+ $fields['Path'] =
+ <x:frag>
+ <a href={"ssh://{$user}@{$host}"}>{$host}</a>:{$path}{$branch}
+ </x:frag>;
+ } else {
+ $fields['Path'] = $path;
+ }
+ }
+
+ $reviewer_links = array();
+ foreach ($revision->getReviewers() as $reviewer) {
+ $reviewer_links[] = <tools:handle handle={$handles[$reviewer]}
+ link={true} />;
+ }
+ if ($reviewer_links) {
+ $fields['Reviewers'] = array_implode(', ', $reviewer_links);
+ } else {
+ $fields['Reviewers'] = <em>None</em>;
+ }
+
+ $ccs = $revision->getCCFBIDs();
+ if ($ccs) {
+ $links = array();
+ foreach ($ccs as $cc) {
+ $links[] = <tools:handle handle={$handles[$cc]}
+ link={true} />;
+ }
+ $fields['CCs'] = array_implode(', ', $links);
+ }
+
+ $blame_rev = $revision->getSvnBlameRevision();
+ if ($blame_rev) {
+ if ($revision->getRepositoryRef() && is_numeric($blame_rev)) {
+ $ref = new RevisionRef($revision->getRepositoryRef(), $blame_rev);
+ $fields['Blame Revision'] =
+ <a href={URI($ref->getDetailURL())}>
+ {$ref->getName()}
+ </a>;
+ } else {
+ $fields['Blame Revision'] = $blame_rev;
+ }
+ }
+
+ $tasks = $revision->getTaskHandles();
+
+ if ($tasks) {
+ $links = array();
+ foreach ($tasks as $task) {
+ $links[] = <tools:handle handle={$task} link={true} />;
+ }
+ $fields['Tasks'] = array_implode(<br />, $links);
+ }
+
+ $bugzilla_id = $revision->getBugzillaID();
+ if ($bugzilla_id) {
+ $href = 'http://bugs.developers.facebook.com/show_bug.cgi?id='.
+ $bugzilla_id;
+ $fields['Bugzilla'] = <a href={$href}>{'#'.$bugzilla_id}</a>;
+ }
+
+ $fields['Apply Patch'] = <tt>arc patch --revision {$revision->getID()}</tt>;
+
+ if ($diff->getParentRevisionID()) {
+ $parent = id(new DifferentialRevision())->load(
+ $diff->getParentRevisionID());
+ if ($parent) {
+ $fields['Depends On'] =
+ <a href={$parent->getURI()}>
+ D{$parent->getID()}: {$parent->getName()}
+ </a>;
+ }
+ }
+
+ $star = <span class="star">{"\xE2\x98\x85"}</span>;
+
+ Javelin::initBehavior('differential-star-more');
+
+ switch ($diff->getLinted()) {
+ case Diff::LINT_FAIL:
+ $more = $this->renderDiffPropertyMoreLink($diff, 'lint');
+ $fields['Lint'] =
+ <x:frag>
+ <span class="star-warn">{$star} Lint Failures</span>
+ {$more}
+ </x:frag>;
+ break;
+ case Diff::LINT_WARNINGS:
+ $more = $this->renderDiffPropertyMoreLink($diff, 'lint');
+ $fields['Lint'] =
+ <x:frag>
+ <span class="star-warn">{$star} Lint Warnings</span>
+ {$more}
+ </x:frag>;
+ break;
+ case Diff::LINT_OKAY:
+ $fields['Lint'] =
+ <span class="star-okay">{$star} Lint Free</span>;
+ break;
+ default:
+ case Diff::LINT_NO:
+ $fields['Lint'] =
+ <span class="star-none">{$star} Not Linted</span>;
+ break;
+ }
+
+ $unit_details = false;
+ switch ($diff->getUnitTested()) {
+ case Diff::UNIT_FAIL:
+ $fields['Unit Tests'] =
+ <span class="star-warn">{$star} Unit Test Failures</span>;
+ $unit_details = true;
+ break;
+ case Diff::UNIT_WARN:
+ $fields['Unit Tests'] =
+ <span class="star-warn">{$star} Unit Test Warnings</span>;
+ $unit_details = true;
+ break;
+ case Diff::UNIT_OKAY:
+ $fields['Unit Tests'] =
+ <span class="star-okay">{$star} Unit Tests Passed</span>;
+ $unit_details = true;
+ break;
+ case Diff::UNIT_NO_TESTS:
+ $fields['Unit Tests'] =
+ <span class="star-none">{$star} No Test Coverage</span>;
+ break;
+ case Diff::UNIT_NO:
+ default:
+ $fields['Unit Tests'] =
+ <span class="star-none">{$star} Not Unit Tested</span>;
+ break;
+ }
+
+ if ($unit_details) {
+ $fields['Unit Tests'] =
+ <x:frag>
+ {$fields['Unit Tests']}
+ {$this->renderDiffPropertyMoreLink($diff, 'unit')}
+ </x:frag>;
+ }
+
+ $platform_impact = $revision->getPlatformImpact();
+ if ($platform_impact) {
+ $fields['Platform Impact'] =
+ <text linebreaks="true">{$platform_impact}</text>;
+ }
+
+ return $fields;
+ }
+
+ protected function renderDiffPropertyMoreLink(Diff $diff, $name) {
+ $target = <div class="star-more"
+ style="display: none;">
+ <div class="star-loading">Loading...</div>
+ </div>;
+ $meta = array(
+ 'target' => $target->requireUniqueID(),
+ 'uri' => '/differential/diffprop/'.$diff->getID().'/'.$name.'/',
+ );
+ $more =
+ <span sigil="star-link-container">
+ &middot;
+ <a mustcapture="true"
+ sigil="star-more"
+ href="#"
+ meta={$meta}>Show Details</a>
+ </span>;
+ return <x:frag>{$more}{$target}</x:frag>;
+ }
+
+
+
+ protected function loadInlineComments(array $feedback, array &$changesets) {
+
+ $inline_comments = array();
+ $feedback_ids = array_filter(array_pull($feedback, 'getID'));
+ if (!$feedback_ids) {
+ return $inline_comments;
+ }
+
+ $inline_comments = id(new DifferentialInlineComment())
+ ->loadAllWhere('feedbackID in (%Ld)', $feedback_ids);
+
+ $load_changesets = array();
+ $load_hunks = array();
+ foreach ($inline_comments as $inline) {
+ $changeset_id = $inline->getChangesetID();
+ if (isset($changesets[$changeset_id])) {
+ continue;
+ }
+ $load_changesets[$changeset_id] = true;
+ }
+
+ $more_changesets = array();
+ if ($load_changesets) {
+ $changeset_ids = array_keys($load_changesets);
+ $more_changesets += id(new DifferentialChangeset())
+ ->loadAllWithIDs($changeset_ids);
+ }
+
+ if ($more_changesets) {
+ $changesets += $more_changesets;
+ $changesets = array_psort($changesets, 'getSortKey');
+ }
+
+ return $inline_comments;
+ }
+
+ protected function getRevisionActions(DifferentialRevision $revision) {
+ $actions = array(
+ 'none' => true,
+ );
+
+ $viewer = $this->getRequest()->getViewerContext();
+
+ $viewer_is_owner = ($viewer->getUserID() == $revision->getOwnerID());
+ if ($viewer_is_owner) {
+ switch ($revision->getStatus()) {
+ case DifferentialConstants::NEEDS_REVIEW:
+ $actions['abandon'] = true;
+ break;
+ case DifferentialConstants::NEEDS_REVISION:
+ $actions['abandon'] = true;
+ $actions['request_review'] = true;
+ break;
+ case DifferentialConstants::ACCEPTED:
+ $actions['abandon'] = true;
+ $actions['request_review'] = true;
+ break;
+ case DifferentialConstants::COMMITTED:
+ break;
+ case DifferentialConstants::ABANDONED:
+ $actions['reclaim'] = true;
+ break;
+ default:
+ throw new Exception('Unknown DifferentialRevision status.');
+ }
+ } else {
+ switch ($revision->getStatus()) {
+ case DifferentialConstants::NEEDS_REVIEW:
+ $actions['accept'] = true;
+ $actions['reject'] = true;
+ break;
+ case DifferentialConstants::NEEDS_REVISION:
+ $actions['accept'] = true;
+ break;
+ case DifferentialConstants::ACCEPTED:
+ $actions['reject'] = true;
+ break;
+ case DifferentialConstants::COMMITTED:
+ break;
+ case DifferentialConstants::ABANDONED:
+ break;
+ default:
+ throw new Exception('Unknown DifferentialRevision status.');
+ }
+
+ if (in_array($viewer->getUserID(), $revision->getReviewers())) {
+ $actions['resign'] = true;
+ }
+ }
+
+ // Put add reviewers at the bottom since it's rare relative to other
+ // actions, notably accept and reject
+ $actions['add_reviewers'] = true;
+
+ static $action_names = array(
+ 'none' => 'Comment',
+ 'abandon' => 'Abandon Revision',
+ 'request_review' => 'Request Review',
+ 'reclaim' => 'Reclaim Revision',
+ 'accept' => "Accept Revision \xE2\x9C\x94",
+ 'reject' => "Request Changes \xE2\x9C\x98",
+ 'resign' => "Resign as Reviewer",
+ 'add_reviewers' => "Add Reviewers",
+ );
+
+ foreach ($actions as $key => $value) {
+ $actions[$key] = $action_names[$key];
+ }
+
+ return $actions;
+ }
+
+ protected function getRevisionStatusDisplay(DifferentialRevision $revision) {
+ $viewer_id = $this->getRequest()->getViewerContext()->getUserID();
+ $viewer_is_owner = ($viewer_id == $revision->getOwnerID());
+ $status = $revision->getStatus();
+
+ $more = null;
+ switch ($status) {
+ case DifferentialConstants::NEEDS_REVIEW:
+ $message = 'Pending Review';
+ break;
+ case DifferentialConstants::NEEDS_REVISION:
+ $message = 'Awaiting Revision';
+ if ($viewer_is_owner) {
+ $more = 'Make the requested changes and update the revision.';
+ }
+ break;
+ case DifferentialConstants::ACCEPTED:
+ $message = 'Ready for Commit';
+ if ($viewer_is_owner) {
+ $more =
+ <x:frag>
+ Run <tt>arc commit</tt> (svn) or <tt>arc amend</tt> (git) to
+ proceed.
+ </x:frag>;
+ }
+ break;
+ case DifferentialConstants::COMMITTED:
+ $message = 'Committed';
+ $ref = $revision->getRevisionRef();
+ $more = $ref
+ ? (<a href={URI($ref->getDetailURL())}>
+ {$ref->getName()}
+ </a>)
+ : null;
+
+ $engineering_repository_id = RepositoryRef::getByCallsign('E')->getID();
+ if ($revision->getSVNRevision() &&
+ $revision->getRepositoryID() == $engineering_repository_id) {
+ Javelin::initBehavior(
+ 'differential-revtracker-status',
+ array(
+ 'uri' => '/differential/revtracker/'.$revision->getID().'/',
+ 'statusId' => 'revtracker_status',
+ 'mergeLinkId' => 'ask_for_merge_link',
+ ));
+ }
+ break;
+ case DifferentialConstants::ABANDONED:
+ $message = 'Abandoned';
+ break;
+ default:
+ throw new Exception("Unknown revision status.");
+ }
+
+ if ($more) {
+ $message =
+ <x:frag>
+ <strong id="revtracker_status">{$message}</strong>
+ &middot; {$more}
+ </x:frag>;
+ } else {
+ $message = <strong id="revtracker_status">{$message}</strong>;
+ }
+
+ return $message;
+ }
+
+ protected function renderFeedbackList(array $xhp, array $obj, $viewer_id) {
+
+ // Use magical heuristics to try to hide older comments.
+
+ $obj = array_reverse($obj);
+ $obj = array_values($obj);
+ $xhp = array_reverse($xhp);
+ $xhp = array_values($xhp);
+
+ $last_comment = null;
+ foreach ($obj as $position => $feedback) {
+ if ($feedback->getUserID() == $viewer_id) {
+ if ($last_comment === null) {
+ $last_comment = $position;
+ } else if ($last_comment == $position - 1) {
+ // If you made consecuitive comments, show them all. This is a spaz
+ // rule for epriestley comments.
+ $last_comment = $position;
+ }
+ }
+ }
+
+ $header = array();
+
+ $hide = array();
+ if ($last_comment !== null) {
+ foreach ($obj as $position => $feedback) {
+ $action = $feedback->getAction();
+ if ($action == 'testplan' || $action == 'summarize') {
+ // Always show summary and test plan.
+ $header[] = $xhp[$position];
+ unset($xhp[$position]);
+ continue;
+ }
+
+ if ($position <= $last_comment) {
+ // Always show comments after your last comment.
+ continue;
+ }
+
+ if ($position < 3) {
+ // Always show the most recent 3 comments.
+ continue;
+ }
+
+ // Hide everything else.
+ $hide[] = $position;
+ }
+ }
+
+ if (count($hide) <= 3) {
+ // Don't hide if there's not much to hide.
+ $hide = array();
+ }
+
+ $header = array_reverse($header);
+
+ $hidden = array_select_keys($xhp, $hide);
+ $visible = array_diff_key($xhp, $hidden);
+
+ $visible = array_reverse($visible);
+ $hidden = array_reverse($hidden);
+
+ if ($hidden) {
+ Javelin::initBehavior(
+ 'differential-show-all-feedback',
+ array(
+ 'markup' => id(<x:frag>{$hidden}</x:frag>)->toString(),
+ ));
+ $hidden =
+ <div sigil="all-feedback-container">
+ <div class="older-replies-are-hidden">
+ {number_format(count($hidden))} older replies are hidden.
+ <a href="#" sigil="show-all-feedback"
+ mustcapture="true">Show all feedback.</a>
+ </div>
+ </div>;
+ } else {
+ $hidden = null;
+ }
+
+ return
+ <x:frag>
+ {$header}
+ {$hidden}
+ {$visible}
+ </x:frag>;
+ }
+
+}
+ protected function getDetailFields(
+ DifferentialRevision $revision,
+ Diff $diff,
+ array $handles) {
+
+ $fields = array();
+ $fields['Revision Status'] = $this->getRevisionStatusDisplay($revision);
+
+ $author = $revision->getOwnerID();
+ $fields['Author'] = <tools:handle handle={$handles[$author]}
+ link={true} />;
+
+ $sandcastle = $this->getSandcastleURI($diff);
+ if ($sandcastle) {
+ $fields['Sandcastle'] = <a href={$sandcastle}>{$sandcastle}</a>;
+ }
+
+ $path = $diff->getSourcePath();
+ if ($path) {
+ $host = $diff->getSourceMachine();
+ $branch = $diff->getGitBranch() ? ' (' . $diff->getGitBranch() . ')' : '';
+
+ if ($host) {
+ $user = $handles[$this->getRequest()->getViewerContext()->getUserID()]
+ ->getName();
+ $fields['Path'] =
+ <x:frag>
+ <a href={"ssh://{$user}@{$host}"}>{$host}</a>:{$path}{$branch}
+ </x:frag>;
+ } else {
+ $fields['Path'] = $path;
+ }
+ }
+
+ $reviewer_links = array();
+ foreach ($revision->getReviewers() as $reviewer) {
+ $reviewer_links[] = <tools:handle handle={$handles[$reviewer]}
+ link={true} />;
+ }
+ if ($reviewer_links) {
+ $fields['Reviewers'] = array_implode(', ', $reviewer_links);
+ } else {
+ $fields['Reviewers'] = <em>None</em>;
+ }
+
+ $ccs = $revision->getCCFBIDs();
+ if ($ccs) {
+ $links = array();
+ foreach ($ccs as $cc) {
+ $links[] = <tools:handle handle={$handles[$cc]}
+ link={true} />;
+ }
+ $fields['CCs'] = array_implode(', ', $links);
+ }
+
+ $blame_rev = $revision->getSvnBlameRevision();
+ if ($blame_rev) {
+ if ($revision->getRepositoryRef() && is_numeric($blame_rev)) {
+ $ref = new RevisionRef($revision->getRepositoryRef(), $blame_rev);
+ $fields['Blame Revision'] =
+ <a href={URI($ref->getDetailURL())}>
+ {$ref->getName()}
+ </a>;
+ } else {
+ $fields['Blame Revision'] = $blame_rev;
+ }
+ }
+
+ $tasks = $revision->getTaskHandles();
+
+ if ($tasks) {
+ $links = array();
+ foreach ($tasks as $task) {
+ $links[] = <tools:handle handle={$task} link={true} />;
+ }
+ $fields['Tasks'] = array_implode(<br />, $links);
+ }
+
+ $bugzilla_id = $revision->getBugzillaID();
+ if ($bugzilla_id) {
+ $href = 'http://bugs.developers.facebook.com/show_bug.cgi?id='.
+ $bugzilla_id;
+ $fields['Bugzilla'] = <a href={$href}>{'#'.$bugzilla_id}</a>;
+ }
+
+ $fields['Apply Patch'] = <tt>arc patch --revision {$revision->getID()}</tt>;
+
+ if ($diff->getParentRevisionID()) {
+ $parent = id(new DifferentialRevision())->load(
+ $diff->getParentRevisionID());
+ if ($parent) {
+ $fields['Depends On'] =
+ <a href={$parent->getURI()}>
+ D{$parent->getID()}: {$parent->getName()}
+ </a>;
+ }
+ }
+
+ $star = <span class="star">{"\xE2\x98\x85"}</span>;
+
+ Javelin::initBehavior('differential-star-more');
+
+ switch ($diff->getLinted()) {
+ case Diff::LINT_FAIL:
+ $more = $this->renderDiffPropertyMoreLink($diff, 'lint');
+ $fields['Lint'] =
+ <x:frag>
+ <span class="star-warn">{$star} Lint Failures</span>
+ {$more}
+ </x:frag>;
+ break;
+ case Diff::LINT_WARNINGS:
+ $more = $this->renderDiffPropertyMoreLink($diff, 'lint');
+ $fields['Lint'] =
+ <x:frag>
+ <span class="star-warn">{$star} Lint Warnings</span>
+ {$more}
+ </x:frag>;
+ break;
+ case Diff::LINT_OKAY:
+ $fields['Lint'] =
+ <span class="star-okay">{$star} Lint Free</span>;
+ break;
+ default:
+ case Diff::LINT_NO:
+ $fields['Lint'] =
+ <span class="star-none">{$star} Not Linted</span>;
+ break;
+ }
+
+ $unit_details = false;
+ switch ($diff->getUnitTested()) {
+ case Diff::UNIT_FAIL:
+ $fields['Unit Tests'] =
+ <span class="star-warn">{$star} Unit Test Failures</span>;
+ $unit_details = true;
+ break;
+ case Diff::UNIT_WARN:
+ $fields['Unit Tests'] =
+ <span class="star-warn">{$star} Unit Test Warnings</span>;
+ $unit_details = true;
+ break;
+ case Diff::UNIT_OKAY:
+ $fields['Unit Tests'] =
+ <span class="star-okay">{$star} Unit Tests Passed</span>;
+ $unit_details = true;
+ break;
+ case Diff::UNIT_NO_TESTS:
+ $fields['Unit Tests'] =
+ <span class="star-none">{$star} No Test Coverage</span>;
+ break;
+ case Diff::UNIT_NO:
+ default:
+ $fields['Unit Tests'] =
+ <span class="star-none">{$star} Not Unit Tested</span>;
+ break;
+ }
+
+ if ($unit_details) {
+ $fields['Unit Tests'] =
+ <x:frag>
+ {$fields['Unit Tests']}
+ {$this->renderDiffPropertyMoreLink($diff, 'unit')}
+ </x:frag>;
+ }
+
+ $platform_impact = $revision->getPlatformImpact();
+ if ($platform_impact) {
+ $fields['Platform Impact'] =
+ <text linebreaks="true">{$platform_impact}</text>;
+ }
+
+ return $fields;
+ }
+
+
+*/
diff --git a/src/applications/differential/controller/revisionview/__init__.php b/src/applications/differential/controller/revisionview/__init__.php
new file mode 100644
index 0000000000..d724343cd1
--- /dev/null
+++ b/src/applications/differential/controller/revisionview/__init__.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'aphront/response/404');
+phutil_require_module('phabricator', 'applications/differential/constants/revisionstatus');
+phutil_require_module('phabricator', 'applications/differential/controller/base');
+phutil_require_module('phabricator', 'applications/differential/storage/revision');
+phutil_require_module('phabricator', 'applications/differential/view/changesetlistview');
+phutil_require_module('phabricator', 'applications/differential/view/difftableofcontents');
+phutil_require_module('phabricator', 'applications/differential/view/revisiondetail');
+phutil_require_module('phabricator', 'applications/differential/view/revisionupdatehistory');
+phutil_require_module('phabricator', 'applications/phid/handle/data');
+
+phutil_require_module('phutil', 'markup');
+phutil_require_module('phutil', 'utils');
+
+
+phutil_require_source('DifferentialRevisionViewController.php');
diff --git a/src/applications/differential/storage/revision/DifferentialRevision.php b/src/applications/differential/storage/revision/DifferentialRevision.php
index a14989e48e..67856cba68 100755
--- a/src/applications/differential/storage/revision/DifferentialRevision.php
+++ b/src/applications/differential/storage/revision/DifferentialRevision.php
@@ -1,151 +1,160 @@
<?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 DifferentialRevision extends DifferentialDAO {
protected $title;
protected $status;
protected $summary;
protected $testPlan;
protected $revertPlan;
protected $blameRevision;
protected $phid;
protected $ownerPHID;
protected $dateCommitted;
protected $lineCount;
private $related;
private $forbidden;
const RELATIONSHIP_TABLE = 'differential_relationship';
const RELATION_REVIEWER = 'revw';
const RELATION_SUBSCRIBED = 'subd';
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID('DREV');
}
+ public function loadDiffs() {
+ if (!$this->getID()) {
+ return array();
+ }
+ return id(new DifferentialDiff())->loadAllWhere(
+ 'revisionID = %d',
+ $this->getID());
+ }
+
public function loadRelationships() {
if (!$this->getID()) {
$this->relationships = array();
$this->related = array();
$this->forbidden = array();
return;
}
$data = queryfx_all(
$this->establishConnection('r'),
'SELECT * FROM %T WHERE revisionID = %d ORDER BY sequence',
self::RELATIONSHIP_TABLE,
$this->getID());
$related = array();
$forbidden = array();
foreach ($data as $row) {
if ($row['forbidden']) {
$forbidden[] = $row;
} else {
$related[] = $row;
}
}
$this->related = igroup($related, 'relation');
$this->forbidden = igroup($related, 'relation');
$this->relationships = igroup($data, 'relation');
return $this;
}
public function getReviewers() {
return $this->getRelatedPHIDs(self::RELATION_REVIEWER);
}
public function getCCPHIDs() {
return $this->getRelatedPHIDs(self::RELATION_SUBSCRIBED);
}
private function getRelatedPHIDs($relation) {
if ($this->related === null) {
throw new Exception("Must load relationships!");
}
$related = idx($this->related, $relation, array());
return ipull($related, 'objectPHID');
}
public function getRawRelations($relation) {
return idx($this->relationships, $relation, array());
}
public function writeRelatedPHIDs(
$relation,
$phids,
$reason_phid,
$forbidden) {
$conn_w = $this->establishConnection('w');
$sql = array();
$phids = array_values($phids);
foreach ($phids as $key => $phid) {
$sql[] = qsprintf(
$conn_w,
'(%d, %s, %d, %s, %d)',
$this->getRevisionID(),
$phid,
$key,
$reason_phid,
$forbidden);
}
$conn_w->openTransaction();
queryfx(
$conn_w,
'DELETE FROM %T WHERE revisionID = %d AND relation = %s
AND forbidden = %d
AND relatedPHID NOT IN (%Ls)',
self::RELATIONSHIP_TABLE,
$this->getID(),
$relation,
$forbidden,
$phids);
queryfx(
$conn_w,
'INSERT INTO %T
(revisionID, relatedPHID, sequence, reason_phid, forbidden)
VALUES %Q
ON DUPLICATE KEY UPDATE sequence = VALUES(sequence)',
self::RELATIONSHIP_TABLE,
implode(', ', $sql));
$conn_w->saveTransaction();
}
}
diff --git a/src/applications/differential/storage/revision/__init__.php b/src/applications/differential/storage/revision/__init__.php
index 641a9e397b..651e976961 100644
--- a/src/applications/differential/storage/revision/__init__.php
+++ b/src/applications/differential/storage/revision/__init__.php
@@ -1,17 +1,18 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/differential/storage/base');
+phutil_require_module('phabricator', 'applications/differential/storage/diff');
phutil_require_module('phabricator', 'applications/phid/storage/phid');
phutil_require_module('phabricator', 'storage/qsprintf');
phutil_require_module('phabricator', 'storage/queryfx');
phutil_require_module('phutil', 'utils');
phutil_require_source('DifferentialRevision.php');
diff --git a/src/applications/differential/view/difftableofcontents/DifferentialDiffTableOfContentsView.php b/src/applications/differential/view/difftableofcontents/DifferentialDiffTableOfContentsView.php
index 3130159981..f755c0571f 100644
--- a/src/applications/differential/view/difftableofcontents/DifferentialDiffTableOfContentsView.php
+++ b/src/applications/differential/view/difftableofcontents/DifferentialDiffTableOfContentsView.php
@@ -1,122 +1,124 @@
<?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.
*/
final class DifferentialDiffTableOfContentsView extends AphrontView {
private $changesets = array();
public function setChangesets($changesets) {
$this->changesets = $changesets;
return $this;
}
public function render() {
+
+ require_celerity_resource('differential-core-view-css');
require_celerity_resource('differential-table-of-contents-css');
$rows = array();
$changesets = $this->changesets;
foreach ($changesets as $changeset) {
$file = $changeset->getFilename();
$display_file = $changeset->getDisplayFilename();
$type = $changeset->getChangeType();
$ftype = $changeset->getFileType();
if (DifferentialChangeType::isOldLocationChangeType($type)) {
$link = phutil_escape_html($display_file);
$away = $changeset->getAwayPaths();
if (count($away) > 1) {
$meta = array();
if ($type == DifferentialChangeType::TYPE_MULTICOPY) {
$meta[] = 'Deleted after being copied to multiple locations:';
} else {
$meta[] = 'Copied to multiple locations:';
}
foreach ($away as $path) {
$meta[] = $path;
}
$meta = implode('<br />', $meta);
} else {
if ($type == DifferentialChangeType::TYPE_MOVE_AWAY) {
$meta = 'Moved to '.reset($away);
} else {
$meta = 'Copied to '.reset($away);
}
}
} else {
$link = phutil_render_tag(
'a',
array(
'href' => '#', // TODO: filename normalizer
),
phutil_escape_html($display_file));
if ($type == DifferentialChangeType::TYPE_MOVE_HERE) {
$meta = 'Moved from '.phutil_escape_html($changeset->getOldFile());
} else if ($type == DifferentialChangeType::TYPE_COPY_HERE) {
$meta = 'Copied from '.phutil_escape_html($changeset->getOldFile());
} else {
$meta = null;
}
}
$line_count = $changeset->getAffectedLineCount();
if ($line_count == 0) {
$lines = null;
} else if ($line_count == 1) {
$lines = ' (1 line)';
} else {
$lines = ' ('.$line_count.' lines)';
}
$char = DifferentialChangeType::getSummaryCharacterForChangeType($type);
$chartitle = DifferentialChangeType::getFullNameForChangeType($type);
$desc = DifferentialChangeType::getShortNameForFileType($ftype);
if ($desc) {
$desc = '('.$desc.')';
}
$pchar =
($changeset->getOldProperties() === $changeset->getNewProperties())
? null
: '<span title="Properties Changed">M</span>';
$rows[] =
'<tr>'.
'<td class="differential-toc-char" title='.$chartitle.'>'.$char.'</td>'.
'<td class="differential-toc-prop">'.$pchar.'</td>'.
'<td class="differential-toc-ftype">'.$desc.'</td>'.
'<td class="differential-toc-file">'.$link.$lines.'</td>'.
'</tr>';
if ($meta) {
$rows[] =
'<tr>'.
'<td colspan="3"></td>'.
'<td class="differential-toc-meta">'.$meta.'</td>'.
'</tr>';
}
}
return
- '<div class="differential-toc">'.
+ '<div class="differential-toc differential-panel">'.
'<h1>Table of Contents</h1>'.
'<table>'.
implode("\n", $rows).
'</table>'.
'</div>';
}
}
diff --git a/src/applications/differential/view/revisiondetail/DifferentialRevisionDetailView.php b/src/applications/differential/view/revisiondetail/DifferentialRevisionDetailView.php
new file mode 100644
index 0000000000..6faf3a62de
--- /dev/null
+++ b/src/applications/differential/view/revisiondetail/DifferentialRevisionDetailView.php
@@ -0,0 +1,90 @@
+<?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.
+ */
+
+final class DifferentialRevisionDetailView extends AphrontView {
+
+ private $revision;
+ private $properties;
+ private $actions;
+
+ public function setRevision($revision) {
+ $this->revision = $revision;
+ return $this;
+ }
+
+ public function setProperties(array $properties) {
+ $this->properties = $properties;
+ return $this;
+ }
+
+ public function setActions(array $actions) {
+ $this->actions = $actions;
+ return $this;
+ }
+
+ public function render() {
+
+ require_celerity_resource('differential-core-view-css');
+ require_celerity_resource('differential-revision-detail-css');
+
+ $revision = $this->revision;
+
+ $rows = array();
+ foreach ($this->properties as $key => $field) {
+ $rows[] =
+ '<tr>'.
+ '<th>'.phutil_escape_html($key).':</th>'.
+ '<td>'.$field.'</td>'.
+ '</tr>';
+ }
+
+ $properties =
+ '<table class="differential-revision-properties">'.
+ implode("\n", $rows).
+ '</table>';
+
+ $actions = array();
+ foreach ($this->actions as $action) {
+ if (empty($action['href'])) {
+ $tag = 'span';
+ } else {
+ $tag = 'a';
+ }
+ $actions[] = phutil_render_tag(
+ $tag,
+ array(
+ 'href' => idx($action, 'href'),
+ 'class' => idx($action, 'class'),
+ ),
+ phutil_escape_html($action['name']));
+ }
+ $actions = implode("\n", $actions);
+
+ return
+ '<div class="differential-revision-detail differential-panel">'.
+ '<div class="differential-revision-actions">'.
+ $actions.
+ '</div>'.
+ '<div class="differential-revision-detail-core">'.
+ '<h1>'.phutil_escape_html($revision->getTitle()).'</h1>'.
+ $properties.
+ '</div>'.
+ '<div style="clear: both;"></div>'.
+ '</div>';
+ }
+}
diff --git a/src/applications/differential/view/revisiondetail/__init__.php b/src/applications/differential/view/revisiondetail/__init__.php
new file mode 100644
index 0000000000..9eb662528a
--- /dev/null
+++ b/src/applications/differential/view/revisiondetail/__init__.php
@@ -0,0 +1,16 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'infratructure/celerity/api');
+phutil_require_module('phabricator', 'view/base');
+
+phutil_require_module('phutil', 'markup');
+phutil_require_module('phutil', 'utils');
+
+
+phutil_require_source('DifferentialRevisionDetailView.php');
diff --git a/src/applications/differential/view/revisionupdatehistory/DifferentialRevisionUpdateHistoryView.php b/src/applications/differential/view/revisionupdatehistory/DifferentialRevisionUpdateHistoryView.php
new file mode 100644
index 0000000000..d03ead76b8
--- /dev/null
+++ b/src/applications/differential/view/revisionupdatehistory/DifferentialRevisionUpdateHistoryView.php
@@ -0,0 +1,121 @@
+<?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.
+ */
+
+final class DifferentialRevisionUpdateHistoryView extends AphrontView {
+
+ private $diffs = array();
+
+ public function setDiffs($diffs) {
+ $this->diffs = $diffs;
+ return $this;
+ }
+
+ public function render() {
+
+ require_celerity_resource('differential-core-view-css');
+ require_celerity_resource('differential-revision-history-css');
+
+ $data = array(
+ array(
+ 'name' => 'Base',
+ 'id' => null,
+ 'desc' => 'Base',
+ 'old' => false,
+ 'new' => false,
+ 'age' => null,
+ 'lint' => null,
+ 'unit' => null,
+ ),
+ );
+
+ $seq = 0;
+ foreach ($this->diffs as $diff) {
+ $data[] = array(
+ 'name' => 'Diff '.(++$seq),
+ 'id' => $diff->getID(),
+ 'desc' => 'TODO',//$diff->getDescription(),
+ 'old' => false,
+ 'new' => false,
+ 'age' => $diff->getDateCreated(),
+ 'lint' => $diff->getLintStatus(),
+ 'unit' => $diff->getUnitStatus(),
+ );
+ }
+
+ $idx = 0;
+ $rows = array();
+ foreach ($data as $row) {
+
+ $name = phutil_escape_html($row['name']);
+ $id = phutil_escape_html($row['id']);
+
+ $lint = '*';
+ $unit = '*';
+ $old = '<input type="radio" name="old" />';
+ $new = '<input type="radio" name="new" />';
+
+ $desc = 'TODO';
+ $age = '-';
+
+ if (++$idx % 2) {
+ $class = ' class="alt"';
+ } else {
+ $class = null;
+ }
+
+ $rows[] =
+ '<tr'.$class.'>'.
+ '<td class="revhistory-name">'.$name.'</td>'.
+ '<td class="revhistory-id">'.$id.'</td>'.
+ '<td class="revhistory-desc">'.$desc.'</td>'.
+ '<td class="revhistory-age">'.$age.'</td>'.
+ '<td class="revhistory-star">'.$lint.'</td>'.
+ '<td class="revhistory-star">'.$unit.'</td>'.
+ '<td class="revhistory-old">'.$old.'</td>'.
+ '<td class="revhistory-new">'.$new.'</td>'.
+ '</tr>';
+ }
+
+ $select = '<select><option>Ignore All</option></select>';
+
+ return
+ '<div class="differential-revision-history differential-panel">'.
+ '<h1>Revision Update History</h1>'.
+ '<form>'.
+ '<table class="differential-revision-history-table">'.
+ '<tr>'.
+ '<th>Diff</th>'.
+ '<th>ID</th>'.
+ '<th>Description</th>'.
+ '<th>Age</th>'.
+ '<th>Lint</th>'.
+ '<th>Unit</th>'.
+ '</tr>'.
+ implode("\n", $rows).
+ '<tr>'.
+ '<td colspan="8" class="diff-differ-submit">'.
+ '<label>Whitespace Changes: '.$select.'</label>'.
+ '<button class="disabled"
+ disabled="disabled">Show Diff</button>'.
+ '</td>'.
+ '</tr>'.
+ '</table>'.
+ '</form>'.
+ '</div>';
+ }
+}
diff --git a/src/applications/differential/view/revisionupdatehistory/__init__.php b/src/applications/differential/view/revisionupdatehistory/__init__.php
new file mode 100644
index 0000000000..6e88bf893d
--- /dev/null
+++ b/src/applications/differential/view/revisionupdatehistory/__init__.php
@@ -0,0 +1,15 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'infratructure/celerity/api');
+phutil_require_module('phabricator', 'view/base');
+
+phutil_require_module('phutil', 'markup');
+
+
+phutil_require_source('DifferentialRevisionUpdateHistoryView.php');
diff --git a/src/applications/phid/handle/PhabricatorObjectHandle.php b/src/applications/phid/handle/PhabricatorObjectHandle.php
index dc379e53d5..5ef9dd861e 100644
--- a/src/applications/phid/handle/PhabricatorObjectHandle.php
+++ b/src/applications/phid/handle/PhabricatorObjectHandle.php
@@ -1,74 +1,81 @@
<?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 PhabricatorObjectHandle {
private $uri;
private $phid;
private $type;
private $name;
private $email;
public function setURI($uri) {
$this->uri = $uri;
return $this;
}
public function getURI() {
return $this->uri;
}
public function setPHID($phid) {
$this->phid = $phid;
return $this;
}
public function getPHID() {
return $this->phid;
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function getName() {
return $this->name;
}
public function setType($type) {
$this->type = $type;
return $this;
}
public function getType() {
return $this->type;
}
public function setEmail($email) {
$this->email = $email;
return $this;
}
public function getEmail() {
return $this->email;
}
-
+ public function renderLink() {
+ return phutil_render_tag(
+ 'a',
+ array(
+ 'href' => $this->getURI(),
+ ),
+ phutil_escape_html($this->getName()));
+ }
}
diff --git a/src/applications/phid/handle/__init__.php b/src/applications/phid/handle/__init__.php
index e66e9c70a0..781f961e90 100644
--- a/src/applications/phid/handle/__init__.php
+++ b/src/applications/phid/handle/__init__.php
@@ -1,10 +1,12 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
+phutil_require_module('phutil', 'markup');
+
phutil_require_source('PhabricatorObjectHandle.php');
diff --git a/webroot/rsrc/css/application/differential/core.css b/webroot/rsrc/css/application/differential/core.css
index f2aad4918a..fdc49a1959 100644
--- a/webroot/rsrc/css/application/differential/core.css
+++ b/webroot/rsrc/css/application/differential/core.css
@@ -1,9 +1,23 @@
/**
* @provides differential-core-view-css
*/
.differential-primary-pane {
margin: 0 40px;
max-width: 1162px;
}
+.differential-panel {
+ margin: 25px 0;
+ max-width: 1118px;
+ border: 1px solid #666622;
+ background: #efefdf;
+ padding: 15px 20px;
+ font-size: 13px;
+}
+
+.differential-panel h1 {
+ border-bottom: 1px solid #aaaa99;
+ padding-bottom: 8px;
+ margin-bottom: 8px;
+}
diff --git a/webroot/rsrc/css/application/differential/revision-detail.css b/webroot/rsrc/css/application/differential/revision-detail.css
new file mode 100644
index 0000000000..0e91516ae9
--- /dev/null
+++ b/webroot/rsrc/css/application/differential/revision-detail.css
@@ -0,0 +1,68 @@
+/**
+ * @provides differential-revision-detail-css
+ */
+
+.differential-revision-properties {
+ font-size: 12px;
+ width: 100%;
+}
+
+.differential-revision-properties tt {
+ letter-spacing: 1.1px;
+}
+
+.differential-revision-properties th {
+ font-weight: bold;
+ width: 100px;
+ text-align: right;
+ padding: 3px 4px 3px 3px;
+ color: #333333;
+ white-space: nowrap;
+}
+
+.differential-revision-properties td {
+ padding: 3px 2px;
+}
+
+.differential-revision-actions {
+ float: right;
+ width: 250px;
+ background: #cfcfbf;
+ border: 1px solid #666622;
+ border-width: 0px 0px 1px 1px;
+ margin: -15px -20px 1em 0;
+ font-size: 11px;
+}
+
+.differential-revision-detail-core {
+ margin-right: 265px;
+}
+
+.differential-revision-actions a,
+.differential-revision-actions span {
+ background-position: 8px center;
+ background-repeat: no-repeat;
+ display: block;
+ padding: 4px 4px 4px 32px;
+}
+
+.differential-revision-actions span.unavailable {
+ color: #666666;
+ font-style: italic;
+}
+
+.differential-revision-actions .subscribe-rem {
+ background-image: url(/rsrc/image/icon/unsubscribe.png);
+}
+
+.differential-revision-actions .revision-edit {
+ background-image: url(/rsrc/image/icon/tango/edit.png);
+}
+
+.differential-revision-actions .revision-edit {
+ background-image: url(/rsrc/image/icon/tango/edit.png);
+}
+
+.differential-revision-actions .transcripts-metamta {
+ background-image: url(/rsrc/image/icon/tango/log.png);
+}
diff --git a/webroot/rsrc/css/application/differential/revision-history.css b/webroot/rsrc/css/application/differential/revision-history.css
new file mode 100644
index 0000000000..8b20e8a6f3
--- /dev/null
+++ b/webroot/rsrc/css/application/differential/revision-history.css
@@ -0,0 +1,87 @@
+/**
+ * @provides differential-revision-history-css
+ */
+
+.differential-revision-history-table {
+ width: 100%;
+ border-collapse: separate;
+ border-spacing: 1px;
+}
+
+.differential-revision-history-table th {
+ padding: 0 .5em;
+ color: #666666;
+}
+
+.differential-revision-history-table td {
+ padding: 4px;
+}
+
+.differential-revision-history-table td {
+ white-space: nowrap;
+}
+
+.differential-revision-history-table tr.alt {
+ background: #ddddcc;
+}
+
+
+.differential-revision-history-table td.revhistory-desc {
+ width: 100%;
+ white-space: normal;
+ padding-left: .5em;
+}
+
+.differential-revision-history-table td.revhistory-name {
+ font-weight: bold;
+ padding-right: .5em;
+ padding-left: .5em;
+}
+
+.differential-revision-history-table td.revhistory-age {
+ text-align: right;
+}
+
+.differential-revision-history-table td.revhistory-old,
+.differential-revision-history-table td.revhistory-new {
+ padding: 0em 1.5em;
+ text-align: center;
+}
+
+.differential-revision-history-table td.revhistory-old {
+ background: #f9d0d0;
+}
+
+.differential-revision-history-table td.revhistory-old-now {
+ background: #ffaaaa;
+}
+
+.differential-revision-history-table td.revhistory-new {
+ background: #d0ffd0;
+}
+
+.differential-revision-history-table td.revhistory-new-now {
+ background: #aaffaa;
+}
+
+.differential-revision-history-table td.revhistory-star {
+ text-align: center;
+}
+
+
+
+
+.differential-revision-history-table td.diff-differ-submit {
+ text-align: right;
+ border-bottom: none;
+}
+
+.differential-revision-history-table td.diff-differ-submit button {
+ margin-left: 1em;
+}
+
+.differential-revision-history-table td.diff-differ-submit label {
+ font-weight: bold;
+ padding-right: .25em;
+ color: #444444;
+}
diff --git a/webroot/rsrc/css/application/differential/table-of-contents.css b/webroot/rsrc/css/application/differential/table-of-contents.css
index a50c91705c..c79d38a955 100644
--- a/webroot/rsrc/css/application/differential/table-of-contents.css
+++ b/webroot/rsrc/css/application/differential/table-of-contents.css
@@ -1,40 +1,25 @@
/**
* @provides differential-table-of-contents-css
*/
-.differential-toc {
- margin: 25px 0;
- max-width: 1118px;
- border: 1px solid #666622;
- background: #efefdf;
- padding: 15px 20px;
- font-size: 13px;
-}
-
.differential-toc-meta {
color: #666666;
padding-left: 1em;
}
.differential-toc-char,
.differential-toc-prop {
width: 1.25em;
text-align: center;
font-weight: bold;
}
.differential-toc-ftype {
padding: 0 .5em;
text-align: center;
color: #666666;
}
.differential-toc-file {
color: #444444;
}
-
-.differential-toc h1 {
- border-bottom: 1px solid #aaaa99;
- padding-bottom: 8px;
- margin-bottom: 8px;
-}
diff --git a/webroot/rsrc/image/icon/subscribe.png b/webroot/rsrc/image/icon/subscribe.png
new file mode 100644
index 0000000000..34ec92ee5e
Binary files /dev/null and b/webroot/rsrc/image/icon/subscribe.png differ
diff --git a/webroot/rsrc/image/icon/tango/README b/webroot/rsrc/image/icon/tango/README
new file mode 100644
index 0000000000..f18ea4955d
--- /dev/null
+++ b/webroot/rsrc/image/icon/tango/README
@@ -0,0 +1,3 @@
+These icons come from the Tango Desktop Project:
+
+ http://tango.freedesktop.org/
diff --git a/webroot/rsrc/image/icon/tango/edit.png b/webroot/rsrc/image/icon/tango/edit.png
new file mode 100644
index 0000000000..188e1c12bd
Binary files /dev/null and b/webroot/rsrc/image/icon/tango/edit.png differ
diff --git a/webroot/rsrc/image/icon/tango/log.png b/webroot/rsrc/image/icon/tango/log.png
new file mode 100644
index 0000000000..2d7f2d6017
Binary files /dev/null and b/webroot/rsrc/image/icon/tango/log.png differ
diff --git a/webroot/rsrc/image/icon/unsubscribe.png b/webroot/rsrc/image/icon/unsubscribe.png
new file mode 100644
index 0000000000..cd725e6e5c
Binary files /dev/null and b/webroot/rsrc/image/icon/unsubscribe.png differ

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jul 29, 1:15 PM (3 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
188673
Default Alt Text
(110 KB)

Event Timeline