Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index ce345f6e52..c8c97ed5ec 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1,325 +1,327 @@
<?php
/**
* This file is automatically generated. Use 'phutil_mapper.php' to rebuild it.
* @generated
*/
phutil_register_library_map(array(
'class' =>
array(
'Aphront400Response' => 'aphront/response/400',
'Aphront404Response' => 'aphront/response/404',
'AphrontAjaxResponse' => 'aphront/response/ajax',
'AphrontApplicationConfiguration' => 'aphront/applicationconfiguration',
'AphrontController' => 'aphront/controller',
'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',
'AphrontRequestFailureView' => 'view/page/failure',
'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',
'DifferentialAddCommentView' => 'applications/differential/view/addcomment',
'DifferentialCCWelcomeMail' => 'applications/differential/mail/ccwelcome',
'DifferentialChangeType' => 'applications/differential/constants/changetype',
'DifferentialChangeset' => 'applications/differential/storage/changeset',
'DifferentialChangesetDetailView' => 'applications/differential/view/changesetdetailview',
'DifferentialChangesetListView' => 'applications/differential/view/changesetlistview',
'DifferentialChangesetParser' => 'applications/differential/parser/changeset',
'DifferentialChangesetViewController' => 'applications/differential/controller/changesetview',
'DifferentialComment' => 'applications/differential/storage/comment',
'DifferentialCommentEditor' => 'applications/differential/editor/comment',
'DifferentialCommentMail' => 'applications/differential/mail/comment',
'DifferentialCommentSaveController' => 'applications/differential/controller/commentsave',
'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',
'DifferentialHunk' => 'applications/differential/storage/hunk',
'DifferentialLintStatus' => 'applications/differential/constants/lintstatus',
'DifferentialMail' => 'applications/differential/mail/base',
'DifferentialMarkupEngineFactory' => 'applications/differential/parser/markup',
'DifferentialNewDiffMail' => 'applications/differential/mail/newdiff',
'DifferentialReviewRequestMail' => 'applications/differential/mail/reviewrequest',
'DifferentialRevision' => 'applications/differential/storage/revision',
'DifferentialRevisionCommentListView' => 'applications/differential/view/revisioncommentlist',
'DifferentialRevisionCommentView' => 'applications/differential/view/revisioncomment',
'DifferentialRevisionControlSystem' => 'applications/differential/constants/revisioncontrolsystem',
'DifferentialRevisionDetailView' => 'applications/differential/view/revisiondetail',
'DifferentialRevisionEditController' => 'applications/differential/controller/revisionedit',
'DifferentialRevisionEditor' => 'applications/differential/editor/revision',
'DifferentialRevisionListController' => 'applications/differential/controller/revisionlist',
'DifferentialRevisionListData' => 'applications/differential/data/revisionlist',
'DifferentialRevisionStatus' => 'applications/differential/constants/revisionstatus',
'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/revisionupdatehistory',
'DifferentialRevisionViewController' => 'applications/differential/controller/revisionview',
'DifferentialUnitStatus' => 'applications/differential/constants/unitstatus',
'Javelin' => 'infratructure/javelin/api',
'LiskDAO' => 'storage/lisk/dao',
'Phabricator404Controller' => 'applications/base/controller/404',
'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',
+ 'PhabricatorLogoutController' => 'applications/auth/controlller/logout',
'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',
'phabricator_format_relative_time' => 'view/utils',
'phabricator_format_timestamp' => 'view/utils',
'phabricator_format_units_generic' => 'view/utils',
'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(
'Aphront400Response' => 'AphrontResponse',
'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',
'AphrontRequestFailureView' => 'AphrontView',
'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',
'DifferentialAddCommentView' => 'AphrontView',
'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail',
'DifferentialChangeset' => 'DifferentialDAO',
'DifferentialChangesetDetailView' => 'AphrontView',
'DifferentialChangesetListView' => 'AphrontView',
'DifferentialChangesetViewController' => 'DifferentialController',
'DifferentialComment' => 'DifferentialDAO',
'DifferentialCommentMail' => 'DifferentialMail',
'DifferentialCommentSaveController' => 'DifferentialController',
'DifferentialController' => 'PhabricatorController',
'DifferentialDAO' => 'PhabricatorLiskDAO',
'DifferentialDiff' => 'DifferentialDAO',
'DifferentialDiffContentMail' => 'DifferentialMail',
'DifferentialDiffProperty' => 'DifferentialDAO',
'DifferentialDiffTableOfContentsView' => 'AphrontView',
'DifferentialDiffViewController' => 'DifferentialController',
'DifferentialHunk' => 'DifferentialDAO',
'DifferentialNewDiffMail' => 'DifferentialReviewRequestMail',
'DifferentialReviewRequestMail' => 'DifferentialMail',
'DifferentialRevision' => 'DifferentialDAO',
'DifferentialRevisionCommentListView' => 'AphrontView',
'DifferentialRevisionCommentView' => 'AphrontView',
'DifferentialRevisionDetailView' => 'AphrontView',
'DifferentialRevisionEditController' => 'DifferentialController',
'DifferentialRevisionListController' => 'DifferentialController',
'DifferentialRevisionUpdateHistoryView' => 'AphrontView',
'DifferentialRevisionViewController' => 'DifferentialController',
'Phabricator404Controller' => 'PhabricatorController',
'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',
+ 'PhabricatorLogoutController' => '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 4344d37c7e..5082398cd4 100644
--- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
+++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
@@ -1,186 +1,187 @@
<?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',
'comment/' => array(
'preview/$' => 'DifferentialCommentPreviewController',
'save/$' => 'DifferentialCommentSaveController',
'inline/' => array(
'preview/$' => 'DifferentialInlineCommentPreviewController',
'edit/$' => 'DifferentialInlineCommentEditController',
),
),
),
'/res/' => array(
'(?<package>pkg/)?(?<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',
+ '/logout/' => 'PhabricatorLogoutController',
);
}
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;
}
} else if ($response instanceof Aphront404Response) {
$failure = new AphrontRequestFailureView();
$failure->setHeader('404 Not Found');
$failure->appendChild(
'<p>The page you requested was not found.</p>');
$view = new PhabricatorStandardPageView();
$view->setTitle('404 Not Found');
$view->appendChild($failure);
$response = new AphrontWebpageResponse();
$response->setContent($view->render());
$response->setHTTPResponseCode(404);
return $response;
}
return $response;
}
public function build404Controller() {
return array(new Phabricator404Controller($this->getRequest()), array());
}
}
diff --git a/src/aphront/request/AphrontRequest.php b/src/aphront/request/AphrontRequest.php
index 4ea9114b8e..6cc50fa2f5 100644
--- a/src/aphront/request/AphrontRequest.php
+++ b/src/aphront/request/AphrontRequest.php
@@ -1,125 +1,127 @@
<?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 AphrontRequest {
const TYPE_AJAX = '__ajax__';
const TYPE_FORM = '__form__';
private $host;
private $path;
private $requestData;
private $user;
final public function __construct($host, $path) {
$this->host = $host;
$this->path = $path;
}
final public function setRequestData(array $request_data) {
$this->requestData = $request_data;
return $this;
}
final public function getPath() {
return $this->path;
}
final public function getHost() {
return $this->host;
}
final public function getInt($name, $default = null) {
if (isset($this->requestData[$name])) {
return (int)$this->requestData[$name];
} else {
return $default;
}
}
final public function getStr($name, $default = null) {
if (isset($this->requestData[$name])) {
return (string)$this->requestData[$name];
} else {
return $default;
}
}
final public function getArr($name, $default = array()) {
if (isset($this->requestData[$name]) &&
is_array($this->requestData[$name])) {
return $this->requestData[$name];
} else {
return $default;
}
}
final public function getExists($name) {
return array_key_exists($name, $this->requestData);
}
final public function isHTTPPost() {
return ($_SERVER['REQUEST_METHOD'] == 'POST');
}
final public function isAjax() {
return $this->getExists(self::TYPE_AJAX);
}
final public function isFormPost() {
- return $this->getExists(self::TYPE_FORM) && $this->isHTTPPost();
+ return $this->getExists(self::TYPE_FORM) &&
+ $this->isHTTPPost() &&
+ $this->getUser()->validateCSRFToken($this->getStr('__csrf__'));
}
final public function getCookie($name, $default = null) {
return idx($_COOKIE, $name, $default);
}
final public function clearCookie($name) {
$this->setCookie($name, '', time() - (60 * 60 * 24 * 30));
}
final public function setCookie($name, $value, $expire = null) {
if ($expire === null) {
$expire = time() + (60 * 60 * 24 * 365 * 5);
}
setcookie(
$name,
$value,
$expire,
$path = '/',
$domain = '',
$secure = false,
$http_only = true);
}
final public function setUser($user) {
$this->user = $user;
return $this;
}
final public function getUser() {
return $this->user;
}
}
diff --git a/src/applications/auth/controlller/login/PhabricatorLoginController.php b/src/applications/auth/controlller/login/PhabricatorLoginController.php
index 914531230c..3d8536e8d3 100644
--- a/src/applications/auth/controlller/login/PhabricatorLoginController.php
+++ b/src/applications/auth/controlller/login/PhabricatorLoginController.php
@@ -1,122 +1,123 @@
<?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 PhabricatorLoginController extends PhabricatorAuthController {
public function shouldRequireLogin() {
return false;
}
public function processRequest() {
$request = $this->getRequest();
$error = false;
- $login_name = $request->getCookie('phu');
+ $login_name = $request->getCookie('phusr');
if ($request->isFormPost()) {
$login_name = $request->getStr('login');
$user = id(new PhabricatorUser())->loadOneWhere(
'username = %s',
$login_name);
$user->setPassword('asdf');
$user->save();
$okay = false;
if ($user) {
if ($user->comparePassword($request->getStr('password'))) {
$conn_w = $user->establishConnection('w');
$urandom = fopen('/dev/urandom', 'r');
if (!$urandom) {
throw new Exception("Failed to open /dev/urandom!");
}
$entropy = fread($urandom, 20);
if (strlen($entropy) != 20) {
throw new Exception("Failed to read /dev/urandom!");
}
$session_key = sha1($entropy);
queryfx(
$conn_w,
'INSERT INTO phabricator_session '.
'(userPHID, type, sessionKey, sessionStart)'.
' VALUES '.
'(%s, %s, %s, UNIX_TIMESTAMP()) '.
'ON DUPLICATE KEY UPDATE '.
'sessionKey = VALUES(sessionKey), '.
'sessionStart = VALUES(sessionStart)',
$user->getPHID(),
'web',
$session_key);
$request->setCookie('phusr', $user->getUsername());
$request->setCookie('phsid', $session_key);
return id(new AphrontRedirectResponse())
->setURI('/');
}
}
if (!$okay) {
$request->clearCookie('phusr');
$request->clearCookie('phsid');
}
$error = true;
}
$error_view = null;
if ($error) {
$error_view = new AphrontErrorView();
$error_view->setTitle('Bad username/password.');
}
$form = new AphrontFormView();
$form
+ ->setUser($request->getUser())
->setAction('/login/')
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Login')
->setName('login')
->setValue($login_name))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Password')
->setName('password'))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Login'));
$panel = new AphrontPanelView();
$panel->setHeader('Phabricator Login');
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
$panel->appendChild($form);
return $this->buildStandardPageResponse(
array(
$error_view,
$panel,
),
array(
'title' => 'Login',
));
}
}
diff --git a/src/applications/auth/controlller/logout/PhabricatorLogoutController.php b/src/applications/auth/controlller/logout/PhabricatorLogoutController.php
new file mode 100644
index 0000000000..7570c251b5
--- /dev/null
+++ b/src/applications/auth/controlller/logout/PhabricatorLogoutController.php
@@ -0,0 +1,37 @@
+<?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 PhabricatorLogoutController extends PhabricatorAuthController {
+
+ public function shouldRequireLogin() {
+ return true;
+ }
+
+ public function processRequest() {
+ $request = $this->getRequest();
+
+ if ($request->isFormPost()) {
+ $request->clearCookie('phsid');
+ return id(new AphrontRedirectResponse())
+ ->setURI('/login/');
+ }
+
+ return id(new AphrontRedirectResponse())->setURI('/');
+ }
+
+}
diff --git a/src/applications/auth/controlller/logout/__init__.php b/src/applications/auth/controlller/logout/__init__.php
new file mode 100644
index 0000000000..4665a47f1f
--- /dev/null
+++ b/src/applications/auth/controlller/logout/__init__.php
@@ -0,0 +1,15 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'aphront/response/redirect');
+phutil_require_module('phabricator', 'applications/auth/controlller/base');
+
+phutil_require_module('phutil', 'utils');
+
+
+phutil_require_source('PhabricatorLogoutController.php');
diff --git a/src/applications/conduit/controller/console/PhabricatorConduitConsoleController.php b/src/applications/conduit/controller/console/PhabricatorConduitConsoleController.php
index 3e8aa0befb..01180b8539 100644
--- a/src/applications/conduit/controller/console/PhabricatorConduitConsoleController.php
+++ b/src/applications/conduit/controller/console/PhabricatorConduitConsoleController.php
@@ -1,179 +1,183 @@
<?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 PhabricatorConduitConsoleController
extends PhabricatorConduitController {
private $method;
public function willProcessRequest(array $data) {
$this->method = idx($data, 'method');
}
public function processRequest() {
+
+ $request = $this->getRequest();
+
$methods = $this->getAllMethods();
if (empty($methods[$this->method])) {
$this->method = key($methods);
}
$method_class = $methods[$this->method];
PhutilSymbolLoader::loadClass($method_class);
$method_object = newv($method_class, array());
$error_description = array();
$error_types = $method_object->defineErrorTypes();
if ($error_types) {
$error_description[] = '<ul>';
foreach ($error_types as $error => $meaning) {
$error_description[] =
'<li>'.
'<strong>'.phutil_escape_html($error).':</strong> '.
phutil_escape_html($meaning).
'</li>';
}
$error_description[] = '</ul>';
$error_description = implode("\n", $error_description);
} else {
$error_description = "This method does not raise any specific errors.";
}
$form = new AphrontFormView();
$form
+ ->setUser($request->getUser())
->setAction('/api/'.$this->method)
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Description')
->setValue($method_object->getMethodDescription()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Returns')
->setValue($method_object->defineReturnType()))
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel('Errors')
->setValue($error_description))
->appendChild(
'<p class="aphront-form-instructions">Enter parameters using '.
'<strong>JSON</strong>. For instance, to enter a list, type: '.
'<tt>["apple", "banana", "cherry"]</tt>');
$params = $method_object->defineParamTypes();
foreach ($params as $param => $desc) {
$form->appendChild(
id(new AphrontFormTextControl())
->setLabel($param)
->setName("params[{$param}]")
->setCaption($desc));
}
$form
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Output Format')
->setName('output')
->setOptions(
array(
'human' => 'Human Readable',
'json' => 'JSON',
)))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Call Method'));
$panel = new AphrontPanelView();
$panel->setHeader('Conduit API: '.phutil_escape_html($this->method));
$panel->appendChild($form);
$panel->setWidth(AphrontPanelView::WIDTH_WIDE);
$view = new AphrontSideNavView();
foreach ($this->buildNavItems() as $item) {
$view->addNavItem($item);
}
$view->appendChild($panel);
return $this->buildStandardPageResponse(
array($view),
array(
'title' => 'Conduit Console',
'tab' => 'console',
));
}
private function buildNavItems() {
$classes = $this->getAllMethodImplementationClasses();
$method_names = array();
foreach ($classes as $method_class) {
$method_name = ConduitAPIMethod::getAPIMethodNameFromClassName(
$method_class);
$method_names[] = array(
'full_name' => $method_name,
'group_name' => reset(explode('.', $method_name)),
);
}
$method_names = igroup($method_names, 'group_name');
ksort($method_names);
$items = array();
foreach ($method_names as $group => $methods) {
$items[] = phutil_render_tag(
'a',
array(
),
phutil_escape_html($group));
foreach ($methods as $method) {
$method_name = $method['full_name'];
$selected = ($method_name == $this->method);
$items[] = phutil_render_tag(
'a',
array(
'class' => $selected ? 'aphront-side-nav-selected' : null,
'href' => '/conduit/method/'.$method_name,
),
'<span style="padding-left: 1em;">'.
phutil_escape_html($method_name).
'</span>');
}
$items[] = '<hr />';
}
// Pop off the last '<hr />'.
array_pop($items);
return $items;
}
private function getAllMethods() {
$classes = $this->getAllMethodImplementationClasses();
$methods = array();
foreach ($classes as $class) {
$name = ConduitAPIMethod::getAPIMethodNameFromClassName($class);
$methods[$name] = $class;
}
return $methods;
}
private function getAllMethodImplementationClasses() {
$classes = id(new PhutilSymbolLoader())
->setAncestorClass('ConduitAPIMethod')
->setType('class')
->selectSymbolsWithoutLoading();
return array_values(ipull($classes, 'name'));
}
}
diff --git a/src/applications/differential/controller/diffview/DifferentialDiffViewController.php b/src/applications/differential/controller/diffview/DifferentialDiffViewController.php
index 2e686d1a16..db37db9308 100644
--- a/src/applications/differential/controller/diffview/DifferentialDiffViewController.php
+++ b/src/applications/differential/controller/diffview/DifferentialDiffViewController.php
@@ -1,86 +1,89 @@
<?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 DifferentialDiffViewController extends DifferentialController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
+ $request = $this->getRequest();
+
$diff = id(new DifferentialDiff())->load($this->id);
if (!$diff) {
return new Aphront404Response();
}
$action_panel = new AphrontPanelView();
$action_panel->setHeader('Preview Diff');
$action_panel->setWidth(AphrontPanelView::WIDTH_WIDE);
$action_panel->appendChild(
'<p class="aphront-panel-instructions">Review the diff for correctness. '.
'When you are satisfied, either <strong>create a new revision</strong> '.
'or <strong>update an existing revision</strong>.');
$action_form = new AphrontFormView();
$action_form
+ ->setUser($request->getUser())
->setAction('/differential/revision/edit/')
->addHiddenInput('diffID', $diff->getID())
->addHiddenInput('viaDiffView', 1)
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Attach To')
->setName('revisionID')
->setValue('')
->setOptions(array(
'' => "Create a new Revision...",
)))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Continue'));
$action_panel->appendChild($action_form);
$changesets = $diff->loadChangesets();
$changesets = msort($changesets, 'getSortKey');
$table_of_contents = id(new DifferentialDiffTableOfContentsView())
->setChangesets($changesets);
$details = id(new DifferentialChangesetListView())
->setChangesets($changesets);
return $this->buildStandardPageResponse(
'<div class="differential-primary-pane">'.
implode(
"\n",
array(
$action_panel->render(),
$table_of_contents->render(),
$details->render(),
)).
'</div>',
array(
'title' => 'Diff View',
));
}
}
diff --git a/src/applications/differential/controller/revisionedit/DifferentialRevisionEditController.php b/src/applications/differential/controller/revisionedit/DifferentialRevisionEditController.php
index f767cba6c4..d26e3442c1 100644
--- a/src/applications/differential/controller/revisionedit/DifferentialRevisionEditController.php
+++ b/src/applications/differential/controller/revisionedit/DifferentialRevisionEditController.php
@@ -1,192 +1,193 @@
<?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 DifferentialRevisionEditController extends DifferentialController {
private $id;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
}
public function processRequest() {
if ($this->id) {
$revision = id(new DifferentialRevision())->load($this->id);
if (!$revision) {
return new Aphront404Response();
}
} else {
$revision = new DifferentialRevision();
}
$request = $this->getRequest();
$diff_id = $request->getInt('diffID');
if ($diff_id) {
$diff = id(new DifferentialDiff())->load($diff_id);
if (!$diff) {
return new Aphront404Response();
}
if ($diff->getRevisionID()) {
// TODO: Redirect?
throw new Exception("This diff is already attached to a revision!");
}
} else {
$diff = null;
}
$e_title = true;
$e_testplan = true;
$errors = array();
if ($request->isFormPost() && !$request->getStr('viaDiffView')) {
$revision->setTitle($request->getStr('title'));
$revision->setSummary($request->getStr('summary'));
$revision->setTestPlan($request->getStr('testplan'));
$revision->setBlameRevision($request->getStr('blame'));
$revision->setRevertPlan($request->getStr('revert'));
if (!strlen(trim($revision->getTitle()))) {
$errors[] = 'You must provide a title.';
$e_title = 'Required';
}
if (!strlen(trim($revision->getTestPlan()))) {
$errors[] = 'You must provide a test plan.';
$e_testplan = 'Required';
}
$user_phid = $request->getUser()->getPHID();
if (in_array($user_phid, $request->getArr('reviewers'))) {
$errors[] = 'You may not review your own revision.';
}
if (!$errors) {
$editor = new DifferentialRevisionEditor($revision, $user_phid);
if ($diff) {
$editor->addDiff($diff, $request->getStr('comments'));
}
$editor->setCCPHIDs($request->getArr('cc'));
$editor->setReviewers($request->getArr('reviewers'));
$editor->save();
return id(new AphrontRedirectResponse())
->setURI('/D'.$revision->getID());
}
$reviewer_phids = $request->getArr('reviewers');
$cc_phids = $request->getArr('cc');
} else {
// $reviewer_phids = $revision->getReviewers();
// $cc_phids = $revision->getCCPHIDs();
$reviewer_phids = array();
$cc_phids = array();
}
$form = new AphrontFormView();
+ $form->setUser($request->getUser());
if ($diff) {
$form->addHiddenInput('diffID', $diff->getID());
}
if ($revision->getID()) {
$form->setAction('/differential/revision/edit/'.$revision->getID().'/');
} else {
$form->setAction('/differential/revision/edit/');
}
$error_view = null;
if ($errors) {
$error_view = id(new AphrontErrorView())
->setTitle('Form Errors')
->setErrors($errors);
}
$form
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel('Title')
->setName('title')
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)
->setValue($revision->getTitle())
->setError($e_title))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel('Summary')
->setName('summary')
->setValue($revision->getSummary()))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel('Test Plan')
->setName('testplan')
->setValue($revision->getTestPlan())
->setError($e_testplan))
->appendChild(
id(new AphrontFormTokenizerControl())
->setLabel('Reviewers')
->setName('reviewers')
->setDatasource('/typeahead/common/users/')
->setValue($reviewer_map))
->appendChild(
id(new AphrontFormTokenizerControl())
->setLabel('CC')
->setName('cc')
->setDatasource('/typeahead/common/mailable/')
->setValue($reviewer_map))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Blame Revision')
->setName('blame')
->setValue($revision->getBlameRevision())
->setCaption('Revision which broke the stuff which this '.
'change fixes.'))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel('Revert Plan')
->setName('revert')
->setValue($revision->getRevertPlan())
->setCaption('Special steps required to safely revert this change.'));
$submit = id(new AphrontFormSubmitControl())
->setValue('Save');
if ($diff) {
$submit->addCancelButton('/differential/diff/'.$diff->getID().'/');
} else {
$submit->addCancelButton('/D'.$revision->getID());
}
$form->appendChild($submit);
$panel = new AphrontPanelView();
if ($revision->getID()) {
$panel->setHeader('Edit Differential Revision');
} else {
$panel->setHeader('Create New Differential Revision');
}
$panel->appendChild($form);
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
return $this->buildStandardPageResponse(
array($error_view, $panel),
array(
'title' => 'Edit Differential Revision',
));
}
}
diff --git a/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php b/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
index 1e55f8c825..ef077224d0 100644
--- a/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
+++ b/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
@@ -1,1728 +1,1729 @@
<?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();
$comments = $revision->loadComments();
$comments = array_merge(
$this->getImplicitComments($revision),
$comments);
$object_phids = array_merge(
$revision->getReviewers(),
$revision->getCCPHIDs(),
array(
$revision->getAuthorPHID(),
$request->getUser()->getPHID(),
),
mpull($comments, 'getAuthorPHID'));
$handles = id(new PhabricatorObjectHandleData($object_phids))
->loadHandles();
$revision_detail = new DifferentialRevisionDetailView();
$revision_detail->setRevision($revision);
$properties = $this->getRevisionProperties($revision, $target, $handles);
$revision_detail->setProperties($properties);
$actions = $this->getRevisionActions($revision);
$revision_detail->setActions($actions);
$comment_view = new DifferentialRevisionCommentListView();
$comment_view->setComments($comments);
$comment_view->setHandles($handles);
$diff_history = new DifferentialRevisionUpdateHistoryView();
$diff_history->setDiffs($diffs);
$toc_view = new DifferentialDiffTableOfContentsView();
$toc_view->setChangesets($changesets);
$changeset_view = new DifferentialChangesetListView();
$changeset_view->setChangesets($changesets);
$comment_form = new DifferentialAddCommentView();
$comment_form->setRevision($revision);
$comment_form->setActions($this->getRevisionCommentActions($revision));
$comment_form->setActionURI('/differential/comment/save/');
+ $comment_form->setUser($request->getUser());
return $this->buildStandardPageResponse(
'<div class="differential-primary-pane">'.
$revision_detail->render().
$comment_view->render().
$diff_history->render().
$toc_view->render().
$changeset_view->render().
$comment_form->render().
'</div>',
array(
'title' => $revision->getTitle(),
));
}
private function getImplicitComments(DifferentialRevision $revision) {
$template = new DifferentialComment();
$template->setAuthorPHID($revision->getAuthorPHID());
$template->setRevisionID($revision->getID());
$template->setDateCreated($revision->getDateCreated());
$comments = array();
if (strlen($revision->getSummary())) {
$summary_comment = clone $template;
$summary_comment->setContent($revision->getSummary());
$summary_comment->setAction(DifferentialAction::ACTION_SUMMARIZE);
$comments[] = $summary_comment;
}
if (strlen($revision->getTestPlan())) {
$testplan_comment = clone $template;
$testplan_comment->setContent($revision->getTestPlan());
$testplan_comment->setAction(DifferentialAction::ACTION_TESTPLAN);
$comments[] = $testplan_comment;
}
return $comments;
}
private function getRevisionProperties(
DifferentialRevision $revision,
DifferentialDiff $diff,
array $handles) {
$properties = array();
$status = $revision->getStatus();
$status = DifferentialRevisionStatus::getNameForRevisionStatus($status);
$properties['Revision Status'] = '<strong>'.$status.'</strong>';
$author = $handles[$revision->getAuthorPHID()];
$properties['Author'] = $author->renderLink();
$properties['Reviewers'] = $this->renderHandleLinkList(
array_select_keys(
$handles,
$revision->getReviewers()));
$properties['CCs'] = $this->renderHandleLinkList(
array_select_keys(
$handles,
$revision->getCCPHIDs()));
$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->getAuthorPHID() == $viewer_phid);
$viewer_is_reviewer = in_array($viewer_phid, $revision->getReviewers());
$viewer_is_cc = in_array($viewer_phid, $revision->getCCPHIDs());
$status = $revision->getStatus();
$revision_id = $revision->getID();
$revision_phid = $revision->getPHID();
$links = array();
if ($viewer_is_owner) {
$links[] = array(
'class' => 'revision-edit',
'href' => "/differential/revision/edit/{$revision_id}/",
'name' => 'Edit Revision',
);
}
if (!$viewer_is_owner && !$viewer_is_reviewer) {
$action = $viewer_is_cc ? 'rem' : 'add';
$links[] = array(
'class' => $viewer_is_cc ? 'subscribe-rem' : 'subscribe-add',
'href' => "/differential/subscribe/{$action}/{$revision_id}/",
'name' => $viewer_is_cc ? 'Unsubscribe' : 'Subscribe',
);
} 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'));
}
private function getRevisionCommentActions(DifferentialRevision $revision) {
$actions = array(
DifferentialAction::ACTION_COMMENT => true,
);
$viewer_phid = $this->getRequest()->getUser()->getPHID();
$viewer_is_owner = ($viewer_phid == $revision->getAuthorPHID());
if ($viewer_is_owner) {
switch ($revision->getStatus()) {
case DifferentialRevisionStatus::NEEDS_REVIEW:
$actions[DifferentialAction::ACTION_ABANDON] = true;
break;
case DifferentialRevisionStatus::NEEDS_REVISION:
case DifferentialRevisionStatus::ACCEPTED:
$actions[DifferentialAction::ACTION_ABANDON] = true;
$actions[DifferentialAction::ACTION_REQUEST] = true;
break;
case DifferentialRevisionStatus::COMMITTED:
break;
case DifferentialRevisionStatus::ABANDONED:
$actions[DifferentialAction::ACTION_RECLAIM] = true;
break;
}
} else {
switch ($revision->getStatus()) {
case DifferentialRevisionStatus::NEEDS_REVIEW:
$actions[DifferentialAction::ACTION_ACCEPT] = true;
$actions[DifferentialAction::ACTION_REJECT] = true;
break;
case DifferentialRevisionStatus::NEEDS_REVISION:
$actions[DifferentialAction::ACTION_ACCEPT] = true;
break;
case DifferentialRevisionStatus::ACCEPTED:
$actions[DifferentialAction::ACTION_REJECT] = true;
break;
case DifferentialRevisionStatus::COMMITTED:
case DifferentialRevisionStatus::ABANDONED:
break;
}
}
$actions[DifferentialAction::ACTION_ADDREVIEWERS] = true;
return array_keys($actions);
}
}
/*
protected function getRevisionActions(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=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 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/view/addcomment/DifferentialAddCommentView.php b/src/applications/differential/view/addcomment/DifferentialAddCommentView.php
index 7d8c135baf..b390c5a083 100644
--- a/src/applications/differential/view/addcomment/DifferentialAddCommentView.php
+++ b/src/applications/differential/view/addcomment/DifferentialAddCommentView.php
@@ -1,71 +1,76 @@
<?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 DifferentialAddCommentView extends AphrontView {
private $revision;
private $actions;
private $actionURI;
+ private $user;
public function setRevision($revision) {
$this->revision = $revision;
return $this;
}
public function setActions(array $actions) {
$this->actions = $actions;
return $this;
}
public function setActionURI($uri) {
$this->actionURI = $uri;
}
- public function render() {
+ public function setUser(PhabricatorUser $user) {
+ $this->user = $user;
+ }
+ public function render() {
$revision = $this->revision;
$actions = array();
foreach ($this->actions as $action) {
$actions[$action] = DifferentialAction::getActionVerb($action);
}
$form = new AphrontFormView();
$form
+ ->setUser($this->user)
->setAction($this->actionURI)
->addHiddenInput('revision_id', $revision->getID())
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Action')
->setName('action')
->setOptions($actions))
->appendChild(
id(new AphrontFormTextAreaControl())
->setName('comment')
->setLabel('Comment'))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Comment'));
return
'<div class="differential-panel">'.
'<h1>Add Comment</h1>'.
$form->render().
'</div>';
}
}
diff --git a/src/applications/directory/controller/categoryedit/PhabricatorDirectoryCategoryEditController.php b/src/applications/directory/controller/categoryedit/PhabricatorDirectoryCategoryEditController.php
index 7194fc796b..a20db00702 100644
--- a/src/applications/directory/controller/categoryedit/PhabricatorDirectoryCategoryEditController.php
+++ b/src/applications/directory/controller/categoryedit/PhabricatorDirectoryCategoryEditController.php
@@ -1,110 +1,111 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class PhabricatorDirectoryCategoryEditController
extends PhabricatorDirectoryController {
private $id;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
}
public function processRequest() {
if ($this->id) {
$category = id(new PhabricatorDirectoryCategory())->load($this->id);
if (!$category) {
return new Aphront404Response();
}
} else {
$category = new PhabricatorDirectoryCategory();
}
$e_name = true;
$errors = array();
$request = $this->getRequest();
if ($request->isFormPost()) {
$category->setName($request->getStr('name'));
$category->setSequence($request->getStr('sequence'));
if (!strlen($category->getName())) {
$errors[] = 'Category name is required.';
$e_name = 'Required';
}
if (!$errors) {
$category->save();
return id(new AphrontRedirectResponse())
->setURI('/directory/category/');
}
}
$error_view = null;
if ($errors) {
$error_view = id(new AphrontErrorView())
->setTitle('Form Errors')
->setErrors($errors);
}
$form = new AphrontFormView();
+ $form->setUser($request->getUser());
if ($category->getID()) {
$form->setAction('/directory/category/edit/'.$category->getID().'/');
} else {
$form->setAction('/directory/category/edit/');
}
$categories = id(new PhabricatorDirectoryCategory())->loadAll();
$category_map = mpull($categories, 'getName', 'getID');
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Name')
->setName('name')
->setValue($category->getName())
->setError($e_name))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Order')
->setName('sequence')
->setValue((int)$category->getSequence()))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Save')
->addCancelButton('/directory/category/'));
$panel = new AphrontPanelView();
if ($category->getID()) {
$panel->setHeader('Edit Directory Category');
} else {
$panel->setHeader('Create New Directory Category');
}
$panel->appendChild($form);
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
return $this->buildStandardPageResponse(
array($error_view, $panel),
array(
'title' => 'Edit Directory Category',
));
}
}
diff --git a/src/applications/directory/controller/itemedit/PhabricatorDirectoryItemEditController.php b/src/applications/directory/controller/itemedit/PhabricatorDirectoryItemEditController.php
index 0d3c58ff12..f4da31fafe 100644
--- a/src/applications/directory/controller/itemedit/PhabricatorDirectoryItemEditController.php
+++ b/src/applications/directory/controller/itemedit/PhabricatorDirectoryItemEditController.php
@@ -1,138 +1,140 @@
<?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 PhabricatorDirectoryItemEditController
extends PhabricatorDirectoryController {
private $id;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
}
public function processRequest() {
if ($this->id) {
$item = id(new PhabricatorDirectoryItem())->load($this->id);
if (!$item) {
return new Aphront404Response();
}
} else {
$item = new PhabricatorDirectoryItem();
}
$e_name = true;
$e_href = true;
$errors = array();
$request = $this->getRequest();
if ($request->isFormPost()) {
$item->setName($request->getStr('name'));
$item->setHref($request->getStr('href'));
$item->setDescription($request->getStr('description'));
$item->setCategoryID($request->getStr('categoryID'));
$item->setSequence($request->getStr('sequence'));
if (!strlen($item->getName())) {
$errors[] = 'Item name is required.';
$e_name = 'Required';
}
if (!strlen($item->getHref())) {
$errors[] = 'Item link is required.';
$e_href = 'Required';
}
if (!$errors) {
$item->save();
return id(new AphrontRedirectResponse())
->setURI('/directory/item/');
}
}
$error_view = null;
if ($errors) {
$error_view = id(new AphrontErrorView())
->setTitle('Form Errors')
->setErrors($errors);
}
$form = new AphrontFormView();
+ $form->setUser($request->getUser());
+
if ($item->getID()) {
$form->setAction('/directory/item/edit/'.$item->getID().'/');
} else {
$form->setAction('/directory/item/edit/');
}
$categories = id(new PhabricatorDirectoryCategory())->loadAll();
$category_map = mpull($categories, 'getName', 'getID');
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Name')
->setName('name')
->setValue($item->getName())
->setError($e_name))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Category')
->setName('categoryID')
->setOptions($category_map)
->setValue($item->getCategoryID()))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Link')
->setName('href')
->setValue($item->getHref())
->setError($e_href))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel('Description')
->setName('description')
->setValue($item->getDescription()))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Order')
->setName('sequence')
->setCaption(
'Items in a category are sorted by "order", then by name.')
->setValue((int)$item->getSequence()))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Save')
->addCancelButton('/directory/item/'));
$panel = new AphrontPanelView();
if ($item->getID()) {
$panel->setHeader('Edit Directory Item');
} else {
$panel->setHeader('Create New Directory Item');
}
$panel->appendChild($form);
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
return $this->buildStandardPageResponse(
array($error_view, $panel),
array(
'title' => 'Edit Directory Item',
));
}
}
diff --git a/src/applications/files/controller/upload/PhabricatorFileUploadController.php b/src/applications/files/controller/upload/PhabricatorFileUploadController.php
index 12d6f83a81..9d4b9e2be2 100644
--- a/src/applications/files/controller/upload/PhabricatorFileUploadController.php
+++ b/src/applications/files/controller/upload/PhabricatorFileUploadController.php
@@ -1,68 +1,69 @@
<?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 PhabricatorFileUploadController extends PhabricatorFileController {
public function processRequest() {
$request = $this->getRequest();
if ($request->isFormPost()) {
$file = PhabricatorFile::newFromPHPUpload(
idx($_FILES, 'file'),
array(
'name' => $request->getStr('name'),
));
return id(new AphrontRedirectResponse())
->setURI('/file/info/'.phutil_escape_uri($file->getPHID()).'/');
}
$form = new AphrontFormView();
$form->setAction('/file/upload/');
+ $form->setUser($request->getUser());
$form
->setEncType('multipart/form-data')
->appendChild(
id(new AphrontFormFileControl())
->setLabel('File')
->setName('file')
->setError(true))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Name')
->setName('name')
->setCaption('Optional file display name.'))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Upload')
->addCancelButton('/file/'));
$panel = new AphrontPanelView();
$panel->setHeader('Upload File');
$panel->appendChild($form);
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
return $this->buildStandardPageResponse(
array($panel),
array(
'title' => 'Upload File',
));
}
}
diff --git a/src/applications/metamta/controller/mailinglistedit/PhabricatorMetaMTAMailingListEditController.php b/src/applications/metamta/controller/mailinglistedit/PhabricatorMetaMTAMailingListEditController.php
index cffa047b8b..65fd88ec45 100644
--- a/src/applications/metamta/controller/mailinglistedit/PhabricatorMetaMTAMailingListEditController.php
+++ b/src/applications/metamta/controller/mailinglistedit/PhabricatorMetaMTAMailingListEditController.php
@@ -1,121 +1,122 @@
<?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 PhabricatorMetaMTAMailingListEditController
extends PhabricatorMetaMTAController {
private $id;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
}
public function processRequest() {
if ($this->id) {
$list = id(new PhabricatorMetaMTAMailingList())->load($this->id);
if (!$list) {
return new Aphront404Response();
}
} else {
$list = new PhabricatorMetaMTAMailingList();
}
$e_email = true;
$errors = array();
$request = $this->getRequest();
if ($request->isFormPost()) {
$list->setName($request->getStr('name'));
$list->setEmail($request->getStr('email'));
$list->setURI($request->getStr('uri'));
if (!strlen($list->getEmail())) {
$e_email = 'Required';
$errors[] = 'Email is required.';
}
if (!$errors) {
$list->save();
return id(new AphrontRedirectResponse())
->setURI('/mail/lists/');
}
}
$error_view = null;
if ($errors) {
$error_view = id(new AphrontErrorView())
->setTitle('Form Errors')
->setErrors($errors);
}
$form = new AphrontFormView();
+ $form->setUser($request->getUser());
if ($list->getID()) {
$form->setAction('/mail/lists/edit/'.$list->getID().'/');
} else {
$form->setAction('/mail/lists/edit/');
}
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Email')
->setName('email')
->setValue($list->getEmail())
->setError($e_email))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Name')
->setName('name')
->setValue($list->getName()))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('URI')
->setName('uri')
->setValue($list->getURI()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('ID')
->setValue(nonempty($list->getID(), '-')))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('PHID')
->setValue(nonempty($list->getPHID(), '-')))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Save')
->addCancelButton('/mail/lists/'));
$panel = new AphrontPanelView();
if ($list->getID()) {
$panel->setHeader('Edit Mailing List');
} else {
$panel->setHeader('Create New Mailing List');
}
$panel->appendChild($form);
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
return $this->buildStandardPageResponse(
array($error_view, $panel),
array(
'title' => 'Edit Mailing List',
));
}
}
diff --git a/src/applications/metamta/controller/send/PhabricatorMetaMTASendController.php b/src/applications/metamta/controller/send/PhabricatorMetaMTASendController.php
index a33dd62d6a..07374421b8 100644
--- a/src/applications/metamta/controller/send/PhabricatorMetaMTASendController.php
+++ b/src/applications/metamta/controller/send/PhabricatorMetaMTASendController.php
@@ -1,107 +1,108 @@
<?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 PhabricatorMetaMTASendController extends PhabricatorMetaMTAController {
public function processRequest() {
$request = $this->getRequest();
if ($request->isFormPost()) {
$mail = new PhabricatorMetaMTAMail();
$mail->addTos($request->getArr('to'));
$mail->addCCs($request->getArr('cc'));
$mail->setSubject($request->getStr('subject'));
$mail->setBody($request->getStr('body'));
$mail->setFrom($request->getUser()->getPHID());
$mail->setSimulatedFailureCount($request->getInt('failures'));
$mail->setIsHTML($request->getInt('html'));
$mail->save();
if ($request->getInt('immediately')) {
$mail->sendNow(
$force_send = true,
new PhabricatorMailImplementationPHPMailerLiteAdapter());
}
return id(new AphrontRedirectResponse())
->setURI('/mail/view/'.$mail->getID().'/');
}
$failure_caption =
"Enter a number to simulate that many consecutive send failures before ".
"really attempting to deliver via the underlying MTA.";
$form = new AphrontFormView();
+ $form->setUser($request->getUser());
$form->setAction('/mail/send/');
$form
->appendChild(
'<p class="aphront-form-instructions">This form will send a normal '.
'email using MetaMTA as a transport mechanism.</p>')
->appendChild(
id(new AphrontFormTokenizerControl())
->setLabel('To')
->setName('to')
->setDatasource('/typeahead/common/mailable/'))
->appendChild(
id(new AphrontFormTokenizerControl())
->setLabel('CC')
->setName('cc')
->setDatasource('/typeahead/common/mailable/'))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Subject')
->setName('subject'))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel('Body')
->setName('body'))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Simulate Failures')
->setName('failures')
->setCaption($failure_caption))
->appendChild(
id(new AphrontFormCheckboxControl())
->setLabel('HTML')
->addCheckbox('html', '1', 'Send as HTML email.'))
->appendChild(
id(new AphrontFormCheckboxControl())
->setLabel('Send Now')
->addCheckbox(
'immediately',
'1',
'Send immediately, not via MetaMTA background script.'))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Send Mail'));
$panel = new AphrontPanelView();
$panel->setHeader('Send Email');
$panel->appendChild($form);
$panel->setWidth(AphrontPanelView::WIDTH_WIDE);
return $this->buildStandardPageResponse(
$panel,
array(
'title' => 'Send Mail',
));
}
}
diff --git a/src/applications/metamta/controller/view/PhabricatorMetaMTAViewController.php b/src/applications/metamta/controller/view/PhabricatorMetaMTAViewController.php
index 0d75c25394..8d716d8331 100644
--- a/src/applications/metamta/controller/view/PhabricatorMetaMTAViewController.php
+++ b/src/applications/metamta/controller/view/PhabricatorMetaMTAViewController.php
@@ -1,79 +1,82 @@
<?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 PhabricatorMetaMTAViewController extends PhabricatorMetaMTAController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
+ $request = $this->getRequest();
+
$mail = id(new PhabricatorMetaMTAMail())->load($this->id);
if (!$mail) {
return new Aphront404Response();
}
-
+
$status = PhabricatorMetaMTAMail::getReadableStatus($mail->getStatus());
$form = new AphrontFormView();
+ $form->setUser($request->getUser());
$form->setAction('/mail/send/');
$form
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Subject')
->setValue($mail->getSubject()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Created')
->setValue(date('F jS, Y g:i:s A', $mail->getDateCreated())))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Status')
->setValue($status))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Message')
->setValue($mail->getMessage()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Related PHID')
->setValue($mail->getRelatedPHID()))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel('Parameters')
->setValue(json_encode($mail->getParameters())))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton('/mail/', 'Done'));
$panel = new AphrontPanelView();
$panel->setHeader('View Email');
$panel->appendChild($form);
$panel->setWidth(AphrontPanelView::WIDTH_WIDE);
return $this->buildStandardPageResponse(
$panel,
array(
'title' => 'View Mail',
));
}
}
diff --git a/src/applications/people/controller/edit/PhabricatorPeopleEditController.php b/src/applications/people/controller/edit/PhabricatorPeopleEditController.php
index 3af6f63d6d..fe52b50df5 100644
--- a/src/applications/people/controller/edit/PhabricatorPeopleEditController.php
+++ b/src/applications/people/controller/edit/PhabricatorPeopleEditController.php
@@ -1,142 +1,143 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class PhabricatorPeopleEditController extends PhabricatorPeopleController {
private $username;
public function willProcessRequest(array $data) {
$this->username = idx($data, 'username');
}
public function processRequest() {
if ($this->username) {
$user = id(new PhabricatorUser())->loadOneWhere(
'userName = %s',
$this->username);
if (!$user) {
return new Aphront404Response();
}
} else {
$user = new PhabricatorUser();
}
$e_username = true;
$e_realname = true;
$e_email = true;
$errors = array();
$request = $this->getRequest();
if ($request->isFormPost()) {
if (!$user->getID()) {
$user->setUsername($request->getStr('username'));
}
$user->setRealName($request->getStr('realname'));
$user->setEmail($request->getStr('email'));
if (!strlen($user->getUsername())) {
$errors[] = "Username is required.";
$e_username = 'Required';
} else if (!preg_match('/^[a-z0-9]+$/', $user->getUsername())) {
$errors[] = "Username must consist of only numbers and letters.";
$e_username = 'Invalid';
}
if (!strlen($user->getRealName())) {
$errors[] = 'Real name is required.';
$e_realname = 'Required';
}
if (!strlen($user->getEmail())) {
$errors[] = 'Email is required.';
$e_email = 'Required';
}
if (!$errors) {
$user->save();
$response = id(new AphrontRedirectResponse())
->setURI('/p/'.$user->getUsername().'/');
return $response;
}
}
$error_view = null;
if ($errors) {
$error_view = id(new AphrontErrorView())
->setTitle('Form Errors')
->setErrors($errors);
}
$form = new AphrontFormView();
+ $form->setUser($request->getUser());
if ($user->getUsername()) {
$form->setAction('/people/edit/'.$user->getUsername().'/');
} else {
$form->setAction('/people/edit/');
}
if ($user->getID()) {
$is_immutable = true;
} else {
$is_immutable = false;
}
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Username')
->setName('username')
->setValue($user->getUsername())
->setError($e_username)
->setDisabled($is_immutable)
->setCaption('Usernames are permanent and can not be changed later!'))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Real Name')
->setName('realname')
->setValue($user->getRealName())
->setError($e_realname))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Email')
->setName('email')
->setValue($user->getEmail())
->setError($e_email))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Save')
->addCancelButton('/people/'));
$panel = new AphrontPanelView();
if ($user->getID()) {
$panel->setHeader('Edit User');
} else {
$panel->setHeader('Create New User');
}
$panel->appendChild($form);
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
return $this->buildStandardPageResponse(
array($error_view, $panel),
array(
'title' => 'Edit User',
));
}
}
diff --git a/src/applications/people/storage/user/PhabricatorUser.php b/src/applications/people/storage/user/PhabricatorUser.php
index 64417b55a3..f5ea83f038 100644
--- a/src/applications/people/storage/user/PhabricatorUser.php
+++ b/src/applications/people/storage/user/PhabricatorUser.php
@@ -1,63 +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.
*/
class PhabricatorUser extends PhabricatorUserDAO {
const PHID_TYPE = 'USER';
protected $phid;
protected $userName;
protected $realName;
protected $email;
protected $passwordSalt;
protected $passwordHash;
+ private $sessionKey;
+
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(self::PHID_TYPE);
}
public function setPassword($password) {
$this->setPasswordSalt(md5(mt_rand()));
$hash = $this->hashPassword($password);
$this->setPasswordHash($hash);
return $this;
}
public function comparePassword($password) {
$password = $this->hashPassword($password);
return ($password === $this->getPasswordHash());
}
private function hashPassword($password) {
$password = $this->getUsername().
$password.
$this->getPHID().
$this->getPasswordSalt();
for ($ii = 0; $ii < 1000; $ii++) {
$password = md5($password);
}
return $password;
}
+ const CSRF_CYCLE_FREQUENCY = 3600;
+
+ public function getCSRFToken() {
+ return $this->generateCSRFToken(time());
+ }
+
+ public function validateCSRFToken($token) {
+ for ($ii = -1; $ii <= 1; $ii++) {
+ $time = time() + (self::CSRF_CYCLE_FREQUENCY * $ii);
+ $valid = $this->generateCSRFToken($time);
+ if ($token == $valid) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private function generateCSRFToken($epoch) {
+ $time_block = floor($epoch / (60 * 60));
+ // TODO: this should be a secret lolol
+ $key = '0b7ec0592e0a2829d8b71df2fa269b2c6172eca3';
+ $vec = $this->getPHID().$this->passwordHash.$key.$time_block;
+ return substr(md5($vec), 0, 16);
+ }
+
}
diff --git a/src/applications/phid/controller/allocate/PhabricatorPHIDAllocateController.php b/src/applications/phid/controller/allocate/PhabricatorPHIDAllocateController.php
index 53f8af3e14..910f17ab3b 100644
--- a/src/applications/phid/controller/allocate/PhabricatorPHIDAllocateController.php
+++ b/src/applications/phid/controller/allocate/PhabricatorPHIDAllocateController.php
@@ -1,68 +1,69 @@
<?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 PhabricatorPHIDAllocateController
extends PhabricatorPHIDController {
public function processRequest() {
$request = $this->getRequest();
if ($request->isFormPost()) {
$type = $request->getStr('type');
$phid = PhabricatorPHID::generateNewPHID($type);
return id(new AphrontRedirectResponse())
->setURI('/phid/?phid='.phutil_escape_uri($phid));
}
$types = id(new PhabricatorPHIDType())->loadAll();
-
+
$options = array();
foreach ($types as $type) {
$options[$type->getType()] = $type->getType().': '.$type->getName();
}
asort($options);
$form = new AphrontFormView();
+ $form->setUser($request->getUser());
$form->setAction('/phid/new/');
$form
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('PHID Type')
->setName('type')
->setOptions($options))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Allocate')
->addCancelButton('/phid/'));
$panel = new AphrontPanelView();
$panel->setHeader('Allocate New PHID');
$panel->appendChild($form);
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
return $this->buildStandardPageResponse(
array($panel),
array(
'title' => 'Allocate New PHID',
));
}
}
diff --git a/src/applications/phid/controller/allocate/__init__.php b/src/applications/phid/controller/allocate/__init__.php
index 0605962517..d007a63f53 100644
--- a/src/applications/phid/controller/allocate/__init__.php
+++ b/src/applications/phid/controller/allocate/__init__.php
@@ -1,20 +1,21 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/phid/controller/base');
phutil_require_module('phabricator', 'applications/phid/storage/phid');
phutil_require_module('phabricator', 'applications/phid/storage/type');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/layout/panel');
+phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorPHIDAllocateController.php');
diff --git a/src/applications/phid/controller/lookup/PhabricatorPHIDLookupController.php b/src/applications/phid/controller/lookup/PhabricatorPHIDLookupController.php
index 0578961e6b..cb8f4cbbc0 100644
--- a/src/applications/phid/controller/lookup/PhabricatorPHIDLookupController.php
+++ b/src/applications/phid/controller/lookup/PhabricatorPHIDLookupController.php
@@ -1,111 +1,112 @@
<?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 PhabricatorPHIDLookupController
extends PhabricatorPHIDController {
public function processRequest() {
$request = $this->getRequest();
if ($request->isFormPost()) {
$phids = preg_split('/[\s,]+/', $request->getStr('phids'));
$phids = array_filter($phids);
if ($phids) {
$handles = id(new PhabricatorObjectHandleData($phids))
->loadHandles();
$rows = array();
foreach ($handles as $handle) {
if ($handle->getURI()) {
$link = phutil_render_tag(
'a',
array(
'href' => $handle->getURI(),
),
phutil_escape_html($handle->getURI()));
} else {
$link = null;
}
$rows[] = array(
phutil_escape_html($handle->getPHID()),
phutil_escape_html($handle->getType()),
phutil_escape_html($handle->getName()),
phutil_escape_html($handle->getEmail()),
$link,
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'PHID',
'Type',
'Name',
'Email',
'URI',
));
$table->setColumnClasses(
array(
null,
null,
null,
null,
'wide',
));
$panel = new AphrontPanelView();
$panel->setHeader('PHID Handles');
$panel->appendChild($table);
return $this->buildStandardPageResponse(
$panel,
array(
'title' => 'PHID Lookup Results',
));
}
}
$lookup_form = new AphrontFormView();
+ $lookup_form->setUser($request->getUser());
$lookup_form
->setAction('/phid/')
->appendChild(
id(new AphrontFormTextAreaControl())
->setName('phids')
// ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT) TODO
->setCaption('Enter PHIDs separated by spaces or commas.'))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Lookup PHIDs'));
$lookup_panel = new AphrontPanelView();
$lookup_panel->setHeader('Lookup PHIDs');
$lookup_panel->appendChild($lookup_form);
$lookup_panel->setWidth(AphrontPanelView::WIDTH_WIDE);
return $this->buildStandardPageResponse(
array(
$lookup_panel,
),
array(
'title' => 'PHID Lookup',
'tab' => 'lookup',
));
}
}
diff --git a/src/applications/phid/controller/typeedit/PhabricatorPHIDTypeEditController.php b/src/applications/phid/controller/typeedit/PhabricatorPHIDTypeEditController.php
index a82e63c778..11a1477fc7 100644
--- a/src/applications/phid/controller/typeedit/PhabricatorPHIDTypeEditController.php
+++ b/src/applications/phid/controller/typeedit/PhabricatorPHIDTypeEditController.php
@@ -1,132 +1,135 @@
<?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 PhabricatorPHIDTypeEditController
extends PhabricatorPHIDController {
private $id;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
}
public function processRequest() {
if ($this->id) {
$type = id(new PhabricatorPHIDType())->load($this->id);
if (!$type) {
return new Aphront404Response();
}
} else {
$type = new PhabricatorPHIDType();
}
$e_type = true;
$e_name = true;
$errors = array();
$request = $this->getRequest();
if ($request->isFormPost()) {
$type->setName($request->getStr('name'));
if (!$type->getID()) {
$type->setType($request->getStr('type'));
}
$type->setDescription($request->getStr('description'));
if (!strlen($type->getType())) {
$errors[] = 'Type code is required.';
$e_type = 'Required';
}
if (!strlen($type->getName())) {
$errors[] = 'Type name is required.';
$e_name = 'Required';
}
if (!$errors) {
$type->save();
return id(new AphrontRedirectResponse())
->setURI('/phid/type/');
}
}
$error_view = null;
if ($errors) {
$error_view = id(new AphrontErrorView())
->setTitle('Form Errors')
->setErrors($errors);
}
$form = new AphrontFormView();
+ $form->setUser($request->getUser());
+
if ($type->getID()) {
$form->setAction('/phid/type/edit/'.$type->getID().'/');
} else {
$form->setAction('/phid/type/edit/');
}
if ($type->getID()) {
$type_immutable = true;
} else {
$type_immutable = false;
}
+
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Type')
->setName('type')
->setValue($type->getType())
->setError($e_type)
->setCaption(
'Four character type identifier. This can not be changed once '.
'it is created.')
->setDisabled($type_immutable))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Name')
->setName('name')
->setValue($type->getName())
->setError($e_name))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel('Description')
->setName('description')
->setValue($type->getDescription()))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Save')
->addCancelButton('/phid/type/'));
$panel = new AphrontPanelView();
if ($type->getID()) {
$panel->setHeader('Edit PHID Type');
} else {
$panel->setHeader('Create New PHID Type');
}
$panel->appendChild($form);
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
return $this->buildStandardPageResponse(
array($error_view, $panel),
array(
'title' => 'Edit PHID Type',
));
}
}
diff --git a/src/view/form/base/AphrontFormView.php b/src/view/form/base/AphrontFormView.php
index 77aa14f64d..e6e0e73840 100755
--- a/src/view/form/base/AphrontFormView.php
+++ b/src/view/form/base/AphrontFormView.php
@@ -1,81 +1,92 @@
<?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 AphrontFormView extends AphrontView {
private $action;
private $method = 'POST';
private $header;
private $data = array();
private $encType;
+ private $user;
+
+ public function setUser(PhabricatorUser $user) {
+ $this->user = $user;
+ return $this;
+ }
public function setAction($action) {
$this->action = $action;
return $this;
}
public function setMethod($method) {
$this->method = $method;
return $this;
}
public function setEncType($enc_type) {
$this->encType = $enc_type;
return $this;
}
public function addHiddenInput($key, $value) {
$this->data[$key] = $value;
return $this;
}
public function render() {
require_celerity_resource('aphront-form-view-css');
return phutil_render_tag(
'form',
array(
'action' => $this->action,
'method' => $this->method,
'class' => 'aphront-form-view',
'enctype' => $this->encType,
),
$this->renderDataInputs().
$this->renderChildren());
}
private function renderDataInputs() {
+ if (!$this->user) {
+ throw new Exception('You must pass the user to AphrontFormView.');
+ }
+
$data = $this->data + array(
'__form__' => 1,
+ '__csrf__' => $this->user->getCSRFToken(),
);
$inputs = array();
foreach ($data as $key => $value) {
if ($value === null) {
continue;
}
$inputs[] = phutil_render_tag(
'input',
array(
'type' => 'hidden',
'name' => $key,
'value' => $value,
));
}
return implode("\n", $inputs);
}
}
diff --git a/src/view/page/standard/PhabricatorStandardPageView.php b/src/view/page/standard/PhabricatorStandardPageView.php
index 7956b4ba2b..26a15aeeba 100755
--- a/src/view/page/standard/PhabricatorStandardPageView.php
+++ b/src/view/page/standard/PhabricatorStandardPageView.php
@@ -1,152 +1,171 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class PhabricatorStandardPageView extends AphrontPageView {
private $baseURI;
private $applicationName;
private $tabs = array();
private $selectedTab;
private $glyph;
private $bodyContent;
private $request;
public function setRequest($request) {
$this->request = $request;
return $this;
}
public function getRequest() {
return $this->request;
}
public function setApplicationName($application_name) {
$this->applicationName = $application_name;
return $this;
}
public function getApplicationName() {
return $this->applicationName;
}
public function setBaseURI($base_uri) {
$this->baseURI = $base_uri;
return $this;
}
public function getBaseURI() {
return $this->baseURI;
}
public function setTabs(array $tabs, $selected_tab) {
$this->tabs = $tabs;
$this->selectedTab = $selected_tab;
return $this;
}
public function getTitle() {
return $this->getGlyph().' '.parent::getTitle();
}
protected function willRenderPage() {
require_celerity_resource('phabricator-core-css');
require_celerity_resource('phabricator-core-buttons-css');
require_celerity_resource('phabricator-standard-page-view');
require_celerity_resource('javelin-lib-dev');
$this->bodyContent = $this->renderChildren();
}
protected function getHead() {
$response = CelerityAPI::getStaticResourceResponse();
return
$response->renderResourcesOfType('css').
'<script type="text/javascript">window.__DEV__=1;</script>'.
'<script type="text/javascript" src="/rsrc/js/javelin/init.dev.js">'.
'</script>';
}
public function setGlyph($glyph) {
$this->glyph = $glyph;
return $this;
}
public function getGlyph() {
return $this->glyph;
}
protected function getBody() {
$tabs = array();
foreach ($this->tabs as $name => $tab) {
$tabs[] = phutil_render_tag(
'a',
array(
'href' => idx($tab, 'href'),
'class' => ($name == $this->selectedTab)
? 'phabricator-selected-tab'
: null,
),
phutil_escape_html(idx($tab, 'name')));
}
$tabs = implode('', $tabs);
if ($tabs) {
$tabs = '<span class="phabricator-head-tabs">'.$tabs.'</span>';
}
$login_stuff = null;
$request = $this->getRequest();
if ($request) {
$user = $request->getUser();
if ($user->getPHID()) {
- $login_stuff = 'Logged in as '.phutil_escape_html($user->getUsername());
+ $login_stuff =
+ 'Logged in as '.phutil_escape_html($user->getUsername()).
+ ' &middot; '.
+ '<form action="/logout/" method="post" style="display: inline;">'.
+ phutil_render_tag(
+ 'input',
+ array(
+ 'type' => 'hidden',
+ 'name' => '__csrf__',
+ 'value' => $user->getCSRFToken(),
+ )).
+ phutil_render_tag(
+ 'input',
+ array(
+ 'type' => 'hidden',
+ 'name' => '__form__',
+ 'value' => true,
+ )).
+ '<button class="small grey">Logout</button>'.
+ '</form>';
}
}
return
'<div class="phabricator-standard-page">'.
'<div class="phabricator-standard-header">'.
'<div class="phabricator-login-details">'.
$login_stuff.
'</div>'.
'<a href="/">Phabricator</a> '.
phutil_render_tag(
'a',
array(
'href' => $this->getBaseURI(),
'class' => 'phabricator-head-appname',
),
phutil_escape_html($this->getApplicationName())).
$tabs.
'</div>'.
$this->bodyContent.
'<div style="clear: both;"></div>'.
'</div>';
}
protected function getTail() {
$response = CelerityAPI::getStaticResourceResponse();
return
$response->renderResourcesOfType('js').
$response->renderHTMLFooter();
}
}
diff --git a/src/view/utils/viewutils.php b/src/view/utils/viewutils.php
index 83588de047..c884a051a7 100644
--- a/src/view/utils/viewutils.php
+++ b/src/view/utils/viewutils.php
@@ -1,85 +1,85 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function phabricator_format_relative_time($duration) {
return phabricator_format_units_generic(
$duration,
array(60, 60, 24, 7),
array('s', 'm', 'h', 'd', 'w'),
$precision = 0);
}
function phabricator_format_timestamp($epoch) {
$difference = (time() - $epoch);
-
+
if ($difference < 60 * 60) {
return phabricator_format_relative_time($difference).' ago';
} else if (date('Y') == date('Y', $epoch)) {
return date('M jS, g:i A', $epoch);
} else {
return date('F jS, Y', $epoch);
}
}
function phabricator_format_units_generic(
$n,
array $scales,
array $labels,
$precision = 0,
&$remainder = null) {
$is_negative = false;
if ($n < 0) {
$is_negative = true;
$n = abs($n);
}
$remainder = 0;
$accum = 1;
$scale = array_shift($scales);
$label = array_shift($labels);
while ($n > $scale && count($labels)) {
$remainder += ($n % $scale) * $accum;
$n /= $scale;
$accum *= $scale;
$label = array_shift($labels);
if (!count($scales)) {
break;
}
$scale = array_shift($scales);
}
if ($is_negative) {
$n = -$n;
$remainder = -$remainder;
}
if ($precision) {
$num_string = number_format($n, $precision);
} else {
$num_string = (int)floor($n);
}
if ($label) {
$num_string .= ' '.$label;
}
return $num_string;
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jul 2, 3:19 PM (1 d, 4 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
164993
Default Alt Text
(157 KB)

Event Timeline