1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-22 14:52:41 +01:00

Remove the "Releeph" application

Summary:
Ref T9530. Ref T13658. The "Releeph" application was never useful outside of Facebook and any application providing release support would not resemble it much.

It has some product name literal strings, so now is as good a time as any to get rid of it.

This application never left prototype and I'm not aware of any install in the wild that uses it (or has ever used it).

I did not destroy the database itself. I'll issue upgrade guidance and destroy the database in some future release, just in case.

Test Plan: Grepped for "releeph", found no relevant/removable hits.

Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam

Maniphest Tasks: T13658, T9530

Differential Revision: https://secure.phabricator.com/D21792
This commit is contained in:
epriestley 2022-04-25 16:31:05 -07:00
parent 27b512e284
commit 84b0c5a669
103 changed files with 8 additions and 9656 deletions

View file

@ -104,10 +104,6 @@ return array(
'rsrc/css/application/project/project-card-view.css' => 'a9f2c2dd', 'rsrc/css/application/project/project-card-view.css' => 'a9f2c2dd',
'rsrc/css/application/project/project-triggers.css' => 'cd9c8bb9', 'rsrc/css/application/project/project-triggers.css' => 'cd9c8bb9',
'rsrc/css/application/project/project-view.css' => '567858b3', 'rsrc/css/application/project/project-view.css' => '567858b3',
'rsrc/css/application/releeph/releeph-core.css' => 'f81ff2db',
'rsrc/css/application/releeph/releeph-preview-branch.css' => '22db5c07',
'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '0ac1ea31',
'rsrc/css/application/releeph/releeph-request-typeahead.css' => 'bce37359',
'rsrc/css/application/search/application-search-view.css' => '0f7c06d8', 'rsrc/css/application/search/application-search-view.css' => '0f7c06d8',
'rsrc/css/application/search/search-results.css' => '9ea70ace', 'rsrc/css/application/search/search-results.css' => '9ea70ace',
'rsrc/css/application/slowvote/slowvote.css' => '1694baed', 'rsrc/css/application/slowvote/slowvote.css' => '1694baed',
@ -435,9 +431,6 @@ return array(
'rsrc/js/application/projects/behavior-project-boards.js' => '58cb6a88', 'rsrc/js/application/projects/behavior-project-boards.js' => '58cb6a88',
'rsrc/js/application/projects/behavior-project-create.js' => '34c53422', 'rsrc/js/application/projects/behavior-project-create.js' => '34c53422',
'rsrc/js/application/projects/behavior-reorder-columns.js' => '8ac32fd9', 'rsrc/js/application/projects/behavior-reorder-columns.js' => '8ac32fd9',
'rsrc/js/application/releeph/releeph-preview-branch.js' => '75184d68',
'rsrc/js/application/releeph/releeph-request-state-change.js' => '9f081f05',
'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'aa3a100c',
'rsrc/js/application/repository/repository-crossreference.js' => '44d48cd1', 'rsrc/js/application/repository/repository-crossreference.js' => '44d48cd1',
'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e5bdb730', 'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e5bdb730',
'rsrc/js/application/search/behavior-reorder-queries.js' => 'b86f297f', 'rsrc/js/application/search/behavior-reorder-queries.js' => 'b86f297f',
@ -686,9 +679,6 @@ return array(
'javelin-behavior-read-only-warning' => 'b9109f8f', 'javelin-behavior-read-only-warning' => 'b9109f8f',
'javelin-behavior-redirect' => '407ee861', 'javelin-behavior-redirect' => '407ee861',
'javelin-behavior-refresh-csrf' => '46116c01', 'javelin-behavior-refresh-csrf' => '46116c01',
'javelin-behavior-releeph-preview-branch' => '75184d68',
'javelin-behavior-releeph-request-state-change' => '9f081f05',
'javelin-behavior-releeph-request-typeahead' => 'aa3a100c',
'javelin-behavior-remarkup-load-image' => '202bfa3f', 'javelin-behavior-remarkup-load-image' => '202bfa3f',
'javelin-behavior-remarkup-preview' => 'd8a86cfb', 'javelin-behavior-remarkup-preview' => 'd8a86cfb',
'javelin-behavior-reorder-applications' => 'aa371860', 'javelin-behavior-reorder-applications' => 'aa371860',
@ -913,10 +903,6 @@ return array(
'project-card-view-css' => 'a9f2c2dd', 'project-card-view-css' => 'a9f2c2dd',
'project-triggers-css' => 'cd9c8bb9', 'project-triggers-css' => 'cd9c8bb9',
'project-view-css' => '567858b3', 'project-view-css' => '567858b3',
'releeph-core' => 'f81ff2db',
'releeph-preview-branch' => '22db5c07',
'releeph-request-differential-create-dialog' => '0ac1ea31',
'releeph-request-typeahead-css' => 'bce37359',
'setup-issue-css' => '5eed85b2', 'setup-issue-css' => '5eed85b2',
'sprite-login-css' => '18b368a6', 'sprite-login-css' => '18b368a6',
'sprite-tokens-css' => 'f1896dc5', 'sprite-tokens-css' => 'f1896dc5',
@ -1606,12 +1592,6 @@ return array(
'javelin-behavior', 'javelin-behavior',
'javelin-dom', 'javelin-dom',
), ),
'75184d68' => array(
'javelin-behavior',
'javelin-dom',
'javelin-uri',
'javelin-request',
),
'78bc5d94' => array( '78bc5d94' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-uri', 'javelin-uri',
@ -1821,14 +1801,6 @@ return array(
'javelin-uri', 'javelin-uri',
'phabricator-textareautils', 'phabricator-textareautils',
), ),
'9f081f05' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-workflow',
'javelin-util',
'phabricator-keyboard-shortcut',
),
'a17b84f1' => array( 'a17b84f1' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-dom', 'javelin-dom',
@ -1896,13 +1868,6 @@ return array(
'javelin-dom', 'javelin-dom',
'phabricator-draggable-list', 'phabricator-draggable-list',
), ),
'aa3a100c' => array(
'javelin-behavior',
'javelin-dom',
'javelin-typeahead',
'javelin-typeahead-ondemand-source',
'javelin-dom',
),
'aa51efb4' => array( 'aa51efb4' => array(
'javelin-dom', 'javelin-dom',
), ),

View file

@ -8667,190 +8667,6 @@ CREATE TABLE `project_triggerusage` (
UNIQUE KEY `key_trigger` (`triggerPHID`) UNIQUE KEY `key_trigger` (`triggerPHID`)
) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT};
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_releeph` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */;
USE `{$NAMESPACE}_releeph`;
SET NAMES utf8 ;
SET character_set_client = {$CHARSET} ;
CREATE TABLE `releeph_branch` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`dateCreated` int(10) unsigned NOT NULL,
`dateModified` int(10) unsigned NOT NULL,
`basename` varchar(64) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`releephProjectID` int(10) unsigned NOT NULL,
`createdByUserPHID` varbinary(64) NOT NULL,
`cutPointCommitPHID` varbinary(64) NOT NULL,
`isActive` tinyint(1) NOT NULL DEFAULT '1',
`symbolicName` varchar(64) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} DEFAULT NULL,
`details` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`phid` varbinary(64) NOT NULL,
`name` varchar(128) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `releephProjectID_2` (`releephProjectID`,`basename`),
UNIQUE KEY `releephProjectID_name` (`releephProjectID`,`name`),
UNIQUE KEY `key_phid` (`phid`),
UNIQUE KEY `releephProjectID` (`releephProjectID`,`symbolicName`)
) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT};
USE `{$NAMESPACE}_releeph`;
SET NAMES utf8 ;
SET character_set_client = {$CHARSET} ;
CREATE TABLE `releeph_branchtransaction` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`phid` varbinary(64) NOT NULL,
`authorPHID` varbinary(64) NOT NULL,
`objectPHID` varbinary(64) NOT NULL,
`viewPolicy` varbinary(64) NOT NULL,
`editPolicy` varbinary(64) NOT NULL,
`commentPHID` varbinary(64) DEFAULT NULL,
`commentVersion` int(10) unsigned NOT NULL,
`transactionType` varchar(32) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`oldValue` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`newValue` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`contentSource` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`metadata` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`dateCreated` int(10) unsigned NOT NULL,
`dateModified` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `key_phid` (`phid`),
KEY `key_object` (`objectPHID`)
) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT};
USE `{$NAMESPACE}_releeph`;
SET NAMES utf8 ;
SET character_set_client = {$CHARSET} ;
CREATE TABLE `releeph_producttransaction` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`phid` varbinary(64) NOT NULL,
`authorPHID` varbinary(64) NOT NULL,
`objectPHID` varbinary(64) NOT NULL,
`viewPolicy` varbinary(64) NOT NULL,
`editPolicy` varbinary(64) NOT NULL,
`commentPHID` varbinary(64) DEFAULT NULL,
`commentVersion` int(10) unsigned NOT NULL,
`transactionType` varchar(32) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`oldValue` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`newValue` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`contentSource` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`metadata` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`dateCreated` int(10) unsigned NOT NULL,
`dateModified` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `key_phid` (`phid`),
KEY `key_object` (`objectPHID`)
) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT};
USE `{$NAMESPACE}_releeph`;
SET NAMES utf8 ;
SET character_set_client = {$CHARSET} ;
CREATE TABLE `releeph_project` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`dateCreated` int(10) unsigned NOT NULL,
`dateModified` int(10) unsigned NOT NULL,
`phid` varbinary(64) NOT NULL,
`name` varchar(128) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`trunkBranch` varchar(255) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`repositoryPHID` varbinary(64) NOT NULL,
`createdByUserPHID` varbinary(64) NOT NULL,
`isActive` tinyint(1) NOT NULL DEFAULT '1',
`details` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `projectName` (`name`),
UNIQUE KEY `key_phid` (`phid`)
) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT};
USE `{$NAMESPACE}_releeph`;
SET NAMES utf8 ;
SET character_set_client = {$CHARSET} ;
CREATE TABLE `releeph_request` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`dateCreated` int(10) unsigned NOT NULL,
`dateModified` int(10) unsigned NOT NULL,
`phid` varbinary(64) NOT NULL,
`branchID` int(10) unsigned NOT NULL,
`requestUserPHID` varbinary(64) NOT NULL,
`requestCommitPHID` varbinary(64) DEFAULT NULL,
`commitIdentifier` varchar(40) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} DEFAULT NULL,
`commitPHID` varbinary(64) DEFAULT NULL,
`pickStatus` int(10) unsigned DEFAULT NULL,
`details` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`userIntents` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT},
`inBranch` tinyint(1) NOT NULL DEFAULT '0',
`mailKey` binary(20) NOT NULL,
`requestedObjectPHID` varbinary(64) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `phid` (`phid`),
UNIQUE KEY `requestIdentifierBranch` (`requestCommitPHID`,`branchID`),
KEY `branchID` (`branchID`),
KEY `key_requestedObject` (`requestedObjectPHID`)
) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT};
USE `{$NAMESPACE}_releeph`;
SET NAMES utf8 ;
SET character_set_client = {$CHARSET} ;
CREATE TABLE `releeph_requesttransaction` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`phid` varbinary(64) NOT NULL,
`authorPHID` varbinary(64) NOT NULL,
`objectPHID` varbinary(64) NOT NULL,
`viewPolicy` varbinary(64) NOT NULL,
`editPolicy` varbinary(64) NOT NULL,
`commentPHID` varbinary(64) DEFAULT NULL,
`commentVersion` int(10) unsigned NOT NULL,
`transactionType` varchar(32) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`oldValue` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`newValue` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`metadata` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`contentSource` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`dateCreated` int(10) unsigned NOT NULL,
`dateModified` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `key_phid` (`phid`),
KEY `key_object` (`objectPHID`)
) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT};
USE `{$NAMESPACE}_releeph`;
SET NAMES utf8 ;
SET character_set_client = {$CHARSET} ;
CREATE TABLE `releeph_requesttransaction_comment` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`phid` varbinary(64) NOT NULL,
`transactionPHID` varbinary(64) DEFAULT NULL,
`authorPHID` varbinary(64) NOT NULL,
`viewPolicy` varbinary(64) NOT NULL,
`editPolicy` varbinary(64) NOT NULL,
`commentVersion` int(10) unsigned NOT NULL,
`content` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`contentSource` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL,
`isDeleted` tinyint(1) NOT NULL,
`dateCreated` int(10) unsigned NOT NULL,
`dateModified` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `key_phid` (`phid`),
UNIQUE KEY `key_version` (`transactionPHID`,`commentVersion`)
) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT};
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_repository` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */; CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_repository` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */;
USE `{$NAMESPACE}_repository`; USE `{$NAMESPACE}_repository`;

View file

@ -588,7 +588,6 @@ phutil_register_library_map(array(
'DifferentialQueryConduitAPIMethod' => 'applications/differential/conduit/DifferentialQueryConduitAPIMethod.php', 'DifferentialQueryConduitAPIMethod' => 'applications/differential/conduit/DifferentialQueryConduitAPIMethod.php',
'DifferentialQueryDiffsConduitAPIMethod' => 'applications/differential/conduit/DifferentialQueryDiffsConduitAPIMethod.php', 'DifferentialQueryDiffsConduitAPIMethod' => 'applications/differential/conduit/DifferentialQueryDiffsConduitAPIMethod.php',
'DifferentialRawDiffRenderer' => 'applications/differential/render/DifferentialRawDiffRenderer.php', 'DifferentialRawDiffRenderer' => 'applications/differential/render/DifferentialRawDiffRenderer.php',
'DifferentialReleephRequestFieldSpecification' => 'applications/releeph/differential/DifferentialReleephRequestFieldSpecification.php',
'DifferentialRemarkupRule' => 'applications/differential/remarkup/DifferentialRemarkupRule.php', 'DifferentialRemarkupRule' => 'applications/differential/remarkup/DifferentialRemarkupRule.php',
'DifferentialReplyHandler' => 'applications/differential/mail/DifferentialReplyHandler.php', 'DifferentialReplyHandler' => 'applications/differential/mail/DifferentialReplyHandler.php',
'DifferentialRepositoryField' => 'applications/differential/customfield/DifferentialRepositoryField.php', 'DifferentialRepositoryField' => 'applications/differential/customfield/DifferentialRepositoryField.php',
@ -4557,8 +4556,6 @@ phutil_register_library_map(array(
'PhabricatorRefreshCSRFController' => 'applications/auth/controller/PhabricatorRefreshCSRFController.php', 'PhabricatorRefreshCSRFController' => 'applications/auth/controller/PhabricatorRefreshCSRFController.php',
'PhabricatorRegexListConfigType' => 'applications/config/type/PhabricatorRegexListConfigType.php', 'PhabricatorRegexListConfigType' => 'applications/config/type/PhabricatorRegexListConfigType.php',
'PhabricatorRegistrationProfile' => 'applications/people/storage/PhabricatorRegistrationProfile.php', 'PhabricatorRegistrationProfile' => 'applications/people/storage/PhabricatorRegistrationProfile.php',
'PhabricatorReleephApplication' => 'applications/releeph/application/PhabricatorReleephApplication.php',
'PhabricatorReleephApplicationConfigOptions' => 'applications/releeph/config/PhabricatorReleephApplicationConfigOptions.php',
'PhabricatorRemarkupCachePurger' => 'applications/cache/purger/PhabricatorRemarkupCachePurger.php', 'PhabricatorRemarkupCachePurger' => 'applications/cache/purger/PhabricatorRemarkupCachePurger.php',
'PhabricatorRemarkupControl' => 'view/form/control/PhabricatorRemarkupControl.php', 'PhabricatorRemarkupControl' => 'view/form/control/PhabricatorRemarkupControl.php',
'PhabricatorRemarkupCowsayBlockInterpreter' => 'infrastructure/markup/interpreter/PhabricatorRemarkupCowsayBlockInterpreter.php', 'PhabricatorRemarkupCowsayBlockInterpreter' => 'infrastructure/markup/interpreter/PhabricatorRemarkupCowsayBlockInterpreter.php',
@ -5894,93 +5891,6 @@ phutil_register_library_map(array(
'ProjectSearchConduitAPIMethod' => 'applications/project/conduit/ProjectSearchConduitAPIMethod.php', 'ProjectSearchConduitAPIMethod' => 'applications/project/conduit/ProjectSearchConduitAPIMethod.php',
'QueryFormattingTestCase' => 'infrastructure/storage/__tests__/QueryFormattingTestCase.php', 'QueryFormattingTestCase' => 'infrastructure/storage/__tests__/QueryFormattingTestCase.php',
'QueryFuture' => 'infrastructure/storage/future/QueryFuture.php', 'QueryFuture' => 'infrastructure/storage/future/QueryFuture.php',
'ReleephAuthorFieldSpecification' => 'applications/releeph/field/specification/ReleephAuthorFieldSpecification.php',
'ReleephBranch' => 'applications/releeph/storage/ReleephBranch.php',
'ReleephBranchAccessController' => 'applications/releeph/controller/branch/ReleephBranchAccessController.php',
'ReleephBranchCommitFieldSpecification' => 'applications/releeph/field/specification/ReleephBranchCommitFieldSpecification.php',
'ReleephBranchController' => 'applications/releeph/controller/branch/ReleephBranchController.php',
'ReleephBranchCreateController' => 'applications/releeph/controller/branch/ReleephBranchCreateController.php',
'ReleephBranchEditController' => 'applications/releeph/controller/branch/ReleephBranchEditController.php',
'ReleephBranchEditor' => 'applications/releeph/editor/ReleephBranchEditor.php',
'ReleephBranchHistoryController' => 'applications/releeph/controller/branch/ReleephBranchHistoryController.php',
'ReleephBranchNamePreviewController' => 'applications/releeph/controller/branch/ReleephBranchNamePreviewController.php',
'ReleephBranchPHIDType' => 'applications/releeph/phid/ReleephBranchPHIDType.php',
'ReleephBranchPreviewView' => 'applications/releeph/view/branch/ReleephBranchPreviewView.php',
'ReleephBranchQuery' => 'applications/releeph/query/ReleephBranchQuery.php',
'ReleephBranchSearchEngine' => 'applications/releeph/query/ReleephBranchSearchEngine.php',
'ReleephBranchTemplate' => 'applications/releeph/view/branch/ReleephBranchTemplate.php',
'ReleephBranchTransaction' => 'applications/releeph/storage/ReleephBranchTransaction.php',
'ReleephBranchTransactionQuery' => 'applications/releeph/query/ReleephBranchTransactionQuery.php',
'ReleephBranchViewController' => 'applications/releeph/controller/branch/ReleephBranchViewController.php',
'ReleephCommitFinder' => 'applications/releeph/commitfinder/ReleephCommitFinder.php',
'ReleephCommitFinderException' => 'applications/releeph/commitfinder/ReleephCommitFinderException.php',
'ReleephCommitMessageFieldSpecification' => 'applications/releeph/field/specification/ReleephCommitMessageFieldSpecification.php',
'ReleephConduitAPIMethod' => 'applications/releeph/conduit/ReleephConduitAPIMethod.php',
'ReleephController' => 'applications/releeph/controller/ReleephController.php',
'ReleephDAO' => 'applications/releeph/storage/ReleephDAO.php',
'ReleephDefaultFieldSelector' => 'applications/releeph/field/selector/ReleephDefaultFieldSelector.php',
'ReleephDependsOnFieldSpecification' => 'applications/releeph/field/specification/ReleephDependsOnFieldSpecification.php',
'ReleephDiffChurnFieldSpecification' => 'applications/releeph/field/specification/ReleephDiffChurnFieldSpecification.php',
'ReleephDiffMessageFieldSpecification' => 'applications/releeph/field/specification/ReleephDiffMessageFieldSpecification.php',
'ReleephDiffSizeFieldSpecification' => 'applications/releeph/field/specification/ReleephDiffSizeFieldSpecification.php',
'ReleephFieldParseException' => 'applications/releeph/field/exception/ReleephFieldParseException.php',
'ReleephFieldSelector' => 'applications/releeph/field/selector/ReleephFieldSelector.php',
'ReleephFieldSpecification' => 'applications/releeph/field/specification/ReleephFieldSpecification.php',
'ReleephGetBranchesConduitAPIMethod' => 'applications/releeph/conduit/ReleephGetBranchesConduitAPIMethod.php',
'ReleephIntentFieldSpecification' => 'applications/releeph/field/specification/ReleephIntentFieldSpecification.php',
'ReleephLevelFieldSpecification' => 'applications/releeph/field/specification/ReleephLevelFieldSpecification.php',
'ReleephOriginalCommitFieldSpecification' => 'applications/releeph/field/specification/ReleephOriginalCommitFieldSpecification.php',
'ReleephProductActionController' => 'applications/releeph/controller/product/ReleephProductActionController.php',
'ReleephProductController' => 'applications/releeph/controller/product/ReleephProductController.php',
'ReleephProductCreateController' => 'applications/releeph/controller/product/ReleephProductCreateController.php',
'ReleephProductEditController' => 'applications/releeph/controller/product/ReleephProductEditController.php',
'ReleephProductEditor' => 'applications/releeph/editor/ReleephProductEditor.php',
'ReleephProductHistoryController' => 'applications/releeph/controller/product/ReleephProductHistoryController.php',
'ReleephProductListController' => 'applications/releeph/controller/product/ReleephProductListController.php',
'ReleephProductPHIDType' => 'applications/releeph/phid/ReleephProductPHIDType.php',
'ReleephProductQuery' => 'applications/releeph/query/ReleephProductQuery.php',
'ReleephProductSearchEngine' => 'applications/releeph/query/ReleephProductSearchEngine.php',
'ReleephProductTransaction' => 'applications/releeph/storage/ReleephProductTransaction.php',
'ReleephProductTransactionQuery' => 'applications/releeph/query/ReleephProductTransactionQuery.php',
'ReleephProductViewController' => 'applications/releeph/controller/product/ReleephProductViewController.php',
'ReleephProject' => 'applications/releeph/storage/ReleephProject.php',
'ReleephQueryBranchesConduitAPIMethod' => 'applications/releeph/conduit/ReleephQueryBranchesConduitAPIMethod.php',
'ReleephQueryProductsConduitAPIMethod' => 'applications/releeph/conduit/ReleephQueryProductsConduitAPIMethod.php',
'ReleephQueryRequestsConduitAPIMethod' => 'applications/releeph/conduit/ReleephQueryRequestsConduitAPIMethod.php',
'ReleephReasonFieldSpecification' => 'applications/releeph/field/specification/ReleephReasonFieldSpecification.php',
'ReleephRequest' => 'applications/releeph/storage/ReleephRequest.php',
'ReleephRequestActionController' => 'applications/releeph/controller/request/ReleephRequestActionController.php',
'ReleephRequestCommentController' => 'applications/releeph/controller/request/ReleephRequestCommentController.php',
'ReleephRequestConduitAPIMethod' => 'applications/releeph/conduit/ReleephRequestConduitAPIMethod.php',
'ReleephRequestController' => 'applications/releeph/controller/request/ReleephRequestController.php',
'ReleephRequestDifferentialCreateController' => 'applications/releeph/controller/request/ReleephRequestDifferentialCreateController.php',
'ReleephRequestEditController' => 'applications/releeph/controller/request/ReleephRequestEditController.php',
'ReleephRequestMailReceiver' => 'applications/releeph/mail/ReleephRequestMailReceiver.php',
'ReleephRequestPHIDType' => 'applications/releeph/phid/ReleephRequestPHIDType.php',
'ReleephRequestQuery' => 'applications/releeph/query/ReleephRequestQuery.php',
'ReleephRequestReplyHandler' => 'applications/releeph/mail/ReleephRequestReplyHandler.php',
'ReleephRequestSearchEngine' => 'applications/releeph/query/ReleephRequestSearchEngine.php',
'ReleephRequestStatus' => 'applications/releeph/constants/ReleephRequestStatus.php',
'ReleephRequestTransaction' => 'applications/releeph/storage/ReleephRequestTransaction.php',
'ReleephRequestTransactionComment' => 'applications/releeph/storage/ReleephRequestTransactionComment.php',
'ReleephRequestTransactionQuery' => 'applications/releeph/query/ReleephRequestTransactionQuery.php',
'ReleephRequestTransactionalEditor' => 'applications/releeph/editor/ReleephRequestTransactionalEditor.php',
'ReleephRequestTypeaheadControl' => 'applications/releeph/view/request/ReleephRequestTypeaheadControl.php',
'ReleephRequestTypeaheadController' => 'applications/releeph/controller/request/ReleephRequestTypeaheadController.php',
'ReleephRequestView' => 'applications/releeph/view/ReleephRequestView.php',
'ReleephRequestViewController' => 'applications/releeph/controller/request/ReleephRequestViewController.php',
'ReleephRequestorFieldSpecification' => 'applications/releeph/field/specification/ReleephRequestorFieldSpecification.php',
'ReleephRevisionFieldSpecification' => 'applications/releeph/field/specification/ReleephRevisionFieldSpecification.php',
'ReleephSeverityFieldSpecification' => 'applications/releeph/field/specification/ReleephSeverityFieldSpecification.php',
'ReleephSummaryFieldSpecification' => 'applications/releeph/field/specification/ReleephSummaryFieldSpecification.php',
'ReleephWorkCanPushConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkCanPushConduitAPIMethod.php',
'ReleephWorkGetAuthorInfoConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkGetAuthorInfoConduitAPIMethod.php',
'ReleephWorkGetBranchCommitMessageConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkGetBranchCommitMessageConduitAPIMethod.php',
'ReleephWorkGetBranchConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkGetBranchConduitAPIMethod.php',
'ReleephWorkGetCommitMessageConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkGetCommitMessageConduitAPIMethod.php',
'ReleephWorkNextRequestConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkNextRequestConduitAPIMethod.php',
'ReleephWorkRecordConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkRecordConduitAPIMethod.php',
'ReleephWorkRecordPickStatusConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkRecordPickStatusConduitAPIMethod.php',
'RemarkupProcessConduitAPIMethod' => 'applications/remarkup/conduit/RemarkupProcessConduitAPIMethod.php', 'RemarkupProcessConduitAPIMethod' => 'applications/remarkup/conduit/RemarkupProcessConduitAPIMethod.php',
'RepositoryConduitAPIMethod' => 'applications/repository/conduit/RepositoryConduitAPIMethod.php', 'RepositoryConduitAPIMethod' => 'applications/repository/conduit/RepositoryConduitAPIMethod.php',
'RepositoryQueryConduitAPIMethod' => 'applications/repository/conduit/RepositoryQueryConduitAPIMethod.php', 'RepositoryQueryConduitAPIMethod' => 'applications/repository/conduit/RepositoryQueryConduitAPIMethod.php',
@ -6714,7 +6624,6 @@ phutil_register_library_map(array(
'DifferentialQueryConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialQueryConduitAPIMethod' => 'DifferentialConduitAPIMethod',
'DifferentialQueryDiffsConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialQueryDiffsConduitAPIMethod' => 'DifferentialConduitAPIMethod',
'DifferentialRawDiffRenderer' => 'Phobject', 'DifferentialRawDiffRenderer' => 'Phobject',
'DifferentialReleephRequestFieldSpecification' => 'Phobject',
'DifferentialRemarkupRule' => 'PhabricatorObjectRemarkupRule', 'DifferentialRemarkupRule' => 'PhabricatorObjectRemarkupRule',
'DifferentialReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 'DifferentialReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
'DifferentialRepositoryField' => 'DifferentialCoreCustomField', 'DifferentialRepositoryField' => 'DifferentialCoreCustomField',
@ -11300,8 +11209,6 @@ phutil_register_library_map(array(
'PhabricatorRefreshCSRFController' => 'PhabricatorAuthController', 'PhabricatorRefreshCSRFController' => 'PhabricatorAuthController',
'PhabricatorRegexListConfigType' => 'PhabricatorTextListConfigType', 'PhabricatorRegexListConfigType' => 'PhabricatorTextListConfigType',
'PhabricatorRegistrationProfile' => 'Phobject', 'PhabricatorRegistrationProfile' => 'Phobject',
'PhabricatorReleephApplication' => 'PhabricatorApplication',
'PhabricatorReleephApplicationConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorRemarkupCachePurger' => 'PhabricatorCachePurger', 'PhabricatorRemarkupCachePurger' => 'PhabricatorCachePurger',
'PhabricatorRemarkupControl' => 'AphrontFormTextAreaControl', 'PhabricatorRemarkupControl' => 'AphrontFormTextAreaControl',
'PhabricatorRemarkupCowsayBlockInterpreter' => 'PhutilRemarkupBlockInterpreter', 'PhabricatorRemarkupCowsayBlockInterpreter' => 'PhutilRemarkupBlockInterpreter',
@ -12919,109 +12826,6 @@ phutil_register_library_map(array(
'ProjectSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'ProjectSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
'QueryFormattingTestCase' => 'PhabricatorTestCase', 'QueryFormattingTestCase' => 'PhabricatorTestCase',
'QueryFuture' => 'Future', 'QueryFuture' => 'Future',
'ReleephAuthorFieldSpecification' => 'ReleephFieldSpecification',
'ReleephBranch' => array(
'ReleephDAO',
'PhabricatorApplicationTransactionInterface',
'PhabricatorPolicyInterface',
),
'ReleephBranchAccessController' => 'ReleephBranchController',
'ReleephBranchCommitFieldSpecification' => 'ReleephFieldSpecification',
'ReleephBranchController' => 'ReleephController',
'ReleephBranchCreateController' => 'ReleephProductController',
'ReleephBranchEditController' => 'ReleephBranchController',
'ReleephBranchEditor' => 'PhabricatorEditor',
'ReleephBranchHistoryController' => 'ReleephBranchController',
'ReleephBranchNamePreviewController' => 'ReleephController',
'ReleephBranchPHIDType' => 'PhabricatorPHIDType',
'ReleephBranchPreviewView' => 'AphrontFormControl',
'ReleephBranchQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'ReleephBranchSearchEngine' => 'PhabricatorApplicationSearchEngine',
'ReleephBranchTemplate' => 'Phobject',
'ReleephBranchTransaction' => 'PhabricatorApplicationTransaction',
'ReleephBranchTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'ReleephBranchViewController' => 'ReleephBranchController',
'ReleephCommitFinder' => 'Phobject',
'ReleephCommitFinderException' => 'Exception',
'ReleephCommitMessageFieldSpecification' => 'ReleephFieldSpecification',
'ReleephConduitAPIMethod' => 'ConduitAPIMethod',
'ReleephController' => 'PhabricatorController',
'ReleephDAO' => 'PhabricatorLiskDAO',
'ReleephDefaultFieldSelector' => 'ReleephFieldSelector',
'ReleephDependsOnFieldSpecification' => 'ReleephFieldSpecification',
'ReleephDiffChurnFieldSpecification' => 'ReleephFieldSpecification',
'ReleephDiffMessageFieldSpecification' => 'ReleephFieldSpecification',
'ReleephDiffSizeFieldSpecification' => 'ReleephFieldSpecification',
'ReleephFieldParseException' => 'Exception',
'ReleephFieldSelector' => 'Phobject',
'ReleephFieldSpecification' => array(
'PhabricatorCustomField',
'PhabricatorMarkupInterface',
),
'ReleephGetBranchesConduitAPIMethod' => 'ReleephConduitAPIMethod',
'ReleephIntentFieldSpecification' => 'ReleephFieldSpecification',
'ReleephLevelFieldSpecification' => 'ReleephFieldSpecification',
'ReleephOriginalCommitFieldSpecification' => 'ReleephFieldSpecification',
'ReleephProductActionController' => 'ReleephProductController',
'ReleephProductController' => 'ReleephController',
'ReleephProductCreateController' => 'ReleephProductController',
'ReleephProductEditController' => 'ReleephProductController',
'ReleephProductEditor' => 'PhabricatorApplicationTransactionEditor',
'ReleephProductHistoryController' => 'ReleephProductController',
'ReleephProductListController' => 'ReleephController',
'ReleephProductPHIDType' => 'PhabricatorPHIDType',
'ReleephProductQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'ReleephProductSearchEngine' => 'PhabricatorApplicationSearchEngine',
'ReleephProductTransaction' => 'PhabricatorApplicationTransaction',
'ReleephProductTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'ReleephProductViewController' => 'ReleephProductController',
'ReleephProject' => array(
'ReleephDAO',
'PhabricatorApplicationTransactionInterface',
'PhabricatorPolicyInterface',
),
'ReleephQueryBranchesConduitAPIMethod' => 'ReleephConduitAPIMethod',
'ReleephQueryProductsConduitAPIMethod' => 'ReleephConduitAPIMethod',
'ReleephQueryRequestsConduitAPIMethod' => 'ReleephConduitAPIMethod',
'ReleephReasonFieldSpecification' => 'ReleephFieldSpecification',
'ReleephRequest' => array(
'ReleephDAO',
'PhabricatorApplicationTransactionInterface',
'PhabricatorPolicyInterface',
'PhabricatorCustomFieldInterface',
),
'ReleephRequestActionController' => 'ReleephRequestController',
'ReleephRequestCommentController' => 'ReleephRequestController',
'ReleephRequestConduitAPIMethod' => 'ReleephConduitAPIMethod',
'ReleephRequestController' => 'ReleephController',
'ReleephRequestDifferentialCreateController' => 'ReleephController',
'ReleephRequestEditController' => 'ReleephBranchController',
'ReleephRequestMailReceiver' => 'PhabricatorObjectMailReceiver',
'ReleephRequestPHIDType' => 'PhabricatorPHIDType',
'ReleephRequestQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'ReleephRequestReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
'ReleephRequestSearchEngine' => 'PhabricatorApplicationSearchEngine',
'ReleephRequestStatus' => 'Phobject',
'ReleephRequestTransaction' => 'PhabricatorApplicationTransaction',
'ReleephRequestTransactionComment' => 'PhabricatorApplicationTransactionComment',
'ReleephRequestTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'ReleephRequestTransactionalEditor' => 'PhabricatorApplicationTransactionEditor',
'ReleephRequestTypeaheadControl' => 'AphrontFormControl',
'ReleephRequestTypeaheadController' => 'PhabricatorTypeaheadDatasourceController',
'ReleephRequestView' => 'AphrontView',
'ReleephRequestViewController' => 'ReleephBranchController',
'ReleephRequestorFieldSpecification' => 'ReleephFieldSpecification',
'ReleephRevisionFieldSpecification' => 'ReleephFieldSpecification',
'ReleephSeverityFieldSpecification' => 'ReleephLevelFieldSpecification',
'ReleephSummaryFieldSpecification' => 'ReleephFieldSpecification',
'ReleephWorkCanPushConduitAPIMethod' => 'ReleephConduitAPIMethod',
'ReleephWorkGetAuthorInfoConduitAPIMethod' => 'ReleephConduitAPIMethod',
'ReleephWorkGetBranchCommitMessageConduitAPIMethod' => 'ReleephConduitAPIMethod',
'ReleephWorkGetBranchConduitAPIMethod' => 'ReleephConduitAPIMethod',
'ReleephWorkGetCommitMessageConduitAPIMethod' => 'ReleephConduitAPIMethod',
'ReleephWorkNextRequestConduitAPIMethod' => 'ReleephConduitAPIMethod',
'ReleephWorkRecordConduitAPIMethod' => 'ReleephConduitAPIMethod',
'ReleephWorkRecordPickStatusConduitAPIMethod' => 'ReleephConduitAPIMethod',
'RemarkupProcessConduitAPIMethod' => 'ConduitAPIMethod', 'RemarkupProcessConduitAPIMethod' => 'ConduitAPIMethod',
'RepositoryConduitAPIMethod' => 'ConduitAPIMethod', 'RepositoryConduitAPIMethod' => 'ConduitAPIMethod',
'RepositoryQueryConduitAPIMethod' => 'RepositoryConduitAPIMethod', 'RepositoryQueryConduitAPIMethod' => 'RepositoryConduitAPIMethod',

View file

@ -14,34 +14,34 @@ final class PhabricatorOwnersPackageTestCase extends PhabricatorTestCase {
'id' => 1, 'id' => 1,
'excluded' => 1, 'excluded' => 1,
'dominion' => PhabricatorOwnersPackage::DOMINION_STRONG, 'dominion' => PhabricatorOwnersPackage::DOMINION_STRONG,
'path' => 'src/releeph/', 'path' => 'src/example/',
), ),
array( array(
'id' => 2, 'id' => 2,
'excluded' => 0, 'excluded' => 0,
'dominion' => PhabricatorOwnersPackage::DOMINION_STRONG, 'dominion' => PhabricatorOwnersPackage::DOMINION_STRONG,
'path' => 'src/releeph/', 'path' => 'src/example/',
), ),
); );
$paths = array( $paths = array(
'src/' => array('src/a.php' => true, 'src/releeph/b.php' => true), 'src/' => array('src/a.php' => true, 'src/example/b.php' => true),
'src/releeph/' => array('src/releeph/b.php' => true), 'src/example/' => array('src/example/b.php' => true),
); );
$this->assertEqual( $this->assertEqual(
array( array(
1 => strlen('src/'), 1 => strlen('src/'),
2 => strlen('src/releeph/'), 2 => strlen('src/example/'),
), ),
PhabricatorOwnersPackage::findLongestPathsPerPackage($rows, $paths)); PhabricatorOwnersPackage::findLongestPathsPerPackage($rows, $paths));
$paths = array( $paths = array(
'src/' => array('src/releeph/b.php' => true), 'src/' => array('src/example/b.php' => true),
'src/releeph/' => array('src/releeph/b.php' => true), 'src/example/' => array('src/example/b.php' => true),
); );
$this->assertEqual( $this->assertEqual(
array( array(
2 => strlen('src/releeph/'), 2 => strlen('src/example/'),
), ),
PhabricatorOwnersPackage::findLongestPathsPerPackage($rows, $paths)); PhabricatorOwnersPackage::findLongestPathsPerPackage($rows, $paths));

View file

@ -1,92 +0,0 @@
<?php
final class PhabricatorReleephApplication extends PhabricatorApplication {
public function getName() {
return pht('Releeph');
}
public function getShortDescription() {
return pht('Pull Requests');
}
public function getBaseURI() {
return '/releeph/';
}
public function getIcon() {
return 'fa-flag-checkered';
}
public function getApplicationGroup() {
return self::GROUP_UTILITIES;
}
public function isPrototype() {
return true;
}
public function getRoutes() {
return array(
'/Y(?P<requestID>[1-9]\d*)' => 'ReleephRequestViewController',
// TODO: Remove these older routes eventually.
'/RQ(?P<requestID>[1-9]\d*)' => 'ReleephRequestViewController',
'/releeph/request/(?P<requestID>[1-9]\d*)/'
=> 'ReleephRequestViewController',
'/releeph/' => array(
'' => 'ReleephProductListController',
'(?:product|project)/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'ReleephProductListController',
'create/' => 'ReleephProductCreateController',
'(?P<projectID>[1-9]\d*)/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'ReleephProductViewController',
'edit/' => 'ReleephProductEditController',
'cutbranch/' => 'ReleephBranchCreateController',
'action/(?P<action>.+)/' => 'ReleephProductActionController',
'history/' => 'ReleephProductHistoryController',
),
),
'branch/' => array(
'edit/(?P<branchID>[1-9]\d*)/'
=> 'ReleephBranchEditController',
'(?P<action>close|re-open)/(?P<branchID>[1-9]\d*)/'
=> 'ReleephBranchAccessController',
'preview/' => 'ReleephBranchNamePreviewController',
'(?P<branchID>[1-9]\d*)/' => array(
'history/' => 'ReleephBranchHistoryController',
'(?:query/(?P<queryKey>[^/]+)/)?' => 'ReleephBranchViewController',
),
'pull/(?P<branchID>[1-9]\d*)/'
=> 'ReleephRequestEditController',
),
'request/' => array(
'create/' => 'ReleephRequestEditController',
'differentialcreate/' => array(
'D(?P<diffRevID>[1-9]\d*)' =>
'ReleephRequestDifferentialCreateController',
),
'edit/(?P<requestID>[1-9]\d*)/'
=> 'ReleephRequestEditController',
'action/(?P<action>.+)/(?P<requestID>[1-9]\d*)/'
=> 'ReleephRequestActionController',
'typeahead/' =>
'ReleephRequestTypeaheadController',
'comment/(?P<requestID>[1-9]\d*)/'
=> 'ReleephRequestCommentController',
),
),
);
}
public function getMailCommandObjects() {
// TODO: Pull requests don't implement any interfaces which give them
// meaningful commands, so don't expose ReleephRequest here for now.
// Once we add relevant commands, return it here.
return array();
}
}

View file

@ -1,120 +0,0 @@
<?php
final class ReleephCommitFinder extends Phobject {
private $releephProject;
private $user;
private $objectPHID;
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function getUser() {
return $this->user;
}
public function setReleephProject(ReleephProject $rp) {
$this->releephProject = $rp;
return $this;
}
public function getRequestedObjectPHID() {
return $this->objectPHID;
}
public function fromPartial($partial_string) {
$this->objectPHID = null;
// Look for diffs
$matches = array();
if (preg_match('/^D([1-9]\d*)$/', $partial_string, $matches)) {
$diff_id = $matches[1];
$diff_rev = id(new DifferentialRevisionQuery())
->setViewer($this->getUser())
->withIDs(array($diff_id))
->needCommitPHIDs(true)
->executeOne();
if (!$diff_rev) {
throw new ReleephCommitFinderException(
pht(
'%s does not refer to an existing diff.',
$partial_string));
}
$commit_phids = $diff_rev->getCommitPHIDs();
if (!$commit_phids) {
throw new ReleephCommitFinderException(
pht(
'%s has no commits associated with it yet.',
$partial_string));
}
$this->objectPHID = $diff_rev->getPHID();
$commits = id(new DiffusionCommitQuery())
->setViewer($this->getUser())
->withPHIDs($commit_phids)
->execute();
$commits = msort($commits, 'getEpoch');
return head($commits);
}
// Look for a raw commit number, or r<callsign><commit-number>.
$repository = $this->releephProject->getRepository();
$dr_data = null;
$matches = array();
if (preg_match('/^r(?P<callsign>[A-Z]+)(?P<commit>\w+)$/',
$partial_string, $matches)) {
$callsign = $matches['callsign'];
if ($callsign != $repository->getCallsign()) {
throw new ReleephCommitFinderException(
pht(
'%s is in a different repository to this Releeph project (%s).',
$partial_string,
$repository->getCallsign()));
} else {
$dr_data = $matches;
}
} else {
$dr_data = array(
'callsign' => $repository->getCallsign(),
'commit' => $partial_string,
);
}
try {
$dr_data['user'] = $this->getUser();
$dr = DiffusionRequest::newFromDictionary($dr_data);
} catch (Exception $ex) {
$message = pht(
'No commit matches %s: %s',
$partial_string,
$ex->getMessage());
throw new ReleephCommitFinderException($message);
}
$phabricator_repository_commit = $dr->loadCommit();
if (!$phabricator_repository_commit) {
throw new ReleephCommitFinderException(
pht(
"The commit %s doesn't exist in this repository.",
$partial_string));
}
// When requesting a single commit, if it has an associated review we
// imply the review was requested instead. This is always correct for now
// and consistent with the older behavior, although it might not be the
// right rule in the future.
$phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$phabricator_repository_commit->getPHID(),
DiffusionCommitHasRevisionEdgeType::EDGECONST);
if ($phids) {
$this->objectPHID = head($phids);
}
return $phabricator_repository_commit;
}
}

View file

@ -1,3 +0,0 @@
<?php
final class ReleephCommitFinderException extends Exception {}

View file

@ -1,17 +0,0 @@
<?php
abstract class ReleephConduitAPIMethod extends ConduitAPIMethod {
public function getMethodStatus() {
return self::METHOD_STATUS_UNSTABLE;
}
public function getMethodStatusDescription() {
return pht('All Releeph methods are subject to abrupt change.');
}
final public function getApplication() {
return PhabricatorApplication::getByClass('PhabricatorReleephApplication');
}
}

View file

@ -1,58 +0,0 @@
<?php
final class ReleephGetBranchesConduitAPIMethod extends ReleephConduitAPIMethod {
public function getAPIMethodName() {
return 'releeph.getbranches';
}
public function getMethodDescription() {
return pht('Return information about all active Releeph branches.');
}
protected function defineParamTypes() {
return array(
);
}
protected function defineReturnType() {
return 'nonempty list<dict<string, wild>>';
}
protected function execute(ConduitAPIRequest $request) {
$results = array();
$projects = id(new ReleephProductQuery())
->setViewer($request->getUser())
->withActive(1)
->execute();
foreach ($projects as $project) {
$repository = $project->getRepository();
$branches = id(new ReleephBranch())->loadAllWhere(
'releephProjectID = %d AND isActive = 1',
$project->getID());
foreach ($branches as $branch) {
$full_branch_name = $branch->getName();
$cut_point_commit = id(new PhabricatorRepositoryCommit())->loadOneWhere(
'phid = %s',
$branch->getCutPointCommitPHID());
$results[] = array(
'project' => $project->getName(),
'repository' => $repository->getCallsign(),
'branch' => $branch->getBasename(),
'fullBranchName' => $full_branch_name,
'symbolicName' => $branch->getSymbolicName(),
'cutPoint' => $cut_point_commit->getCommitIdentifier(),
);
}
}
return $results;
}
}

View file

@ -1,73 +0,0 @@
<?php
final class ReleephQueryBranchesConduitAPIMethod
extends ReleephConduitAPIMethod {
public function getAPIMethodName() {
return 'releeph.querybranches';
}
public function getMethodDescription() {
return pht('Query information about Releeph branches.');
}
protected function defineParamTypes() {
return array(
'ids' => 'optional list<id>',
'phids' => 'optional list<phid>',
'productPHIDs' => 'optional list<phid>',
) + $this->getPagerParamTypes();
}
protected function defineReturnType() {
return 'query-results';
}
protected function execute(ConduitAPIRequest $request) {
$viewer = $request->getUser();
$query = id(new ReleephBranchQuery())
->setViewer($viewer);
$ids = $request->getValue('ids');
if ($ids !== null) {
$query->withIDs($ids);
}
$phids = $request->getValue('phids');
if ($phids !== null) {
$query->withPHIDs($phids);
}
$product_phids = $request->getValue('productPHIDs');
if ($product_phids !== null) {
$query->withProductPHIDs($product_phids);
}
$pager = $this->newPager($request);
$branches = $query->executeWithCursorPager($pager);
$data = array();
foreach ($branches as $branch) {
$id = $branch->getID();
$uri = '/releeph/branch/'.$id.'/';
$uri = PhabricatorEnv::getProductionURI($uri);
$data[] = array(
'id' => $id,
'phid' => $branch->getPHID(),
'uri' => $uri,
'name' => $branch->getName(),
'productPHID' => $branch->getProduct()->getPHID(),
);
}
return $this->addPagerResults(
array(
'data' => $data,
),
$pager);
}
}

View file

@ -1,80 +0,0 @@
<?php
final class ReleephQueryProductsConduitAPIMethod
extends ReleephConduitAPIMethod {
public function getAPIMethodName() {
return 'releeph.queryproducts';
}
public function getMethodDescription() {
return pht('Query information about Releeph products.');
}
protected function defineParamTypes() {
return array(
'ids' => 'optional list<id>',
'phids' => 'optional list<phid>',
'repositoryPHIDs' => 'optional list<phid>',
'isActive' => 'optional bool',
) + $this->getPagerParamTypes();
}
protected function defineReturnType() {
return 'query-results';
}
protected function execute(ConduitAPIRequest $request) {
$viewer = $request->getUser();
$query = id(new ReleephProductQuery())
->setViewer($viewer);
$ids = $request->getValue('ids');
if ($ids !== null) {
$query->withIDs($ids);
}
$phids = $request->getValue('phids');
if ($phids !== null) {
$query->withPHIDs($phids);
}
$repository_phids = $request->getValue('repositoryPHIDs');
if ($repository_phids !== null) {
$query->withRepositoryPHIDs($repository_phids);
}
$is_active = $request->getValue('isActive');
if ($is_active !== null) {
$query->withActive($is_active);
}
$pager = $this->newPager($request);
$products = $query->executeWithCursorPager($pager);
$data = array();
foreach ($products as $product) {
$id = $product->getID();
$uri = '/releeph/product/'.$id.'/';
$uri = PhabricatorEnv::getProductionURI($uri);
$data[] = array(
'id' => $id,
'phid' => $product->getPHID(),
'uri' => $uri,
'name' => $product->getName(),
'isActive' => (bool)$product->getIsActive(),
'repositoryPHID' => $product->getRepositoryPHID(),
);
}
return $this->addPagerResults(
array(
'data' => $data,
),
$pager);
}
}

View file

@ -1,77 +0,0 @@
<?php
final class ReleephQueryRequestsConduitAPIMethod
extends ReleephConduitAPIMethod {
public function getAPIMethodName() {
return 'releeph.queryrequests';
}
public function getMethodDescription() {
return pht(
'Return information about all Releeph requests linked to the given ids.');
}
protected function defineParamTypes() {
return array(
'revisionPHIDs' => 'optional list<phid>',
'requestedCommitPHIDs' => 'optional list<phid>',
);
}
protected function defineReturnType() {
return 'dict<string, wild>';
}
protected function execute(ConduitAPIRequest $conduit_request) {
$revision_phids = $conduit_request->getValue('revisionPHIDs');
$requested_commit_phids =
$conduit_request->getValue('requestedCommitPHIDs');
$result = array();
if (!$revision_phids && !$requested_commit_phids) {
return $result;
}
$query = new ReleephRequestQuery();
$query->setViewer($conduit_request->getUser());
if ($revision_phids) {
$query->withRequestedObjectPHIDs($revision_phids);
} else if ($requested_commit_phids) {
$query->withRequestedCommitPHIDs($requested_commit_phids);
}
$releeph_requests = $query->execute();
foreach ($releeph_requests as $releeph_request) {
$branch = $releeph_request->getBranch();
$request_commit_phid = $releeph_request->getRequestCommitPHID();
$object = $releeph_request->getRequestedObject();
if ($object instanceof DifferentialRevision) {
$object_phid = $object->getPHID();
} else {
$object_phid = null;
}
$status = $releeph_request->getStatus();
$status_name = ReleephRequestStatus::getStatusDescriptionFor($status);
$url = PhabricatorEnv::getProductionURI('/RQ'.$releeph_request->getID());
$result[] = array(
'branchBasename' => $branch->getBasename(),
'branchSymbolic' => $branch->getSymbolicName(),
'requestID' => $releeph_request->getID(),
'revisionPHID' => $object_phid,
'status' => $status,
'status_name' => $status_name,
'url' => $url,
);
}
return $result;
}
}

View file

@ -1,169 +0,0 @@
<?php
final class ReleephRequestConduitAPIMethod extends ReleephConduitAPIMethod {
public function getAPIMethodName() {
return 'releeph.request';
}
public function getMethodDescription() {
return pht('Request a commit or diff to be picked to a branch.');
}
protected function defineParamTypes() {
return array(
'branchPHID' => 'required string',
'things' => 'required list<string>',
'fields' => 'dict<string, string>',
);
}
protected function defineReturnType() {
return 'dict<string, wild>';
}
protected function defineErrorTypes() {
return array(
'ERR_BRANCH' => pht('Unknown Releeph branch.'),
'ERR_FIELD_PARSE' => pht('Unable to parse a Releeph field.'),
);
}
protected function execute(ConduitAPIRequest $request) {
$user = $request->getUser();
$viewer_handle = id(new PhabricatorHandleQuery())
->setViewer($user)
->withPHIDs(array($user->getPHID()))
->executeOne();
$branch_phid = $request->getValue('branchPHID');
$releeph_branch = id(new ReleephBranchQuery())
->setViewer($user)
->withPHIDs(array($branch_phid))
->executeOne();
if (!$releeph_branch) {
throw id(new ConduitException('ERR_BRANCH'))->setErrorDescription(
pht(
'No %s found with PHID %s!',
'ReleephBranch',
$branch_phid));
}
$releeph_project = $releeph_branch->getProduct();
// Find the requested commit identifiers
$requested_commits = array();
$requested_object_phids = array();
$things = $request->getValue('things');
$finder = id(new ReleephCommitFinder())
->setUser($user)
->setReleephProject($releeph_project);
foreach ($things as $thing) {
try {
$requested_commits[$thing] = $finder->fromPartial($thing);
$object_phid = $finder->getRequestedObjectPHID();
if (!$object_phid) {
$object_phid = $requested_commits[$thing]->getPHID();
}
$requested_object_phids[$thing] = $object_phid;
} catch (ReleephCommitFinderException $ex) {
throw id(new ConduitException('ERR_NO_MATCHES'))
->setErrorDescription($ex->getMessage());
}
}
$requested_commit_phids = mpull($requested_commits, 'getPHID');
// Find any existing requests that clash on the commit id, for this branch
$existing_releeph_requests = id(new ReleephRequest())->loadAllWhere(
'requestCommitPHID IN (%Ls) AND branchID = %d',
$requested_commit_phids,
$releeph_branch->getID());
$existing_releeph_requests = mpull(
$existing_releeph_requests,
null,
'getRequestCommitPHID');
$selector = $releeph_project->getReleephFieldSelector();
$fields = $selector->getFieldSpecifications();
foreach ($fields as $field) {
$field
->setReleephProject($releeph_project)
->setReleephBranch($releeph_branch);
}
$results = array();
$handles = id(new PhabricatorHandleQuery())
->setViewer($user)
->withPHIDs($requested_commit_phids)
->execute();
foreach ($requested_commits as $thing => $commit) {
$phid = $commit->getPHID();
$name = id($handles[$phid])->getName();
$releeph_request = null;
$existing_releeph_request = idx($existing_releeph_requests, $phid);
if ($existing_releeph_request) {
$releeph_request = $existing_releeph_request;
} else {
$releeph_request = id(new ReleephRequest())
->setRequestUserPHID($user->getPHID())
->setBranchID($releeph_branch->getID())
->setInBranch(0)
->setRequestedObjectPHID($requested_object_phids[$thing]);
$xactions = array();
$xactions[] = id(new ReleephRequestTransaction())
->setTransactionType(ReleephRequestTransaction::TYPE_REQUEST)
->setNewValue($commit->getPHID());
$xactions[] = id(new ReleephRequestTransaction())
->setTransactionType(ReleephRequestTransaction::TYPE_USER_INTENT)
->setMetadataValue('userPHID', $user->getPHID())
->setMetadataValue(
'isAuthoritative',
$releeph_project->isAuthoritative($user))
->setNewValue(ReleephRequest::INTENT_WANT);
foreach ($fields as $field) {
if (!$field->isEditable()) {
continue;
}
$field->setReleephRequest($releeph_request);
try {
$field->setValueFromConduitAPIRequest($request);
} catch (ReleephFieldParseException $ex) {
throw id(new ConduitException('ERR_FIELD_PARSE'))
->setErrorDescription($ex->getMessage());
}
}
$editor = id(new ReleephRequestTransactionalEditor())
->setActor($user)
->setContinueOnNoEffect(true)
->setContentSource($request->newContentSource());
$editor->applyTransactions($releeph_request, $xactions);
}
$url = PhabricatorEnv::getProductionURI('/Y'.$releeph_request->getID());
$results[$thing] = array(
'thing' => $thing,
'branch' => $releeph_branch->getDisplayNameWithDetail(),
'commitName' => $name,
'commitID' => $commit->getCommitIdentifier(),
'url' => $url,
'requestID' => $releeph_request->getID(),
'requestor' => $viewer_handle->getName(),
'requestTime' => $releeph_request->getDateCreated(),
'existing' => $existing_releeph_request !== null,
);
}
return $results;
}
}

View file

@ -1,34 +0,0 @@
<?php
final class ReleephWorkCanPushConduitAPIMethod extends ReleephConduitAPIMethod {
public function getAPIMethodName() {
return 'releephwork.canpush';
}
public function getMethodStatus() {
return self::METHOD_STATUS_UNSTABLE;
}
public function getMethodDescription() {
return pht('Return whether the conduit user is allowed to push.');
}
protected function defineParamTypes() {
return array(
'projectPHID' => 'required string',
);
}
protected function defineReturnType() {
return 'bool';
}
protected function execute(ConduitAPIRequest $request) {
$releeph_project = id(new ReleephProject())
->loadOneWhere('phid = %s', $request->getValue('projectPHID'));
$user = $request->getUser();
return $releeph_project->isAuthoritative($user);
}
}

View file

@ -1,44 +0,0 @@
<?php
final class ReleephWorkGetAuthorInfoConduitAPIMethod
extends ReleephConduitAPIMethod {
public function getAPIMethodName() {
return 'releephwork.getauthorinfo';
}
public function getMethodStatus() {
return self::METHOD_STATUS_UNSTABLE;
}
public function getMethodDescription() {
return pht('Return a string to use as the VCS author.');
}
protected function defineParamTypes() {
return array(
'userPHID' => 'required string',
'vcsType' => 'required string',
);
}
protected function defineReturnType() {
return 'nonempty string';
}
protected function execute(ConduitAPIRequest $request) {
$user = id(new PhabricatorUser())
->loadOneWhere('phid = %s', $request->getValue('userPHID'));
$email = $user->loadPrimaryEmailAddress();
if (is_numeric($email)) {
$email = $user->getUserName().'@fb.com';
}
return sprintf(
'%s <%s>',
$user->getRealName(),
$email);
}
}

View file

@ -1,104 +0,0 @@
<?php
final class ReleephWorkGetBranchCommitMessageConduitAPIMethod
extends ReleephConduitAPIMethod {
public function getAPIMethodName() {
return 'releephwork.getbranchcommitmessage';
}
public function getMethodStatus() {
return self::METHOD_STATUS_UNSTABLE;
}
public function getMethodDescription() {
return pht('Get a commit message for committing a Releeph branch.');
}
protected function defineParamTypes() {
return array(
'branchPHID' => 'required string',
);
}
protected function defineReturnType() {
return 'nonempty string';
}
protected function execute(ConduitAPIRequest $request) {
$viewer = $request->getUser();
$branch = id(new ReleephBranchQuery())
->setViewer($viewer)
->withPHIDs(array($request->getValue('branchPHID')))
->executeOne();
$project = $branch->getProduct();
$creator_phid = $branch->getCreatedByUserPHID();
$cut_phid = $branch->getCutPointCommitPHID();
$phids = array(
$branch->getPHID(),
$project->getPHID(),
$creator_phid,
$cut_phid,
);
$handles = id(new PhabricatorHandleQuery())
->setViewer($request->getUser())
->withPHIDs($phids)
->execute();
$h_branch = $handles[$branch->getPHID()];
$h_project = $handles[$project->getPHID()];
// Not as customizable as a ReleephRequest's commit message. It doesn't
// really need to be.
// TODO: Yes it does, see FB-specific stuff below.
$commit_message = array();
$commit_message[] = $h_branch->getFullName();
$commit_message[] = $h_branch->getURI();
$commit_message[] = pht('Cut Point: %s', $handles[$cut_phid]->getName());
$cut_point_pr_commit = id(new PhabricatorRepositoryCommit())
->loadOneWhere('phid = %s', $cut_phid);
$cut_point_commit_date = strftime(
'%Y-%m-%d %H:%M:%S%z',
$cut_point_pr_commit->getEpoch());
$commit_message[] = pht('Cut Point Date: %s', $cut_point_commit_date);
$commit_message[] = pht(
'Created By: %s',
$handles[$creator_phid]->getName());
$project_uri = $project->getURI();
$commit_message[] = pht(
'Project: %s',
$h_project->getName().' '.$project_uri);
/**
* Required for 090-limit_new_branch_creations.sh in
* admin/scripts/git/hosting/hooks/update.d (in the E repo):
*
* http://fburl.com/2372545
*
* The commit message must have a line saying:
*
* @new-branch: <branch-name>
*
*/
$repo = $project->getRepository();
switch ($repo->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$commit_message[] = sprintf(
'@new-branch: %s',
$branch->getName());
break;
}
return implode("\n\n", $commit_message);
}
}

View file

@ -1,57 +0,0 @@
<?php
final class ReleephWorkGetBranchConduitAPIMethod
extends ReleephConduitAPIMethod {
public function getAPIMethodName() {
return 'releephwork.getbranch';
}
public function getMethodStatus() {
return self::METHOD_STATUS_UNSTABLE;
}
public function getMethodDescription() {
return pht('Return information to help checkout / cut a Releeph branch.');
}
protected function defineParamTypes() {
return array(
'branchPHID' => 'required string',
);
}
protected function defineReturnType() {
return 'dict<string, wild>';
}
protected function execute(ConduitAPIRequest $request) {
$branch = id(new ReleephBranchQuery())
->setViewer($request->getUser())
->withPHIDs(array($request->getValue('branchPHID')))
->needCutPointCommits(true)
->executeOne();
$cut_phid = $branch->getCutPointCommitPHID();
$phids = array($cut_phid);
$handles = id(new PhabricatorHandleQuery())
->setViewer($request->getUser())
->withPHIDs($phids)
->execute();
$project = $branch->getProject();
$repo = $project->getRepository();
$commit = $branch->getCutPointCommit();
return array(
'branchName' => $branch->getName(),
'branchPHID' => $branch->getPHID(),
'vcsType' => $repo->getVersionControlSystem(),
'cutCommitID' => $commit->getCommitIdentifier(),
'cutCommitName' => $handles[$cut_phid]->getName(),
'creatorPHID' => $branch->getCreatedByUserPHID(),
'trunk' => $project->getTrunkBranch(),
);
}
}

View file

@ -1,96 +0,0 @@
<?php
final class ReleephWorkGetCommitMessageConduitAPIMethod
extends ReleephConduitAPIMethod {
public function getAPIMethodName() {
return 'releephwork.getcommitmessage';
}
public function getMethodStatus() {
return self::METHOD_STATUS_UNSTABLE;
}
public function getMethodDescription() {
return pht(
'Get commit message components for building a %s commit message.',
'ReleephRequest');
}
protected function defineParamTypes() {
$action_const = $this->formatStringConstants(array('pick', 'revert'));
return array(
'requestPHID' => 'required string',
'action' => 'required '.$action_const,
);
}
protected function defineReturnType() {
return 'dict<string, string>';
}
protected function execute(ConduitAPIRequest $request) {
$viewer = $request->getUser();
$releeph_request = id(new ReleephRequestQuery())
->setViewer($viewer)
->withPHIDs(array($request->getValue('requestPHID')))
->executeOne();
$action = $request->getValue('action');
$title = $releeph_request->getSummaryForDisplay();
$commit_message = array();
$branch = $releeph_request->getBranch();
$project = $branch->getProduct();
$selector = $project->getReleephFieldSelector();
$fields = $selector->getFieldSpecifications();
$fields = $selector->sortFieldsForCommitMessage($fields);
foreach ($fields as $field) {
$field
->setUser($request->getUser())
->setReleephProject($project)
->setReleephBranch($branch)
->setReleephRequest($releeph_request);
$label = null;
$value = null;
switch ($action) {
case 'pick':
if ($field->shouldAppearOnCommitMessage()) {
$label = $field->renderLabelForCommitMessage();
$value = $field->renderValueForCommitMessage();
}
break;
case 'revert':
if ($field->shouldAppearOnRevertMessage()) {
$label = $field->renderLabelForRevertMessage();
$value = $field->renderValueForRevertMessage();
}
break;
}
if ($label && $value) {
if (strpos($value, "\n") !== false ||
substr($value, 0, 2) === ' ') {
$commit_message[] = "{$label}:\n{$value}";
} else {
$commit_message[] = "{$label}: {$value}";
}
}
}
return array(
'title' => $title,
'body' => implode("\n\n", $commit_message),
);
}
}

View file

@ -1,228 +0,0 @@
<?php
final class ReleephWorkNextRequestConduitAPIMethod
extends ReleephConduitAPIMethod {
private $project;
private $branch;
public function getAPIMethodName() {
return 'releephwork.nextrequest';
}
public function getMethodStatus() {
return self::METHOD_STATUS_UNSTABLE;
}
public function getMethodDescription() {
return pht(
'Return info required to cut a branch, and pick and revert %s.',
'ReleephRequests');
}
protected function defineParamTypes() {
return array(
'branchPHID' => 'required phid',
'seen' => 'required map<string, bool>',
);
}
protected function defineReturnType() {
return '';
}
protected function defineErrorTypes() {
return array(
'ERR-NOT-PUSHER' => pht(
'You are not listed as a pusher for the Releeph project!'),
);
}
protected function execute(ConduitAPIRequest $request) {
$viewer = $request->getUser();
$seen = $request->getValue('seen');
$branch = id(new ReleephBranchQuery())
->setViewer($viewer)
->withPHIDs(array($request->getValue('branchPHID')))
->executeOne();
$project = $branch->getProduct();
$needs_pick = array();
$needs_revert = array();
// Load every request ever made for this branch...?!!!
$releeph_requests = id(new ReleephRequestQuery())
->setViewer($viewer)
->withBranchIDs(array($branch->getID()))
->execute();
foreach ($releeph_requests as $candidate) {
$phid = $candidate->getPHID();
if (idx($seen, $phid)) {
continue;
}
$should = $candidate->shouldBeInBranch();
$in = $candidate->getInBranch();
if ($should && !$in) {
$needs_pick[] = $candidate;
}
if (!$should && $in) {
$needs_revert[] = $candidate;
}
}
/**
* Sort both needs_pick and needs_revert in ascending commit order, as
* discovered by Phabricator (using the `id` column to perform that
* ordering).
*
* This is easy for $needs_pick as the ordinal is stored. It is hard for
* reverts, as we have to look that information up.
*/
$needs_pick = $this->sortPicks($needs_pick);
$needs_revert = $this->sortReverts($needs_revert);
/**
* Do reverts first in reverse order, then the picks in original-commit
* order.
*
* This seems like the correct thing to do, but there may be a better
* algorithm for the releephwork.nextrequest Conduit call that orders
* things better.
*
* We could also button-mash our way through everything that failed (at the
* end of the run) to try failed things again.
*/
$releeph_request = null;
$action = null;
if ($needs_revert) {
$releeph_request = last($needs_revert);
$action = 'revert';
$commit_id = $releeph_request->getCommitIdentifier();
$commit_phid = $releeph_request->getCommitPHID();
} else if ($needs_pick) {
$releeph_request = head($needs_pick);
$action = 'pick';
$commit = $releeph_request->loadPhabricatorRepositoryCommit();
$commit_id = $commit->getCommitIdentifier();
$commit_phid = $commit->getPHID();
} else {
// Return early if there's nothing to do!
return array();
}
// Build the response
$phids = array();
$phids[] = $commit_phid;
$diff_phid = null;
$diff_rev_id = null;
$requested_object = $releeph_request->getRequestedObject();
if ($requested_object instanceof DifferentialRevision) {
$diff_rev = $requested_object;
} else {
$diff_rev = null;
}
if ($diff_rev) {
$diff_phid = $diff_rev->getPHID();
$phids[] = $diff_phid;
$diff_rev_id = $diff_rev->getID();
}
$phids[] = $releeph_request->getPHID();
$handles = id(new PhabricatorHandleQuery())
->setViewer($request->getUser())
->withPHIDs($phids)
->execute();
$diff_name = null;
if ($diff_rev) {
$diff_name = $handles[$diff_phid]->getName();
}
$new_author_phid = null;
if ($diff_rev) {
$new_author_phid = $diff_rev->getAuthorPHID();
} else {
$pr_commit = $releeph_request->loadPhabricatorRepositoryCommit();
if ($pr_commit) {
$new_author_phid = $pr_commit->getAuthorPHID();
}
}
return array(
'requestID' => $releeph_request->getID(),
'requestPHID' => $releeph_request->getPHID(),
'requestName' => $handles[$releeph_request->getPHID()]->getName(),
'requestorPHID' => $releeph_request->getRequestUserPHID(),
'action' => $action,
'diffRevID' => $diff_rev_id,
'diffName' => $diff_name,
'commitIdentifier' => $commit_id,
'commitPHID' => $commit_phid,
'commitName' => $handles[$commit_phid]->getName(),
'needsRevert' => mpull($needs_revert, 'getID'),
'needsPick' => mpull($needs_pick, 'getID'),
'newAuthorPHID' => $new_author_phid,
);
}
private function sortPicks(array $releeph_requests) {
$surrogate = array();
foreach ($releeph_requests as $rq) {
// TODO: it's likely that relying on the `id` column to provide
// trunk-commit-order is thoroughly broken.
$ordinal = (int)$rq->loadPhabricatorRepositoryCommit()->getID();
$surrogate[$ordinal] = $rq;
}
ksort($surrogate);
return $surrogate;
}
/**
* Sort an array of ReleephRequests, that have been picked into a branch, in
* the order in which they were picked to the branch.
*/
private function sortReverts(array $releeph_requests) {
if (!$releeph_requests) {
return array();
}
// ReleephRequests, keyed by <branch-commit-id>
$releeph_requests = mpull($releeph_requests, null, 'getCommitIdentifier');
$commits = id(new PhabricatorRepositoryCommit())
->loadAllWhere(
'commitIdentifier IN (%Ls)',
mpull($releeph_requests, 'getCommitIdentifier'));
// A map of <branch-commit-id> => <branch-commit-ordinal>
$surrogate = mpull($commits, 'getID', 'getCommitIdentifier');
$unparsed = array();
$result = array();
foreach ($releeph_requests as $commit_id => $releeph_request) {
$ordinal = idx($surrogate, $commit_id);
if ($ordinal) {
$result[$ordinal] = $releeph_request;
} else {
$unparsed[] = $releeph_request;
}
}
// Sort $result in ascending order
ksort($result);
// Unparsed commits we'll just have to guess, based on time
$unparsed = msort($unparsed, 'getDateModified');
return array_merge($result, $unparsed);
}
}

View file

@ -1,73 +0,0 @@
<?php
final class ReleephWorkRecordConduitAPIMethod
extends ReleephConduitAPIMethod {
public function getAPIMethodName() {
return 'releephwork.record';
}
public function getMethodStatus() {
return self::METHOD_STATUS_UNSTABLE;
}
/**
* Record that a request was committed locally, and is about to be pushed to
* the remote repository.
*
* This lets us mark a ReleephRequest as being in a branch in real time so
* that no one else tries to pick it.
*
* When the daemons discover this commit in the repository with
* DifferentialReleephRequestFieldSpecification, we'll be able to record the
* commit's PHID as well. That process is slow though, and we don't want to
* wait a whole minute before marking something as cleanly picked or
* reverted.
*/
public function getMethodDescription() {
return pht(
'Record whether we committed a pick or revert '.
'to the upstream repository.');
}
protected function defineParamTypes() {
$action_const = $this->formatStringConstants(
array(
'pick',
'revert',
));
return array(
'requestPHID' => 'required string',
'action' => 'required '.$action_const,
'commitIdentifier' => 'required string',
);
}
protected function defineReturnType() {
return 'void';
}
protected function execute(ConduitAPIRequest $request) {
$action = $request->getValue('action');
$new_commit_id = $request->getValue('commitIdentifier');
$releeph_request = id(new ReleephRequest())
->loadOneWhere('phid = %s', $request->getValue('requestPHID'));
$xactions = array();
$xactions[] = id(new ReleephRequestTransaction())
->setTransactionType(ReleephRequestTransaction::TYPE_COMMIT)
->setMetadataValue('action', $action)
->setNewValue($new_commit_id);
$editor = id(new ReleephRequestTransactionalEditor())
->setActor($request->getUser())
->setContinueOnNoEffect(true)
->setContentSource($request->newContentSource());
$editor->applyTransactions($releeph_request, $xactions);
}
}

View file

@ -1,80 +0,0 @@
<?php
final class ReleephWorkRecordPickStatusConduitAPIMethod
extends ReleephConduitAPIMethod {
public function getAPIMethodName() {
return 'releephwork.recordpickstatus';
}
public function getMethodStatus() {
return self::METHOD_STATUS_UNSTABLE;
}
public function getMethodDescription() {
return pht('Record whether a pick or revert was successful or not.');
}
protected function defineParamTypes() {
$action_const = $this->formatStringConstants(
array(
'pick',
'revert',
));
return array(
'requestPHID' => 'required string',
'action' => 'required '.$action_const,
'ok' => 'required bool',
'dryRun' => 'optional bool',
'details' => 'optional dict<string, wild>',
);
}
protected function defineReturnType() {
return '';
}
protected function execute(ConduitAPIRequest $request) {
$action = $request->getValue('action');
$ok = $request->getValue('ok');
$dry_run = $request->getValue('dryRun');
$details = $request->getValue('details', array());
switch ($request->getValue('action')) {
case 'pick':
$pick_status = $ok
? ReleephRequest::PICK_OK
: ReleephRequest::PICK_FAILED;
break;
case 'revert':
$pick_status = $ok
? ReleephRequest::REVERT_OK
: ReleephRequest::REVERT_FAILED;
break;
default:
throw new Exception(pht('Unknown action %s!', $action));
}
$releeph_request = id(new ReleephRequest())
->loadOneWhere('phid = %s', $request->getValue('requestPHID'));
$editor = id(new ReleephRequestTransactionalEditor())
->setActor($request->getUser())
->setContinueOnNoEffect(true)
->setContentSource($request->newContentSource());
$xactions = array();
$xactions[] = id(new ReleephRequestTransaction())
->setTransactionType(ReleephRequestTransaction::TYPE_PICK_STATUS)
->setMetadataValue('dryRun', $dry_run)
->setMetadataValue('details', $details)
->setNewValue($pick_status);
$editor->applyTransactions($releeph_request, $xactions);
}
}

View file

@ -1,67 +0,0 @@
<?php
final class PhabricatorReleephApplicationConfigOptions
extends PhabricatorApplicationConfigOptions {
public function getName() {
return pht('Releeph');
}
public function getDescription() {
return pht('Options for configuring Releeph, the release branch tool.');
}
public function getIcon() {
return 'fa-flag-checkered';
}
public function getGroup() {
return 'apps';
}
public function getOptions() {
$default_fields = array(
new ReleephSummaryFieldSpecification(),
new ReleephRequestorFieldSpecification(),
new ReleephSeverityFieldSpecification(),
new ReleephIntentFieldSpecification(),
new ReleephReasonFieldSpecification(),
new ReleephAuthorFieldSpecification(),
new ReleephRevisionFieldSpecification(),
new ReleephOriginalCommitFieldSpecification(),
new ReleephBranchCommitFieldSpecification(),
new ReleephDiffSizeFieldSpecification(),
new ReleephDiffChurnFieldSpecification(),
new ReleephDiffMessageFieldSpecification(),
new ReleephCommitMessageFieldSpecification(),
);
$default = array();
foreach ($default_fields as $default_field) {
$default[$default_field->getFieldKey()] = true;
}
foreach ($default as $key => $enabled) {
$default[$key] = array(
'disabled' => !$enabled,
);
}
$custom_field_type = 'custom:PhabricatorCustomFieldConfigOptionType';
return array(
$this->newOption('releeph.fields', $custom_field_type, $default)
->setCustomData('ReleephFieldSpecification'),
$this->newOption(
'releeph.default-branch-template',
'string',
'releases/%P/%p-%Y%m%d-%v')
->setDescription(
pht(
'The default branch template for new branches in unconfigured '.
'Releeph projects. This is also configurable on a per-project '.
'basis.')),
);
}
}

View file

@ -1,32 +0,0 @@
<?php
final class ReleephRequestStatus extends Phobject {
const STATUS_REQUESTED = 1;
const STATUS_NEEDS_PICK = 2; // aka approved
const STATUS_REJECTED = 3;
const STATUS_ABANDONED = 4;
const STATUS_PICKED = 5;
const STATUS_REVERTED = 6;
const STATUS_NEEDS_REVERT = 7; // aka revert requested
public static function getStatusDescriptionFor($status) {
$descriptions = array(
self::STATUS_REQUESTED => pht('Requested'),
self::STATUS_REJECTED => pht('Rejected'),
self::STATUS_ABANDONED => pht('Abandoned'),
self::STATUS_PICKED => pht('Pulled'),
self::STATUS_REVERTED => pht('Reverted'),
self::STATUS_NEEDS_PICK => pht('Needs Pull'),
self::STATUS_NEEDS_REVERT => pht('Needs Revert'),
);
return idx($descriptions, $status, '??');
}
public static function getStatusClassSuffixFor($status) {
$description = self::getStatusDescriptionFor($status);
$class = str_replace(' ', '-', strtolower($description));
return $class;
}
}

View file

@ -1,37 +0,0 @@
<?php
abstract class ReleephController extends PhabricatorController {
public function buildSideNavView($for_app = false) {
$user = $this->getRequest()->getUser();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
if ($for_app) {
$nav->addFilter('project/create/', pht('Create Product'));
}
id(new ReleephProductSearchEngine())
->setViewer($user)
->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
}
public function buildApplicationMenu() {
return $this->buildSideNavView(true)->getMenu();
}
protected function getProductViewURI(ReleephProject $product) {
return $this->getApplicationURI('project/'.$product->getID().'/');
}
protected function getBranchViewURI(ReleephBranch $branch) {
return $this->getApplicationURI('branch/'.$branch->getID().'/');
}
}

View file

@ -1,73 +0,0 @@
<?php
final class ReleephBranchAccessController extends ReleephBranchController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$action = $request->getURIData('action');
$id = $request->getURIData('branchID');
$branch = id(new ReleephBranchQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$branch) {
return new Aphront404Response();
}
$this->setBranch($branch);
switch ($action) {
case 'close':
case 're-open':
break;
default:
return new Aphront404Response();
}
$branch_uri = $this->getBranchViewURI($branch);
if ($request->isFormPost()) {
if ($action == 're-open') {
$is_active = 1;
} else {
$is_active = 0;
}
id(new ReleephBranchEditor())
->setActor($request->getUser())
->setReleephBranch($branch)
->changeBranchAccess($is_active);
return id(new AphrontReloadResponse())->setURI($branch_uri);
}
if ($action == 'close') {
$title_text = pht('Really Close Branch?');
$short = pht('Close Branch');
$body_text = pht(
'Really close the branch "%s"?',
phutil_tag('strong', array(), $branch->getBasename()));
$button_text = pht('Close Branch');
} else {
$title_text = pht('Really Reopen Branch?');
$short = pht('Reopen Branch');
$body_text = pht(
'Really reopen the branch "%s"?',
phutil_tag('strong', array(), $branch->getBasename()));
$button_text = pht('Reopen Branch');
}
return $this->newDialog()
->setTitle($title_text)
->setShortTitle($short)
->appendChild($body_text)
->addSubmitButton($button_text)
->addCancelButton($branch_uri);
}
}

View file

@ -1,35 +0,0 @@
<?php
abstract class ReleephBranchController extends ReleephController {
private $branch;
public function setBranch($branch) {
$this->branch = $branch;
return $this;
}
public function getBranch() {
return $this->branch;
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$branch = $this->getBranch();
if ($branch) {
$product = $branch->getProduct();
$crumbs->addTextCrumb(
$product->getName(),
$this->getProductViewURI($product));
$crumbs->addTextCrumb(
$branch->getName(),
$this->getBranchViewURI($branch));
}
return $crumbs;
}
}

View file

@ -1,133 +0,0 @@
<?php
final class ReleephBranchCreateController extends ReleephProductController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('projectID');
$product = id(new ReleephProductQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$product) {
return new Aphront404Response();
}
$this->setProduct($product);
$cut_point = $request->getStr('cutPoint');
$symbolic_name = $request->getStr('symbolicName');
if (!$cut_point) {
$repository = $product->getRepository();
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$cut_point = $product->getTrunkBranch();
break;
}
}
$e_cut = true;
$errors = array();
$branch_date_control = id(new AphrontFormDateControl())
->setUser($request->getUser())
->setName('templateDate')
->setLabel(pht('Date'))
->setCaption(pht('The date used for filling out the branch template.'))
->setInitialTime(AphrontFormDateControl::TIME_START_OF_DAY);
$branch_date = $branch_date_control->readValueFromRequest($request);
if ($request->isFormPost()) {
$cut_commit = null;
if (!$cut_point) {
$e_cut = pht('Required');
$errors[] = pht('You must give a branch cut point');
} else {
try {
$finder = id(new ReleephCommitFinder())
->setUser($request->getUser())
->setReleephProject($product);
$cut_commit = $finder->fromPartial($cut_point);
} catch (Exception $e) {
$e_cut = pht('Invalid');
$errors[] = $e->getMessage();
}
}
if (!$errors) {
$branch = id(new ReleephBranchEditor())
->setReleephProject($product)
->setActor($request->getUser())
->newBranchFromCommit(
$cut_commit,
$branch_date,
$symbolic_name);
$branch_uri = $this->getApplicationURI('branch/'.$branch->getID());
return id(new AphrontRedirectResponse())
->setURI($branch_uri);
}
}
$product_uri = $this->getProductViewURI($product);
$form = id(new AphrontFormView())
->setUser($request->getUser())
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Symbolic Name'))
->setName('symbolicName')
->setValue($symbolic_name)
->setCaption(pht(
'Mutable alternate name, for easy reference, (e.g. "LATEST")')))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Cut point'))
->setName('cutPoint')
->setValue($cut_point)
->setError($e_cut)
->setCaption(pht(
'A commit ID for your repo type, or a Diffusion ID like "rE123"')))
->appendChild($branch_date_control)
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Cut Branch'))
->addCancelButton($product_uri));
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Branch'))
->setFormErrors($errors)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($form);
$title = pht('New Branch');
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($title);
$crumbs->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-plus-square');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter($box);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
}

View file

@ -1,114 +0,0 @@
<?php
final class ReleephBranchEditController extends ReleephBranchController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('branchID');
$branch = id(new ReleephBranchQuery())
->setViewer($viewer)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->withIDs(array($id))
->executeOne();
if (!$branch) {
return new Aphront404Response();
}
$this->setBranch($branch);
$symbolic_name = $request->getStr(
'symbolicName',
$branch->getSymbolicName());
if ($request->isFormPost()) {
$existing_with_same_symbolic_name =
id(new ReleephBranch())
->loadOneWhere(
'id != %d AND releephProjectID = %d AND symbolicName = %s',
$branch->getID(),
$branch->getReleephProjectID(),
$symbolic_name);
$branch->openTransaction();
$branch->setSymbolicName($symbolic_name);
if ($existing_with_same_symbolic_name) {
$existing_with_same_symbolic_name
->setSymbolicName(null)
->save();
}
$branch->save();
$branch->saveTransaction();
return id(new AphrontRedirectResponse())
->setURI($this->getBranchViewURI($branch));
}
$phids = array();
$phids[] = $creator_phid = $branch->getCreatedByUserPHID();
$phids[] = $cut_commit_phid = $branch->getCutPointCommitPHID();
$handles = id(new PhabricatorHandleQuery())
->setViewer($request->getUser())
->withPHIDs($phids)
->execute();
$form = id(new AphrontFormView())
->setUser($request->getUser())
->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Branch Name'))
->setValue($branch->getName()))
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Cut Point'))
->setValue($handles[$cut_commit_phid]->renderLink()))
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Created By'))
->setValue($handles[$creator_phid]->renderLink()))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Symbolic Name'))
->setName('symbolicName')
->setValue($symbolic_name)
->setCaption(pht(
'Mutable alternate name, for easy reference, (e.g. "LATEST")')))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($this->getBranchViewURI($branch))
->setValue(pht('Save Branch')));
$title = pht(
'Edit Branch: %s',
$branch->getDisplayNameWithDetail());
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Branch'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($form);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit'));
$crumbs->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader(pht('Edit Branch'))
->setHeaderIcon('fa-pencil');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter($box);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
}

View file

@ -1,41 +0,0 @@
<?php
final class ReleephBranchHistoryController extends ReleephBranchController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('branchID');
$branch = id(new ReleephBranchQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$branch) {
return new Aphront404Response();
}
$this->setBranch($branch);
$timeline = $this->buildTransactionTimeline(
$branch,
new ReleephBranchTransactionQuery());
$timeline
->setShouldTerminate(true);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('History'));
$crumbs->setBorder(true);
$title = pht('Branch History');
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($timeline);
}
}

View file

@ -1,47 +0,0 @@
<?php
final class ReleephBranchNamePreviewController
extends ReleephController {
public function handleRequest(AphrontRequest $request) {
$is_symbolic = $request->getBool('isSymbolic');
$template = $request->getStr('template');
if (!$is_symbolic && !$template) {
$template = ReleephBranchTemplate::getDefaultTemplate();
}
$repository_phid = $request->getInt('repositoryPHID');
$fake_commit_handle =
ReleephBranchTemplate::getFakeCommitHandleFor(
$repository_phid,
$request->getUser());
list($name, $errors) = id(new ReleephBranchTemplate())
->setCommitHandle($fake_commit_handle)
->setReleephProjectName($request->getStr('projectName'))
->setSymbolic($is_symbolic)
->interpolate($template);
$markup = '';
if ($name) {
$markup = phutil_tag(
'div',
array('class' => 'name'),
$name);
}
if ($errors) {
$markup .= phutil_tag(
'div',
array('class' => 'error'),
head($errors));
}
return id(new AphrontAjaxResponse())
->setContent(array('markup' => $markup));
}
}

View file

@ -1,149 +0,0 @@
<?php
final class ReleephBranchViewController extends ReleephBranchController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('branchID');
$querykey = $request->getURIData('queryKey');
$branch = id(new ReleephBranchQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$branch) {
return new Aphront404Response();
}
$this->setBranch($branch);
$controller = id(new PhabricatorApplicationSearchController())
->setPreface($this->renderPreface())
->setQueryKey($querykey)
->setSearchEngine($this->getSearchEngine())
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
}
public function buildSideNavView($for_app = false) {
$user = $this->getRequest()->getUser();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
$this->getSearchEngine()->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
}
private function getSearchEngine() {
$branch = $this->getBranch();
return id(new ReleephRequestSearchEngine())
->setBranch($branch)
->setBaseURI($this->getApplicationURI('branch/'.$branch->getID().'/'))
->setViewer($this->getRequest()->getUser());
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$branch = $this->getBranch();
if ($branch) {
$pull_uri = $this->getApplicationURI('branch/pull/'.$branch->getID().'/');
$crumbs->addAction(
id(new PHUIListItemView())
->setHref($pull_uri)
->setName(pht('New Pull Request'))
->setIcon('fa-plus-square')
->setDisabled(!$branch->isActive()));
}
return $crumbs;
}
private function renderPreface() {
$viewer = $this->getRequest()->getUser();
$branch = $this->getBranch();
$id = $branch->getID();
$header = id(new PHUIHeaderView())
->setHeader($branch->getDisplayName())
->setUser($viewer)
->setPolicyObject($branch);
if ($branch->getIsActive()) {
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
} else {
$header->setStatus('fa-ban', 'dark', pht('Closed'));
}
$actions = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($branch);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$branch,
PhabricatorPolicyCapability::CAN_EDIT);
$edit_uri = $this->getApplicationURI("branch/edit/{$id}/");
$close_uri = $this->getApplicationURI("branch/close/{$id}/");
$reopen_uri = $this->getApplicationURI("branch/re-open/{$id}/");
$history_uri = $this->getApplicationURI("branch/{$id}/history/");
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Branch'))
->setHref($edit_uri)
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
if ($branch->getIsActive()) {
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Close Branch'))
->setHref($close_uri)
->setIcon('fa-times')
->setDisabled(!$can_edit)
->setWorkflow(true));
} else {
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Reopen Branch'))
->setHref($reopen_uri)
->setIcon('fa-plus')
->setUser($viewer)
->setDisabled(!$can_edit)
->setWorkflow(true));
}
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('View History'))
->setHref($history_uri)
->setIcon('fa-list'));
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($branch)
->setActionList($actions);
$properties->addProperty(
pht('Branch'),
$branch->getName());
return id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
}
}

View file

@ -1,85 +0,0 @@
<?php
final class ReleephProductActionController extends ReleephProductController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('projectID');
$action = $request->getURIData('action');
$product = id(new ReleephProductQuery())
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->setViewer($viewer)
->executeOne();
if (!$product) {
return new Aphront404Response();
}
$this->setProduct($product);
$product_id = $product->getID();
$product_uri = $this->getProductViewURI($product);
switch ($action) {
case 'deactivate':
case 'activate':
break;
default:
throw new Aphront404Response();
}
if ($request->isFormPost()) {
$type_active = ReleephProductTransaction::TYPE_ACTIVE;
$xactions = array();
if ($action == 'activate') {
$xactions[] = id(new ReleephProductTransaction())
->setTransactionType($type_active)
->setNewValue(1);
} else {
$xactions[] = id(new ReleephProductTransaction())
->setTransactionType($type_active)
->setNewValue(0);
}
$editor = id(new ReleephProductEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$editor->applyTransactions($product, $xactions);
return id(new AphrontRedirectResponse())->setURI($product_uri);
}
if ($action == 'activate') {
$title = pht('Activate Product?');
$body = pht(
'Reactivate the product %s?',
phutil_tag('strong', array(), $product->getName()));
$submit = pht('Reactivate Product');
$short = pht('Deactivate');
} else {
$title = pht('Really Deactivate Product?');
$body = pht(
'Really deactivate the product %s?',
phutil_tag('strong', array(), $product->getName()));
$submit = pht('Deactivate Product');
$short = pht('Activate');
}
return $this->newDialog()
->setTitle($title)
->setShortTitle($short)
->appendParagraph($body)
->addSubmitButton($submit)
->addCancelButton($product_uri);
}
}

View file

@ -1,30 +0,0 @@
<?php
abstract class ReleephProductController extends ReleephController {
private $product;
protected function setProduct(ReleephProject $product) {
$this->product = $product;
return $this;
}
protected function getProduct() {
return $this->product;
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$product = $this->getProduct();
if ($product) {
$crumbs->addTextCrumb(
$product->getName(),
$this->getProductViewURI($product));
}
return $crumbs;
}
}

View file

@ -1,140 +0,0 @@
<?php
final class ReleephProductCreateController extends ReleephProductController {
public function handleRequest(AphrontRequest $request) {
$name = trim($request->getStr('name'));
$trunk_branch = trim($request->getStr('trunkBranch'));
$repository_phid = $request->getStr('repositoryPHID');
$e_name = true;
$e_trunk_branch = true;
$errors = array();
if ($request->isFormPost()) {
if (!$name) {
$e_name = pht('Required');
$errors[] = pht(
'Your product should have a simple, descriptive name.');
}
if (!$trunk_branch) {
$e_trunk_branch = pht('Required');
$errors[] = pht(
'You must specify which branch you will be picking from.');
}
$pr_repository = id(new PhabricatorRepositoryQuery())
->setViewer($request->getUser())
->withPHIDs(array($repository_phid))
->executeOne();
if (!$errors) {
$releeph_product = id(new ReleephProject())
->setName($name)
->setTrunkBranch($trunk_branch)
->setRepositoryPHID($pr_repository->getPHID())
->setCreatedByUserPHID($request->getUser()->getPHID())
->setIsActive(1);
try {
$releeph_product->save();
return id(new AphrontRedirectResponse())
->setURI($releeph_product->getURI());
} catch (AphrontDuplicateKeyQueryException $ex) {
$e_name = pht('Not Unique');
$errors[] = pht('Another product already uses this name.');
}
}
}
$repo_options = $this->getRepositorySelectOptions();
$product_name_input = id(new AphrontFormTextControl())
->setLabel(pht('Name'))
->setDisableAutocomplete(true)
->setName('name')
->setValue($name)
->setError($e_name)
->setCaption(pht('A name like "Thrift" but not "Thrift releases".'));
$repository_input = id(new AphrontFormSelectControl())
->setLabel(pht('Repository'))
->setName('repositoryPHID')
->setValue($repository_phid)
->setOptions($repo_options);
$branch_name_preview = id(new ReleephBranchPreviewView())
->setLabel(pht('Example Branch'))
->addControl('projectName', $product_name_input)
->addControl('repositoryPHID', $repository_input)
->addStatic('template', '')
->addStatic('isSymbolic', false);
$form = id(new AphrontFormView())
->setUser($request->getUser())
->appendChild($product_name_input)
->appendChild($repository_input)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Trunk'))
->setName('trunkBranch')
->setValue($trunk_branch)
->setError($e_trunk_branch)
->setCaption(pht(
'The development branch, from which requests will be picked.')))
->appendChild($branch_name_preview)
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton('/releeph/project/')
->setValue(pht('Create Release Product')));
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Product'))
->setFormErrors($errors)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form);
$title = pht('Create New Product');
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('New Product'));
$crumbs->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-plus-square');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter($box);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function getRepositorySelectOptions() {
$repos = id(new PhabricatorRepositoryQuery())
->setViewer($this->getRequest()->getUser())
->execute();
$repos = msort($repos, 'getName');
$repos = mpull($repos, null, 'getID');
$choices = array();
foreach ($repos as $repo_id => $repo) {
$repo_name = $repo->getName();
$display = $repo->getDisplayName();
$choices[$repo->getPHID()] = "{$display} ({$repo_name})";
}
asort($choices);
return $choices;
}
}

View file

@ -1,275 +0,0 @@
<?php
final class ReleephProductEditController extends ReleephProductController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('projectID');
$product = id(new ReleephProductQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$product) {
return new Aphront404Response();
}
$this->setProduct($product);
$e_name = true;
$e_trunk_branch = true;
$e_branch_template = false;
$errors = array();
$product_name = $request->getStr('name', $product->getName());
$trunk_branch = $request->getStr('trunkBranch', $product->getTrunkBranch());
$branch_template = $request->getStr('branchTemplate');
if ($branch_template === null) {
$branch_template = $product->getDetail('branchTemplate');
}
$pick_failure_instructions = $request->getStr('pickFailureInstructions',
$product->getDetail('pick_failure_instructions'));
$test_paths = $request->getStr('testPaths');
if ($test_paths !== null) {
$test_paths = array_filter(explode("\n", $test_paths));
} else {
$test_paths = $product->getDetail('testPaths', array());
}
$repository_phid = $product->getRepositoryPHID();
if ($request->isFormPost()) {
$pusher_phids = $request->getArr('pushers');
if (!$product_name) {
$e_name = pht('Required');
$errors[] =
pht('Your Releeph product should have a simple descriptive name.');
}
if (!$trunk_branch) {
$e_trunk_branch = pht('Required');
$errors[] =
pht('You must specify which branch you will be picking from.');
}
$other_releeph_products = id(new ReleephProject())
->loadAllWhere('id != %d', $product->getID());
$other_releeph_product_names = mpull($other_releeph_products,
'getName', 'getID');
if (in_array($product_name, $other_releeph_product_names)) {
$errors[] = pht('Releeph product name %s is already taken',
$product_name);
}
foreach ($test_paths as $test_path) {
$result = @preg_match($test_path, '');
$is_a_valid_regexp = $result !== false;
if (!$is_a_valid_regexp) {
$errors[] = pht('Please provide a valid regular expression: '.
'%s is not valid', $test_path);
}
}
$product
->setName($product_name)
->setTrunkBranch($trunk_branch)
->setDetail('pushers', $pusher_phids)
->setDetail('pick_failure_instructions', $pick_failure_instructions)
->setDetail('branchTemplate', $branch_template)
->setDetail('testPaths', $test_paths);
$fake_commit_handle = ReleephBranchTemplate::getFakeCommitHandleFor(
$repository_phid,
$viewer);
if ($branch_template) {
list($branch_name, $template_errors) = id(new ReleephBranchTemplate())
->setCommitHandle($fake_commit_handle)
->setReleephProjectName($product_name)
->interpolate($branch_template);
if ($template_errors) {
$e_branch_template = pht('Whoopsies!');
foreach ($template_errors as $template_error) {
$errors[] = pht('Template error: %s', $template_error);
}
}
}
if (!$errors) {
$product->save();
return id(new AphrontRedirectResponse())->setURI($product->getURI());
}
}
$pusher_phids = $request->getArr(
'pushers',
$product->getDetail('pushers', array()));
$form = id(new AphrontFormView())
->setUser($request->getUser())
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Name'))
->setName('name')
->setValue($product_name)
->setError($e_name)
->setCaption(pht('A name like "Thrift" but not "Thrift releases".')))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Repository'))
->setValue(
$product->getRepository()->getName()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Repository'))
->setValue(
$product->getRepository()->getName()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Releeph Project PHID'))
->setValue(
$product->getPHID()))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Trunk'))
->setValue($trunk_branch)
->setName('trunkBranch')
->setError($e_trunk_branch))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel(pht('Pick Instructions'))
->setValue($pick_failure_instructions)
->setName('pickFailureInstructions')
->setCaption(
pht('Instructions for pick failures, which will be used '.
'in emails generated by failed picks')))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel(pht('Tests paths'))
->setValue(implode("\n", $test_paths))
->setName('testPaths')
->setCaption(
pht('List of strings that all test files contain in their path '.
'in this project. One string per line. '.
'Examples: \'__tests__\', \'/javatests/\'...')));
$branch_template_input = id(new AphrontFormTextControl())
->setName('branchTemplate')
->setValue($branch_template)
->setLabel(pht('Branch Template'))
->setError($e_branch_template)
->setCaption(
pht("Leave this blank to use your installation's default."));
$branch_template_preview = id(new ReleephBranchPreviewView())
->setLabel(pht('Preview'))
->addControl('template', $branch_template_input)
->addStatic('repositoryPHID', $repository_phid)
->addStatic('isSymbolic', false)
->addStatic('projectName', $product->getName());
$form
->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Pushers'))
->setName('pushers')
->setDatasource(new PhabricatorPeopleDatasource())
->setValue($pusher_phids))
->appendChild($branch_template_input)
->appendChild($branch_template_preview)
->appendRemarkupInstructions($this->getBranchHelpText());
$form
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton('/releeph/product/')
->setValue(pht('Save')));
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Product'))
->setFormErrors($errors)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($form);
$title = pht('Edit Product');
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit Product'));
$crumbs->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-pencil');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter($box);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function getBranchHelpText() {
return <<<EOTEXT
==== Interpolations ====
| Code | Meaning
| ----- | -------
| `%P` | The name of your product, with spaces changed to "-".
| `%p` | Like %P, but all lowercase.
| `%Y` | The four digit year associated with the branch date.
| `%m` | The two digit month.
| `%d` | The two digit day.
| `%v` | The handle of the commit where the branch was cut ("rXYZa4b3c2d1").
| `%V` | The abbreviated commit id where the branch was cut ("a4b3c2d1").
| `%..` | Any other sequence interpreted by `strftime()`.
| `%%` | A literal percent sign.
==== Tips for Branch Templates ====
Use a directory to separate your release branches from other branches:
lang=none
releases/%Y-%M-%d-%v
=> releases/2012-30-16-rHERGE32cd512a52b7
Include a second hierarchy if you share your repository with other products:
lang=none
releases/%P/%p-release-%Y%m%d-%V
=> releases/Tintin/tintin-release-20121116-32cd512a52b7
Keep your branch names simple, avoiding strange punctuation, most of which is
forbidden or escaped anyway:
lang=none, counterexample
releases//..clown-releases..//`date --iso=seconds`-$(sudo halt)
Include the date early in your template, in an order which sorts properly:
lang=none
releases/%Y%m%d-%v
=> releases/20121116-rHERGE32cd512a52b7 (good!)
releases/%V-%m.%d.%Y
=> releases/32cd512a52b7-11.16.2012 (awful!)
EOTEXT;
}
}

View file

@ -1,39 +0,0 @@
<?php
final class ReleephProductHistoryController extends ReleephProductController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('projectID');
$product = id(new ReleephProductQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$product) {
return new Aphront404Response();
}
$this->setProduct($product);
$timeline = $this->buildTransactionTimeline(
$product,
new ReleephProductTransactionQuery());
$timeline->setShouldTerminate(true);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('History'));
$crumbs->setBorder(true);
$title = pht('Product History');
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($timeline);
}
}

View file

@ -1,31 +0,0 @@
<?php
final class ReleephProductListController extends ReleephController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$query_key = $request->getURIData('queryKey');
$controller = id(new PhabricatorApplicationSearchController())
->setQueryKey($query_key)
->setSearchEngine(new ReleephProductSearchEngine())
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$crumbs->addAction(
id(new PHUIListItemView())
->setName(pht('Create Product'))
->setHref($this->getApplicationURI('product/create/'))
->setIcon('fa-plus-square'));
return $crumbs;
}
}

View file

@ -1,153 +0,0 @@
<?php
final class ReleephProductViewController extends ReleephProductController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$id = $request->getURIData('projectID');
$query_key = $request->getURIData('queryKey');
$viewer = $request->getViewer();
$product = id(new ReleephProductQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$product) {
return new Aphront404Response();
}
$this->setProduct($product);
$controller = id(new PhabricatorApplicationSearchController())
->setQueryKey($query_key)
->setPreface($this->renderPreface())
->setSearchEngine(
id(new ReleephBranchSearchEngine())
->setProduct($product))
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
}
public function buildSideNavView($for_app = false) {
$viewer = $this->getRequest()->getUser();
$product = $this->getProduct();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
if ($for_app) {
$nav->addFilter('product/create/', pht('Create Product'));
}
id(new ReleephBranchSearchEngine())
->setProduct($product)
->setViewer($viewer)
->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$product = $this->getProduct();
if ($product) {
$crumbs->addAction(
id(new PHUIListItemView())
->setHref($product->getURI('cutbranch/'))
->setName(pht('Cut New Branch'))
->setIcon('fa-plus'));
}
return $crumbs;
}
private function renderPreface() {
$viewer = $this->getRequest()->getUser();
$product = $this->getProduct();
$id = $product->getID();
$header = id(new PHUIHeaderView())
->setHeader($product->getName())
->setUser($viewer)
->setPolicyObject($product);
if ($product->getIsActive()) {
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
} else {
$header->setStatus('fa-ban', 'dark', pht('Inactive'));
}
$actions = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($product);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$product,
PhabricatorPolicyCapability::CAN_EDIT);
$edit_uri = $this->getApplicationURI("product/{$id}/edit/");
$history_uri = $this->getApplicationURI("product/{$id}/history/");
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Product'))
->setHref($edit_uri)
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
if ($product->getIsActive()) {
$status_name = pht('Deactivate Product');
$status_href = "product/{$id}/action/deactivate/";
$status_icon = 'fa-times';
} else {
$status_name = pht('Reactivate Product');
$status_href = "product/{$id}/action/activate/";
$status_icon = 'fa-plus-circle-o';
}
$actions->addAction(
id(new PhabricatorActionView())
->setName($status_name)
->setHref($this->getApplicationURI($status_href))
->setIcon($status_icon)
->setDisabled(!$can_edit)
->setWorkflow(true));
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('View History'))
->setHref($history_uri)
->setIcon('fa-list'));
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($product);
$properties->addProperty(
pht('Repository'),
$product->getRepository()->getName());
$properties->setActionList($actions);
$pushers = $product->getPushers();
if ($pushers) {
$properties->addProperty(
pht('Pushers'),
$viewer->renderHandleList($pushers));
}
return id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
}
}

View file

@ -1,122 +0,0 @@
<?php
final class ReleephRequestActionController
extends ReleephRequestController {
public function handleRequest(AphrontRequest $request) {
$action = $request->getURIData('action');
$id = $request->getURIData('requestID');
$viewer = $request->getViewer();
$request->validateCSRF();
$pull = id(new ReleephRequestQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$pull) {
return new Aphront404Response();
}
$branch = $pull->getBranch();
$product = $branch->getProduct();
$origin_uri = '/'.$pull->getMonogram();
$editor = id(new ReleephRequestTransactionalEditor())
->setActor($viewer)
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request);
$xactions = array();
switch ($action) {
case 'want':
case 'pass':
static $action_map = array(
'want' => ReleephRequest::INTENT_WANT,
'pass' => ReleephRequest::INTENT_PASS,
);
$intent = $action_map[$action];
$xactions[] = id(new ReleephRequestTransaction())
->setTransactionType(ReleephRequestTransaction::TYPE_USER_INTENT)
->setMetadataValue(
'isAuthoritative',
$product->isAuthoritative($viewer))
->setNewValue($intent);
break;
case 'mark-manually-picked':
case 'mark-manually-reverted':
if (
$pull->getRequestUserPHID() === $viewer->getPHID() ||
$product->isAuthoritative($viewer)) {
// We're all good!
} else {
throw new Exception(
pht(
"Bug! Only pushers or the requestor can manually change a ".
"request's in-branch status!"));
}
if ($action === 'mark-manually-picked') {
$in_branch = 1;
$intent = ReleephRequest::INTENT_WANT;
} else {
$in_branch = 0;
$intent = ReleephRequest::INTENT_PASS;
}
$xactions[] = id(new ReleephRequestTransaction())
->setTransactionType(ReleephRequestTransaction::TYPE_USER_INTENT)
->setMetadataValue('isManual', true)
->setMetadataValue('isAuthoritative', true)
->setNewValue($intent);
$xactions[] = id(new ReleephRequestTransaction())
->setTransactionType(ReleephRequestTransaction::TYPE_MANUAL_IN_BRANCH)
->setNewValue($in_branch);
break;
default:
throw new Exception(
pht('Unknown or unimplemented action %s.', $action));
}
$editor->applyTransactions($pull, $xactions);
if ($request->getBool('render')) {
$field_list = PhabricatorCustomField::getObjectFields(
$pull,
PhabricatorCustomField::ROLE_VIEW);
$field_list
->setViewer($viewer)
->readFieldsFromStorage($pull);
// TODO: This should be more modern and general.
$engine = id(new PhabricatorMarkupEngine())
->setViewer($viewer);
foreach ($field_list->getFields() as $field) {
if ($field->shouldMarkup()) {
$field->setMarkupEngine($engine);
}
}
$engine->process();
$pull_box = id(new ReleephRequestView())
->setUser($viewer)
->setCustomFields($field_list)
->setPullRequest($pull)
->setIsListView(true);
return id(new AphrontAjaxResponse())->setContent(
array(
'markup' => hsprintf('%s', $pull_box),
));
}
return id(new AphrontRedirectResponse())->setURI($origin_uri);
}
}

View file

@ -1,64 +0,0 @@
<?php
final class ReleephRequestCommentController
extends ReleephRequestController {
public function handleRequest(AphrontRequest $request) {
$id = $request->getURIData('requestID');
$viewer = $request->getViewer();
if (!$request->isFormPost()) {
return new Aphront400Response();
}
$pull = id(new ReleephRequestQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$pull) {
return new Aphront404Response();
}
$is_preview = $request->isPreviewRequest();
$draft = PhabricatorDraft::buildFromRequest($request);
$view_uri = $this->getApplicationURI('/'.$pull->getMonogram());
$xactions = array();
$xactions[] = id(new ReleephRequestTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
->attachComment(
id(new ReleephRequestTransactionComment())
->setContent($request->getStr('comment')));
$editor = id(new ReleephRequestTransactionalEditor())
->setActor($viewer)
->setContinueOnNoEffect($request->isContinueRequest())
->setContentSourceFromRequest($request)
->setIsPreview($is_preview);
try {
$xactions = $editor->applyTransactions($pull, $xactions);
} catch (PhabricatorApplicationTransactionNoEffectException $ex) {
return id(new PhabricatorApplicationTransactionNoEffectResponse())
->setCancelURI($view_uri)
->setException($ex);
}
if ($draft) {
$draft->replaceOrDelete();
}
if ($request->isAjax() && $is_preview) {
return id(new PhabricatorApplicationTransactionResponse())
->setObject($pull)
->setViewer($viewer)
->setTransactions($xactions)
->setIsPreview($is_preview);
} else {
return id(new AphrontRedirectResponse())
->setURI($view_uri);
}
}
}

View file

@ -1,3 +0,0 @@
<?php
abstract class ReleephRequestController extends ReleephController {}

View file

@ -1,102 +0,0 @@
<?php
// TODO: After T2222, this is likely unreachable?
final class ReleephRequestDifferentialCreateController
extends ReleephController {
private $revision;
public function handleRequest(AphrontRequest $request) {
$revision_id = $request->getURIData('diffRevID');
$viewer = $request->getViewer();
$diff_rev = id(new DifferentialRevisionQuery())
->setViewer($viewer)
->withIDs(array($revision_id))
->executeOne();
if (!$diff_rev) {
return new Aphront404Response();
}
$this->revision = $diff_rev;
$repository = $this->revision->getRepository();
$projects = id(new ReleephProject())->loadAllWhere(
'repositoryPHID = %s AND isActive = 1',
$repository->getPHID());
if (!$projects) {
throw new Exception(
pht(
"%s belongs to the '%s' repository, ".
"which is not part of any Releeph project!",
'D'.$this->revision->getID(),
$repository->getMonogram()));
}
$branches = id(new ReleephBranch())->loadAllWhere(
'releephProjectID IN (%Ld) AND isActive = 1',
mpull($projects, 'getID'));
if (!$branches) {
throw new Exception(pht(
'%s could be in the Releeph project(s) %s, '.
'but this project / none of these projects have open branches.',
'D'.$this->revision->getID(),
implode(', ', mpull($projects, 'getName'))));
}
if (count($branches) === 1) {
return id(new AphrontRedirectResponse())
->setURI($this->buildReleephRequestURI(head($branches)));
}
$projects = msort(
mpull($projects, null, 'getID'),
'getName');
$branch_groups = mgroup($branches, 'getReleephProjectID');
require_celerity_resource('releeph-request-differential-create-dialog');
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->setTitle(pht('Choose Releeph Branch'))
->setClass('releeph-request-differential-create-dialog')
->addCancelButton('/D'.$request->getStr('D'));
$dialog->appendChild(
pht(
'This differential revision changes code that is associated '.
'with multiple Releeph branches. Please select the branch '.
'where you would like this code to be picked.'));
foreach ($branch_groups as $project_id => $branches) {
$project = idx($projects, $project_id);
$dialog->appendChild(
phutil_tag(
'h1',
array(),
$project->getName()));
$branches = msort($branches, 'getBasename');
foreach ($branches as $branch) {
$uri = $this->buildReleephRequestURI($branch);
$dialog->appendChild(
phutil_tag(
'a',
array(
'href' => $uri,
),
$branch->getDisplayNameWithDetail()));
}
}
return id(new AphrontDialogResponse())
->setDialog($dialog);
}
private function buildReleephRequestURI(ReleephBranch $branch) {
$uri = $branch->getURI('request/');
return id(new PhutilURI($uri))
->replaceQueryParam('D', $this->revision->getID());
}
}

View file

@ -1,320 +0,0 @@
<?php
final class ReleephRequestEditController extends ReleephBranchController {
public function handleRequest(AphrontRequest $request) {
$action = $request->getURIData('action');
$request_id = $request->getURIData('requestID');
$branch_id = $request->getURIData('branchID');
$viewer = $request->getViewer();
if ($request_id) {
$pull = id(new ReleephRequestQuery())
->setViewer($viewer)
->withIDs(array($request_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$pull) {
return new Aphront404Response();
}
$branch = $pull->getBranch();
$is_edit = true;
} else {
$branch = id(new ReleephBranchQuery())
->setViewer($viewer)
->withIDs(array($branch_id))
->executeOne();
if (!$branch) {
return new Aphront404Response();
}
$pull = id(new ReleephRequest())
->setRequestUserPHID($viewer->getPHID())
->setBranchID($branch->getID())
->setInBranch(0)
->attachBranch($branch);
$is_edit = false;
}
$this->setBranch($branch);
$product = $branch->getProduct();
$request_identifier = $request->getStr('requestIdentifierRaw');
$e_request_identifier = true;
// Load all the ReleephFieldSpecifications
$selector = $branch->getProduct()->getReleephFieldSelector();
$fields = $selector->getFieldSpecifications();
foreach ($fields as $field) {
$field
->setReleephProject($product)
->setReleephBranch($branch)
->setReleephRequest($pull);
}
$field_list = PhabricatorCustomField::getObjectFields(
$pull,
PhabricatorCustomField::ROLE_EDIT);
foreach ($field_list->getFields() as $field) {
$field
->setReleephProject($product)
->setReleephBranch($branch)
->setReleephRequest($pull);
}
$field_list->readFieldsFromStorage($pull);
if ($branch_id) {
$cancel_uri = $this->getApplicationURI('branch/'.$branch_id.'/');
} else {
$cancel_uri = '/'.$pull->getMonogram();
}
// Make edits
$errors = array();
if ($request->isFormPost()) {
$xactions = array();
// The commit-identifier being requested...
if (!$is_edit) {
if ($request_identifier ===
ReleephRequestTypeaheadControl::PLACEHOLDER) {
$errors[] = pht('No commit ID was provided.');
$e_request_identifier = pht('Required');
} else {
$pr_commit = null;
$finder = id(new ReleephCommitFinder())
->setUser($viewer)
->setReleephProject($product);
try {
$pr_commit = $finder->fromPartial($request_identifier);
} catch (Exception $e) {
$e_request_identifier = pht('Invalid');
$errors[] = pht(
'Request %s is probably not a valid commit.',
$request_identifier);
$errors[] = $e->getMessage();
}
if (!$errors) {
$object_phid = $finder->getRequestedObjectPHID();
if (!$object_phid) {
$object_phid = $pr_commit->getPHID();
}
$pull->setRequestedObjectPHID($object_phid);
}
}
if (!$errors) {
$existing = id(new ReleephRequest())
->loadOneWhere('requestCommitPHID = %s AND branchID = %d',
$pr_commit->getPHID(), $branch->getID());
if ($existing) {
return id(new AphrontRedirectResponse())
->setURI('/releeph/request/edit/'.$existing->getID().
'?existing=1');
}
$xactions[] = id(new ReleephRequestTransaction())
->setTransactionType(ReleephRequestTransaction::TYPE_REQUEST)
->setNewValue($pr_commit->getPHID());
$xactions[] = id(new ReleephRequestTransaction())
->setTransactionType(ReleephRequestTransaction::TYPE_USER_INTENT)
// To help hide these implicit intents...
->setMetadataValue('isRQCreate', true)
->setMetadataValue('userPHID', $viewer->getPHID())
->setMetadataValue(
'isAuthoritative',
$product->isAuthoritative($viewer))
->setNewValue(ReleephRequest::INTENT_WANT);
}
}
// TODO: This should happen implicitly while building transactions
// instead.
foreach ($field_list->getFields() as $field) {
$field->readValueFromRequest($request);
}
if (!$errors) {
foreach ($fields as $field) {
if ($field->isEditable()) {
try {
$data = $request->getRequestData();
$value = idx($data, $field->getRequiredStorageKey());
$field->validate($value);
$xactions[] = id(new ReleephRequestTransaction())
->setTransactionType(ReleephRequestTransaction::TYPE_EDIT_FIELD)
->setMetadataValue('fieldClass', get_class($field))
->setNewValue($value);
} catch (ReleephFieldParseException $ex) {
$errors[] = $ex->getMessage();
}
}
}
}
if (!$errors) {
$editor = id(new ReleephRequestTransactionalEditor())
->setActor($viewer)
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request);
$editor->applyTransactions($pull, $xactions);
return id(new AphrontRedirectResponse())->setURI($cancel_uri);
}
}
$handle_phids = array(
$pull->getRequestUserPHID(),
$pull->getRequestCommitPHID(),
);
$handle_phids = array_filter($handle_phids);
if ($handle_phids) {
$handles = id(new PhabricatorHandleQuery())
->setViewer($viewer)
->withPHIDs($handle_phids)
->execute();
} else {
$handles = array();
}
$age_string = '';
if ($is_edit) {
$age_string = phutil_format_relative_time(
time() - $pull->getDateCreated()).' ago';
}
// Warn the user if we've been redirected here because we tried to
// re-request something.
$notice_view = null;
if ($request->getInt('existing')) {
$notice_messages = array(
pht('You are editing an existing pick request!'),
pht(
'Requested %s by %s',
$age_string,
$handles[$pull->getRequestUserPHID()]->renderLink()),
);
$notice_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->setErrors($notice_messages);
}
$form = id(new AphrontFormView())
->setUser($viewer);
if ($is_edit) {
$form
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Original Commit'))
->setValue(
$handles[$pull->getRequestCommitPHID()]->renderLink()))
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Requestor'))
->setValue(hsprintf(
'%s %s',
$handles[$pull->getRequestUserPHID()]->renderLink(),
$age_string)));
} else {
$origin = null;
$diff_rev_id = $request->getStr('D');
if ($diff_rev_id) {
$diff_rev = id(new DifferentialRevisionQuery())
->setViewer($viewer)
->withIDs(array($diff_rev_id))
->executeOne();
$origin = '/D'.$diff_rev->getID();
$title = sprintf(
'D%d: %s',
$diff_rev_id,
$diff_rev->getTitle());
$form
->addHiddenInput('requestIdentifierRaw', 'D'.$diff_rev_id)
->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Diff'))
->setValue($title));
} else {
$origin = $branch->getURI();
$repo = $product->getRepository();
$branch_cut_point = id(new PhabricatorRepositoryCommit())
->loadOneWhere(
'phid = %s',
$branch->getCutPointCommitPHID());
$form->appendChild(
id(new ReleephRequestTypeaheadControl())
->setName('requestIdentifierRaw')
->setLabel(pht('Commit ID'))
->setRepo($repo)
->setValue($request_identifier)
->setError($e_request_identifier)
->setStartTime($branch_cut_point->getEpoch())
->setCaption(
pht(
'Start typing to autocomplete on commit title, '.
'or give a Phabricator commit identifier like rFOO1234.')));
}
}
$field_list->appendFieldsToForm($form);
$crumbs = $this->buildApplicationCrumbs();
if ($is_edit) {
$title = pht('Edit Pull Request');
$submit_name = pht('Save');
$header_icon = 'fa-pencil';
$crumbs->addTextCrumb($pull->getMonogram(), '/'.$pull->getMonogram());
$crumbs->addTextCrumb(pht('Edit'));
} else {
$title = pht('Create Pull Request');
$submit_name = pht('Create Pull Request');
$header_icon = 'fa-plus-square';
$crumbs->addTextCrumb(pht('New Pull Request'));
}
$form->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($cancel_uri, pht('Cancel'))
->setValue($submit_name));
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Request'))
->setFormErrors($errors)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($form);
$crumbs->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon($header_icon);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$notice_view,
$box,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
}

View file

@ -1,90 +0,0 @@
<?php
final class ReleephRequestTypeaheadController
extends PhabricatorTypeaheadDatasourceController {
public function handleRequest(AphrontRequest $request) {
$query = $request->getStr('q');
$repo_id = $request->getInt('repo');
$since = $request->getInt('since');
$limit = $request->getInt('limit');
$now = time();
$data = array();
// Dummy instances used for getting connections, table names, etc.
$pr_commit = new PhabricatorRepositoryCommit();
$pr_commit_data = new PhabricatorRepositoryCommitData();
$conn = $pr_commit->establishConnection('r');
$rows = queryfx_all(
$conn,
'SELECT
rc.phid as commitPHID,
rc.authorPHID,
rcd.authorName,
SUBSTRING(rcd.commitMessage, 1, 100) AS shortMessage,
rc.commitIdentifier,
rc.epoch
FROM %T rc
INNER JOIN %T rcd ON rcd.commitID = rc.id
WHERE repositoryID = %d
AND rc.epoch >= %d
AND (
rcd.commitMessage LIKE %~
OR
rc.commitIdentifier LIKE %~
)
ORDER BY rc.epoch DESC
LIMIT %d',
$pr_commit->getTableName(),
$pr_commit_data->getTableName(),
$repo_id,
$since,
$query,
$query,
$limit);
foreach ($rows as $row) {
$full_commit_id = $row['commitIdentifier'];
$short_commit_id = substr($full_commit_id, 0, 12);
$first_line = $this->getFirstLine($row['shortMessage']);
$data[] = array(
$full_commit_id,
$short_commit_id,
$row['authorName'],
phutil_format_relative_time($now - $row['epoch']),
$first_line,
);
}
return id(new AphrontAjaxResponse())
->setContent($data);
}
/**
* Split either at the first new line, or a bunch of dashes.
*
* Really just a legacy from old Releeph Daemon commit messages where I used
* to say:
*
* Commit of FOO for BAR
* ------------
* This does X Y Z
*
*/
private function getFirstLine($commit_message_fragment) {
static $separators = array('-------', "\n");
$string = ltrim($commit_message_fragment);
$first_line = $string;
foreach ($separators as $separator) {
if ($pos = strpos($string, $separator)) {
$first_line = substr($string, 0, $pos);
break;
}
}
return $first_line;
}
}

View file

@ -1,101 +0,0 @@
<?php
final class ReleephRequestViewController
extends ReleephBranchController {
public function handleRequest(AphrontRequest $request) {
$id = $request->getURIData('requestID');
$viewer = $request->getViewer();
$pull = id(new ReleephRequestQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$pull) {
return new Aphront404Response();
}
$this->setBranch($pull->getBranch());
// Redirect older URIs to new "Y" URIs.
// TODO: Get rid of this eventually.
$actual_path = $request->getRequestURI()->getPath();
$expect_path = '/'.$pull->getMonogram();
if ($actual_path != $expect_path) {
return id(new AphrontRedirectResponse())->setURI($expect_path);
}
// TODO: Break this 1:1 stuff?
$branch = $pull->getBranch();
$field_list = PhabricatorCustomField::getObjectFields(
$pull,
PhabricatorCustomField::ROLE_VIEW);
$field_list
->setViewer($viewer)
->readFieldsFromStorage($pull);
// TODO: This should be more modern and general.
$engine = id(new PhabricatorMarkupEngine())
->setViewer($viewer);
foreach ($field_list->getFields() as $field) {
if ($field->shouldMarkup()) {
$field->setMarkupEngine($engine);
}
}
$engine->process();
$pull_box = id(new ReleephRequestView())
->setUser($viewer)
->setCustomFields($field_list)
->setPullRequest($pull);
$timeline = $this->buildTransactionTimeline(
$pull,
new ReleephRequestTransactionQuery());
$add_comment_header = pht('Plea or Yield');
$draft = PhabricatorDraft::newFromUserAndKey(
$viewer,
$pull->getPHID());
$title = hsprintf(
'%s %s',
$pull->getMonogram(),
$pull->getSummaryForDisplay());
$add_comment_form = id(new PhabricatorApplicationTransactionCommentView())
->setUser($viewer)
->setObjectPHID($pull->getPHID())
->setDraft($draft)
->setHeaderText($add_comment_header)
->setAction($this->getApplicationURI(
'/request/comment/'.$pull->getID().'/'))
->setSubmitButtonName(pht('Comment'));
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($pull->getMonogram(), '/'.$pull->getMonogram());
$crumbs->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-flag-checkered');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$pull_box,
$timeline,
$add_comment_form,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
}

View file

@ -1,384 +0,0 @@
<?php
/**
* This DifferentialFieldSpecification exists for two reason:
*
* 1: To parse "Releeph: picks RQ<nn>" headers in commits created by
* arc-releeph so that RQs committed by arc-releeph have real
* PhabricatorRepositoryCommits associated with them (instead of just the SHA
* of the commit, as seen by the pusher).
*
* 2: If requestors want to commit directly to their release branch, they can
* use this header to (i) indicate on a differential revision that this
* differential revision is for the release branch, and (ii) when they land
* their diff on to the release branch manually, the ReleephRequest is
* automatically updated (instead of having to use the "Mark Manually Picked"
* button.)
*
*/
final class DifferentialReleephRequestFieldSpecification extends Phobject {
// TODO: This class is essentially dead right now, see T2222.
const ACTION_PICKS = 'picks';
const ACTION_REVERTS = 'reverts';
private $releephAction;
private $releephPHIDs = array();
public function getStorageKey() {
return 'releeph:actions';
}
public function getValueForStorage() {
return json_encode(array(
'releephAction' => $this->releephAction,
'releephPHIDs' => $this->releephPHIDs,
));
}
public function setValueFromStorage($json) {
if ($json) {
$dict = phutil_json_decode($json);
$this->releephAction = idx($dict, 'releephAction');
$this->releephPHIDs = idx($dict, 'releephPHIDs');
}
return $this;
}
public function shouldAppearOnRevisionView() {
return true;
}
public function renderLabelForRevisionView() {
return pht('Releeph');
}
public function getRequiredHandlePHIDs() {
return mpull($this->loadReleephRequests(), 'getPHID');
}
public function renderValueForRevisionView() {
static $tense;
if ($tense === null) {
$tense = array(
self::ACTION_PICKS => array(
'future' => pht('Will pick'),
'past' => pht('Picked'),
),
self::ACTION_REVERTS => array(
'future' => pht('Will revert'),
'past' => pht('Reverted'),
),
);
}
$releeph_requests = $this->loadReleephRequests();
if (!$releeph_requests) {
return null;
}
if ($this->getRevision()->isClosed()) {
$verb = $tense[$this->releephAction]['past'];
} else {
$verb = $tense[$this->releephAction]['future'];
}
$parts = hsprintf('%s...', $verb);
foreach ($releeph_requests as $releeph_request) {
$parts->appendHTML(phutil_tag('br'));
$parts->appendHTML(
$this->getHandle($releeph_request->getPHID())->renderLink());
}
return $parts;
}
public function shouldAppearOnCommitMessage() {
return true;
}
public function getCommitMessageKey() {
return 'releephActions';
}
public function setValueFromParsedCommitMessage($dict) {
$this->releephAction = $dict['releephAction'];
$this->releephPHIDs = $dict['releephPHIDs'];
return $this;
}
public function renderValueForCommitMessage($is_edit) {
$releeph_requests = $this->loadReleephRequests();
if (!$releeph_requests) {
return null;
}
$parts = array($this->releephAction);
foreach ($releeph_requests as $releeph_request) {
$parts[] = 'RQ'.$releeph_request->getID();
}
return implode(' ', $parts);
}
/**
* Releeph fields should look like:
*
* Releeph: picks RQ1 RQ2, RQ3
* Releeph: reverts RQ1
*/
public function parseValueFromCommitMessage($value) {
/**
* Releeph commit messages look like this (but with more blank lines,
* omitted here):
*
* Make CaptainHaddock more reasonable
* Releeph: picks RQ1
* Requested By: edward
* Approved By: edward (requestor)
* Request Reason: x
* Summary: Make the Haddock implementation more reasonable.
* Test Plan: none
* Reviewers: user1
*
* Some of these fields are recognized by Differential (e.g. "Requested
* By"). They are folded up into the "Releeph" field, parsed by this
* class. As such $value includes more than just the first-line:
*
* "picks RQ1\n\nRequested By: edward\n\nApproved By: edward (requestor)"
*
* To hack around this, just consider the first line of $value when
* determining what Releeph actions the parsed commit is performing.
*/
$first_line = head(array_filter(explode("\n", $value)));
$tokens = preg_split('/\s*,?\s+/', $first_line);
$raw_action = array_shift($tokens);
$action = strtolower($raw_action);
if (!$action) {
return null;
}
switch ($action) {
case self::ACTION_REVERTS:
case self::ACTION_PICKS:
break;
default:
throw new DifferentialFieldParseException(
pht(
"Commit message contains unknown Releeph action '%s'!",
$raw_action));
break;
}
$releeph_requests = array();
foreach ($tokens as $token) {
$match = array();
if (!preg_match('/^(?:RQ)?(\d+)$/i', $token, $match)) {
$label = $this->renderLabelForCommitMessage();
throw new DifferentialFieldParseException(
pht(
"Commit message contains unparseable ".
"Releeph request token '%s'!",
$token));
}
$id = (int)$match[1];
$releeph_request = id(new ReleephRequest())->load($id);
if (!$releeph_request) {
throw new DifferentialFieldParseException(
pht(
'Commit message references non existent Releeph request: %s!',
$value));
}
$releeph_requests[] = $releeph_request;
}
if (count($releeph_requests) > 1) {
$rqs_seen = array();
$groups = array();
foreach ($releeph_requests as $releeph_request) {
$releeph_branch = $releeph_request->getBranch();
$branch_name = $releeph_branch->getName();
$rq_id = 'RQ'.$releeph_request->getID();
if (idx($rqs_seen, $rq_id)) {
throw new DifferentialFieldParseException(
pht(
'Commit message refers to %s multiple times!',
$rq_id));
}
$rqs_seen[$rq_id] = true;
if (!isset($groups[$branch_name])) {
$groups[$branch_name] = array();
}
$groups[$branch_name][] = $rq_id;
}
if (count($groups) > 1) {
$lists = array();
foreach ($groups as $branch_name => $rq_ids) {
$lists[] = implode(', ', $rq_ids).' in '.$branch_name;
}
throw new DifferentialFieldParseException(
pht(
'Commit message references multiple Releeph requests, '.
'but the requests are in different branches: %s',
implode('; ', $lists)));
}
}
$phids = mpull($releeph_requests, 'getPHID');
$data = array(
'releephAction' => $action,
'releephPHIDs' => $phids,
);
return $data;
}
public function renderLabelForCommitMessage() {
return pht('Releeph');
}
public function shouldAppearOnCommitMessageTemplate() {
return false;
}
public function didParseCommit(
PhabricatorRepository $repo,
PhabricatorRepositoryCommit $commit,
PhabricatorRepositoryCommitData $data) {
// NOTE: This is currently dead code. See T2222.
$releeph_requests = $this->loadReleephRequests();
if (!$releeph_requests) {
return;
}
$releeph_branch = head($releeph_requests)->getBranch();
if (!$this->isCommitOnBranch($repo, $commit, $releeph_branch)) {
return;
}
foreach ($releeph_requests as $releeph_request) {
if ($this->releephAction === self::ACTION_PICKS) {
$action = 'pick';
} else {
$action = 'revert';
}
$actor_phid = coalesce(
$data->getCommitDetail('committerPHID'),
$data->getCommitDetail('authorPHID'));
$actor = id(new PhabricatorUser())
->loadOneWhere('phid = %s', $actor_phid);
$xactions = array();
$xactions[] = id(new ReleephRequestTransaction())
->setTransactionType(ReleephRequestTransaction::TYPE_DISCOVERY)
->setMetadataValue('action', $action)
->setMetadataValue('authorPHID',
$data->getCommitDetail('authorPHID'))
->setMetadataValue('committerPHID',
$data->getCommitDetail('committerPHID'))
->setNewValue($commit->getPHID());
$editor = id(new ReleephRequestTransactionalEditor())
->setActor($actor)
->setContinueOnNoEffect(true)
->setContentSource(
PhabricatorContentSource::newForSource(
PhabricatorUnknownContentSource::SOURCECONST));
$editor->applyTransactions($releeph_request, $xactions);
}
}
private function loadReleephRequests() {
if (!$this->releephPHIDs) {
return array();
}
return id(new ReleephRequestQuery())
->setViewer($this->getViewer())
->withPHIDs($this->releephPHIDs)
->execute();
}
private function isCommitOnBranch(
PhabricatorRepository $repo,
PhabricatorRepositoryCommit $commit,
ReleephBranch $releeph_branch) {
switch ($repo->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
list($output) = $repo->execxLocalCommand(
'branch --all --no-color --contains %s',
$commit->getCommitIdentifier());
$remote_prefix = 'remotes/origin/';
$branches = array();
foreach (array_filter(explode("\n", $output)) as $line) {
$tokens = explode(' ', $line);
$ref = last($tokens);
if (strncmp($ref, $remote_prefix, strlen($remote_prefix)) === 0) {
$branch = substr($ref, strlen($remote_prefix));
$branches[$branch] = $branch;
}
}
return idx($branches, $releeph_branch->getName());
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$change_query = DiffusionPathChangeQuery::newFromDiffusionRequest(
DiffusionRequest::newFromDictionary(array(
'user' => $this->getUser(),
'repository' => $repo,
'commit' => $commit->getCommitIdentifier(),
)));
$path_changes = $change_query->loadChanges();
$commit_paths = mpull($path_changes, 'getPath');
$branch_path = $releeph_branch->getName();
$in_branch = array();
$ex_branch = array();
foreach ($commit_paths as $path) {
if (strncmp($path, $branch_path, strlen($branch_path)) === 0) {
$in_branch[] = $path;
} else {
$ex_branch[] = $path;
}
}
if ($in_branch && $ex_branch) {
$error = pht(
'CONFUSION: commit %s in %s contains %d path change(s) that were '.
'part of a Releeph branch, but also has %d path change(s) not '.
'part of a Releeph branch!',
$commit->getCommitIdentifier(),
$repo->getDisplayName(),
count($in_branch),
count($ex_branch));
phlog($error);
}
return !empty($in_branch);
break;
}
}
}

View file

@ -1,86 +0,0 @@
<?php
final class ReleephBranchEditor extends PhabricatorEditor {
private $releephProject;
private $releephBranch;
public function setReleephProject(ReleephProject $rp) {
$this->releephProject = $rp;
return $this;
}
public function setReleephBranch(ReleephBranch $branch) {
$this->releephBranch = $branch;
return $this;
}
public function newBranchFromCommit(
PhabricatorRepositoryCommit $cut_point,
$branch_date,
$symbolic_name = null) {
$template = $this->releephProject->getDetail('branchTemplate');
if (!$template) {
$template = ReleephBranchTemplate::getRequiredDefaultTemplate();
}
$cut_point_handle = id(new PhabricatorHandleQuery())
->setViewer($this->requireActor())
->withPHIDs(array($cut_point->getPHID()))
->executeOne();
list($name, $errors) = id(new ReleephBranchTemplate())
->setCommitHandle($cut_point_handle)
->setBranchDate($branch_date)
->setReleephProjectName($this->releephProject->getName())
->interpolate($template);
$basename = last(explode('/', $name));
$table = id(new ReleephBranch());
$transaction = $table->openTransaction();
$branch = id(new ReleephBranch())
->setName($name)
->setBasename($basename)
->setReleephProjectID($this->releephProject->getID())
->setCreatedByUserPHID($this->requireActor()->getPHID())
->setCutPointCommitPHID($cut_point->getPHID())
->setIsActive(1)
->setDetail('branchDate', $branch_date)
->save();
/**
* Steal the symbolic name from any other branch that has it (in this
* project).
*/
if ($symbolic_name) {
$others = id(new ReleephBranch())->loadAllWhere(
'releephProjectID = %d',
$this->releephProject->getID());
foreach ($others as $other) {
if ($other->getSymbolicName() == $symbolic_name) {
$other
->setSymbolicName(null)
->save();
}
}
$branch
->setSymbolicName($symbolic_name)
->save();
}
$table->saveTransaction();
return $branch;
}
// aka "close" and "reopen"
public function changeBranchAccess($is_active) {
$branch = $this->releephBranch;
$branch
->setIsActive((int)$is_active)
->save();
}
}

View file

@ -1,61 +0,0 @@
<?php
final class ReleephProductEditor
extends PhabricatorApplicationTransactionEditor {
public function getEditorApplicationClass() {
return 'PhabricatorReleephApplication';
}
public function getEditorObjectsDescription() {
return pht('Releeph Products');
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = ReleephProductTransaction::TYPE_ACTIVE;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case ReleephProductTransaction::TYPE_ACTIVE:
return (int)$object->getIsActive();
}
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case ReleephProductTransaction::TYPE_ACTIVE:
return (int)$xaction->getNewValue();
}
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
$new = $xaction->getNewValue();
switch ($xaction->getTransactionType()) {
case ReleephProductTransaction::TYPE_ACTIVE:
$object->setIsActive($new);
break;
}
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
return;
}
}

View file

@ -1,307 +0,0 @@
<?php
final class ReleephRequestTransactionalEditor
extends PhabricatorApplicationTransactionEditor {
public function getEditorApplicationClass() {
return 'PhabricatorReleephApplication';
}
public function getEditorObjectsDescription() {
return pht('Releeph Requests');
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = PhabricatorTransactions::TYPE_COMMENT;
$types[] = ReleephRequestTransaction::TYPE_COMMIT;
$types[] = ReleephRequestTransaction::TYPE_DISCOVERY;
$types[] = ReleephRequestTransaction::TYPE_EDIT_FIELD;
$types[] = ReleephRequestTransaction::TYPE_MANUAL_IN_BRANCH;
$types[] = ReleephRequestTransaction::TYPE_PICK_STATUS;
$types[] = ReleephRequestTransaction::TYPE_REQUEST;
$types[] = ReleephRequestTransaction::TYPE_USER_INTENT;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case ReleephRequestTransaction::TYPE_REQUEST:
return $object->getRequestCommitPHID();
case ReleephRequestTransaction::TYPE_EDIT_FIELD:
$field = newv($xaction->getMetadataValue('fieldClass'), array());
$value = $field->setReleephRequest($object)->getValue();
return $value;
case ReleephRequestTransaction::TYPE_USER_INTENT:
$user_phid = $xaction->getAuthorPHID();
return idx($object->getUserIntents(), $user_phid);
case ReleephRequestTransaction::TYPE_PICK_STATUS:
return (int)$object->getPickStatus();
break;
case ReleephRequestTransaction::TYPE_COMMIT:
return $object->getCommitIdentifier();
case ReleephRequestTransaction::TYPE_DISCOVERY:
return $object->getCommitPHID();
case ReleephRequestTransaction::TYPE_MANUAL_IN_BRANCH:
return $object->getInBranch();
}
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case ReleephRequestTransaction::TYPE_REQUEST:
case ReleephRequestTransaction::TYPE_USER_INTENT:
case ReleephRequestTransaction::TYPE_EDIT_FIELD:
case ReleephRequestTransaction::TYPE_PICK_STATUS:
case ReleephRequestTransaction::TYPE_COMMIT:
case ReleephRequestTransaction::TYPE_DISCOVERY:
case ReleephRequestTransaction::TYPE_MANUAL_IN_BRANCH:
return $xaction->getNewValue();
}
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
$new = $xaction->getNewValue();
switch ($xaction->getTransactionType()) {
case ReleephRequestTransaction::TYPE_REQUEST:
$object->setRequestCommitPHID($new);
break;
case ReleephRequestTransaction::TYPE_USER_INTENT:
$user_phid = $xaction->getAuthorPHID();
$intents = $object->getUserIntents();
$intents[$user_phid] = $new;
$object->setUserIntents($intents);
break;
case ReleephRequestTransaction::TYPE_EDIT_FIELD:
$field = newv($xaction->getMetadataValue('fieldClass'), array());
$field
->setReleephRequest($object)
->setValue($new);
break;
case ReleephRequestTransaction::TYPE_PICK_STATUS:
$object->setPickStatus($new);
break;
case ReleephRequestTransaction::TYPE_COMMIT:
$this->setInBranchFromAction($object, $xaction);
$object->setCommitIdentifier($new);
break;
case ReleephRequestTransaction::TYPE_DISCOVERY:
$this->setInBranchFromAction($object, $xaction);
$object->setCommitPHID($new);
break;
case ReleephRequestTransaction::TYPE_MANUAL_IN_BRANCH:
$object->setInBranch((int)$new);
break;
}
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
return;
}
protected function filterTransactions(
PhabricatorLiskDAO $object,
array $xactions) {
// Remove TYPE_DISCOVERY xactions that are the result of a reparse.
$previously_discovered_commits = array();
$discovery_xactions = idx(
mgroup($xactions, 'getTransactionType'),
ReleephRequestTransaction::TYPE_DISCOVERY);
if ($discovery_xactions) {
$previous_xactions = id(new ReleephRequestTransactionQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withObjectPHIDs(array($object->getPHID()))
->execute();
foreach ($previous_xactions as $xaction) {
if ($xaction->getTransactionType() ===
ReleephRequestTransaction::TYPE_DISCOVERY) {
$commit_phid = $xaction->getNewValue();
$previously_discovered_commits[$commit_phid] = true;
}
}
}
foreach ($xactions as $key => $xaction) {
if ($xaction->getTransactionType() ===
ReleephRequestTransaction::TYPE_DISCOVERY &&
idx($previously_discovered_commits, $xaction->getNewValue())) {
unset($xactions[$key]);
}
}
return parent::filterTransactions($object, $xactions);
}
protected function shouldSendMail(
PhabricatorLiskDAO $object,
array $xactions) {
// Avoid sending emails that only talk about commit discovery.
$types = array_unique(mpull($xactions, 'getTransactionType'));
if ($types === array(ReleephRequestTransaction::TYPE_DISCOVERY)) {
return false;
}
// Don't email people when we discover that something picks or reverts OK.
if ($types === array(ReleephRequestTransaction::TYPE_PICK_STATUS)) {
if (!mfilter($xactions, 'isBoringPickStatus', true /* negate */)) {
// If we effectively call "isInterestingPickStatus" and get nothing...
return false;
}
}
return true;
}
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
return id(new ReleephRequestReplyHandler())
->setActor($this->getActor())
->setMailReceiver($object);
}
protected function getMailSubjectPrefix() {
return '[Releeph]';
}
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
$id = $object->getID();
$title = $object->getSummaryForDisplay();
return id(new PhabricatorMetaMTAMail())
->setSubject("RQ{$id}: {$title}");
}
protected function getMailTo(PhabricatorLiskDAO $object) {
$to_phids = array();
$product = $object->getBranch()->getProduct();
foreach ($product->getPushers() as $phid) {
$to_phids[] = $phid;
}
foreach ($object->getUserIntents() as $phid => $intent) {
$to_phids[] = $phid;
}
return $to_phids;
}
protected function getMailCC(PhabricatorLiskDAO $object) {
return array();
}
protected function buildMailBody(
PhabricatorLiskDAO $object,
array $xactions) {
$body = parent::buildMailBody($object, $xactions);
$rq = $object;
$releeph_branch = $rq->getBranch();
$releeph_project = $releeph_branch->getProduct();
/**
* If any of the events we are emailing about were about a pick failure
* (and/or a revert failure?), include pick failure instructions.
*/
$has_pick_failure = false;
foreach ($xactions as $xaction) {
if ($xaction->getTransactionType() ===
ReleephRequestTransaction::TYPE_PICK_STATUS &&
$xaction->getNewValue() === ReleephRequest::PICK_FAILED) {
$has_pick_failure = true;
break;
}
}
if ($has_pick_failure) {
$instructions = $releeph_project->getDetail('pick_failure_instructions');
if ($instructions) {
$body->addRemarkupSection(
pht('PICK FAILURE INSTRUCTIONS'),
$instructions);
}
}
$name = sprintf('RQ%s: %s', $rq->getID(), $rq->getSummaryForDisplay());
$body->addTextSection(
pht('RELEEPH REQUEST'),
$name."\n".
PhabricatorEnv::getProductionURI('/RQ'.$rq->getID()));
$project_and_branch = sprintf(
'%s - %s',
$releeph_project->getName(),
$releeph_branch->getDisplayNameWithDetail());
$body->addTextSection(
pht('RELEEPH BRANCH'),
$project_and_branch."\n".
PhabricatorEnv::getProductionURI($releeph_branch->getURI()));
return $body;
}
private function setInBranchFromAction(
ReleephRequest $rq,
ReleephRequestTransaction $xaction) {
$action = $xaction->getMetadataValue('action');
switch ($action) {
case 'pick':
$rq->setInBranch(1);
break;
case 'revert':
$rq->setInBranch(0);
break;
default:
$id = $rq->getID();
$type = $xaction->getTransactionType();
$new = $xaction->getNewValue();
phlog(
pht(
"Unknown discovery action '%s' for xaction of type %s ".
"with new value %s mentioning %s!",
$action,
$type,
$new,
'RQ'.$id));
break;
}
return $this;
}
}

View file

@ -1,12 +0,0 @@
<?php
final class ReleephFieldParseException extends Exception {
public function __construct(ReleephFieldSpecification $field,
$message) {
$name = $field->getName();
parent::__construct("{$name}: {$message}");
}
}

View file

@ -1,71 +0,0 @@
<?php
final class ReleephDefaultFieldSelector extends ReleephFieldSelector {
/**
* Determine if this install is Facebook.
*
* TODO: This is a giant hacky mess because I am dumb and moved forward on
* Releeph changes with partial information. Recover from this as gracefully
* as possible. This obviously is an abomination. -epriestley
*/
public static function isFacebook() {
return class_exists('ReleephFacebookKarmaFieldSpecification');
}
/**
* @phutil-external-symbol class ReleephFacebookKarmaFieldSpecification
* @phutil-external-symbol class ReleephFacebookSeverityFieldSpecification
* @phutil-external-symbol class ReleephFacebookTagFieldSpecification
* @phutil-external-symbol class ReleephFacebookTasksFieldSpecification
*/
public function getFieldSpecifications() {
if (self::isFacebook()) {
return array(
new ReleephCommitMessageFieldSpecification(),
new ReleephSummaryFieldSpecification(),
new ReleephReasonFieldSpecification(),
new ReleephAuthorFieldSpecification(),
new ReleephRevisionFieldSpecification(),
new ReleephRequestorFieldSpecification(),
new ReleephFacebookKarmaFieldSpecification(),
new ReleephFacebookSeverityFieldSpecification(),
new ReleephOriginalCommitFieldSpecification(),
new ReleephDiffMessageFieldSpecification(),
new ReleephIntentFieldSpecification(),
new ReleephBranchCommitFieldSpecification(),
new ReleephDiffSizeFieldSpecification(),
new ReleephDiffChurnFieldSpecification(),
new ReleephDependsOnFieldSpecification(),
new ReleephFacebookTagFieldSpecification(),
new ReleephFacebookTasksFieldSpecification(),
);
} else {
return array(
new ReleephCommitMessageFieldSpecification(),
new ReleephSummaryFieldSpecification(),
new ReleephReasonFieldSpecification(),
new ReleephAuthorFieldSpecification(),
new ReleephRevisionFieldSpecification(),
new ReleephRequestorFieldSpecification(),
new ReleephSeverityFieldSpecification(),
new ReleephOriginalCommitFieldSpecification(),
new ReleephDiffMessageFieldSpecification(),
new ReleephIntentFieldSpecification(),
new ReleephBranchCommitFieldSpecification(),
new ReleephDiffSizeFieldSpecification(),
new ReleephDiffChurnFieldSpecification(),
);
}
}
public function sortFieldsForCommitMessage(array $fields) {
return self::selectFields($fields, array(
'ReleephCommitMessageFieldSpecification',
'ReleephRequestorFieldSpecification',
'ReleephIntentFieldSpecification',
'ReleephReasonFieldSpecification',
));
}
}

View file

@ -1,48 +0,0 @@
<?php
abstract class ReleephFieldSelector extends Phobject {
final public function __construct() {
// <empty>
}
abstract public function getFieldSpecifications();
public function sortFieldsForCommitMessage(array $fields) {
assert_instances_of($fields, 'ReleephFieldSpecification');
return $fields;
}
protected static function selectFields(array $fields, array $classes) {
assert_instances_of($fields, 'ReleephFieldSpecification');
$map = array();
foreach ($fields as $field) {
$map[get_class($field)] = $field;
}
$result = array();
foreach ($classes as $class) {
$field = idx($map, $class);
if (!$field) {
throw new Exception(
pht(
"Tried to select a in instance of '%s' but that field ".
"is not configured for this project!",
$class));
}
if (idx($result, $class)) {
throw new Exception(
pht(
"You have asked to select the field '%s' more than once!",
$class));
}
$result[$class] = $field;
}
return $result;
}
}

View file

@ -1,33 +0,0 @@
<?php
final class ReleephAuthorFieldSpecification
extends ReleephFieldSpecification {
public function getFieldKey() {
return 'author';
}
public function getName() {
return pht('Author');
}
public function getRequiredHandlePHIDsForPropertyView() {
$pull = $this->getReleephRequest();
$commit = $pull->loadPhabricatorRepositoryCommit();
if (!$commit) {
return array();
}
$author_phid = $commit->getAuthorPHID();
if (!$author_phid) {
return array();
}
return array($author_phid);
}
public function renderPropertyViewValue(array $handles) {
return $this->renderHandleList($handles);
}
}

View file

@ -1,28 +0,0 @@
<?php
final class ReleephBranchCommitFieldSpecification
extends ReleephFieldSpecification {
public function getFieldKey() {
return 'commit';
}
public function getName() {
return pht('Commit');
}
public function getRequiredHandlePHIDsForPropertyView() {
$pull = $this->getReleephRequest();
if ($pull->getCommitPHID()) {
return array($pull->getCommitPHID());
}
return array();
}
public function renderPropertyViewValue(array $handles) {
return $this->renderHandleList($handles);
}
}

View file

@ -1,54 +0,0 @@
<?php
final class ReleephCommitMessageFieldSpecification
extends ReleephFieldSpecification {
public function getFieldKey() {
return 'commit:apply';
}
public function getName() {
return '__only_for_commit_message!';
}
public function shouldAppearInPropertyView() {
return false;
}
public function shouldAppearOnCommitMessage() {
return true;
}
public function renderLabelForCommitMessage() {
return $this->renderCommonLabel();
}
public function renderValueForCommitMessage() {
return $this->renderCommonValue(
DifferentialReleephRequestFieldSpecification::ACTION_PICKS);
}
public function shouldAppearOnRevertMessage() {
return true;
}
public function renderLabelForRevertMessage() {
return $this->renderCommonLabel();
}
public function renderValueForRevertMessage() {
return $this->renderCommonValue(
DifferentialReleephRequestFieldSpecification::ACTION_REVERTS);
}
private function renderCommonLabel() {
return id(new DifferentialReleephRequestFieldSpecification())
->renderLabelForCommitMessage();
}
private function renderCommonValue($action) {
$rq = 'RQ'.$this->getReleephRequest()->getID();
return "{$action} {$rq}";
}
}

View file

@ -1,34 +0,0 @@
<?php
final class ReleephDependsOnFieldSpecification
extends ReleephFieldSpecification {
public function getFieldKey() {
return 'dependsOn';
}
public function getName() {
return pht('Depends On');
}
public function getRequiredHandlePHIDsForPropertyView() {
return $this->getDependentRevisionPHIDs();
}
public function renderPropertyViewValue(array $handles) {
return $this->renderHandleList($handles);
}
private function getDependentRevisionPHIDs() {
$requested_object = $this->getObject()->getRequestedObjectPHID();
if (!($requested_object instanceof DifferentialRevision)) {
return array();
}
$revision = $requested_object;
return PhabricatorEdgeQuery::loadDestinationPHIDs(
$revision->getPHID(),
DifferentialRevisionDependsOnRevisionEdgeType::EDGECONST);
}
}

View file

@ -1,90 +0,0 @@
<?php
final class ReleephDiffChurnFieldSpecification
extends ReleephFieldSpecification {
const REJECTIONS_WEIGHT = 30;
const COMMENTS_WEIGHT = 7;
const UPDATES_WEIGHT = 10;
const MAX_POINTS = 100;
public function getFieldKey() {
return 'churn';
}
public function getName() {
return pht('Churn');
}
public function renderPropertyViewValue(array $handles) {
$requested_object = $this->getObject()->getRequestedObject();
if (!($requested_object instanceof DifferentialRevision)) {
return null;
}
$diff_rev = $requested_object;
$xactions = id(new DifferentialTransactionQuery())
->setViewer($this->getViewer())
->withObjectPHIDs(array($diff_rev->getPHID()))
->execute();
$rejections = 0;
$comments = 0;
$updates = 0;
foreach ($xactions as $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorTransactions::TYPE_COMMENT:
$comments++;
break;
case DifferentialRevisionUpdateTransaction::TRANSACTIONTYPE:
$updates++;
break;
case DifferentialTransaction::TYPE_ACTION:
switch ($xaction->getNewValue()) {
case DifferentialAction::ACTION_REJECT:
$rejections++;
break;
}
break;
}
}
$points =
self::REJECTIONS_WEIGHT * $rejections +
self::COMMENTS_WEIGHT * $comments +
self::UPDATES_WEIGHT * $updates;
if ($points === 0) {
$points = 0.15 * self::MAX_POINTS;
$blurb = pht('Silent diff');
} else {
$parts = array();
if ($rejections) {
$parts[] = pht('%s rejection(s)', new PhutilNumber($rejections));
}
if ($comments) {
$parts[] = pht('%s comment(s)', new PhutilNumber($comments));
}
if ($updates) {
$parts[] = pht('%s update(s)', new PhutilNumber($updates));
}
if (count($parts) === 0) {
$blurb = '';
} else if (count($parts) === 1) {
$blurb = head($parts);
} else {
$last = array_pop($parts);
$blurb = pht('%s and %s', implode(', ', $parts), $last);
}
}
return id(new AphrontProgressBarView())
->setValue($points)
->setMax(self::MAX_POINTS)
->setCaption($blurb)
->render();
}
}

View file

@ -1,42 +0,0 @@
<?php
final class ReleephDiffMessageFieldSpecification
extends ReleephFieldSpecification {
public function getFieldKey() {
return 'commit:message';
}
public function getName() {
return pht('Message');
}
public function getStyleForPropertyView() {
return 'block';
}
public function renderPropertyViewValue(array $handles) {
return phutil_tag(
'div',
array(
'class' => 'phabricator-remarkup',
),
$this->getMarkupEngineOutput());
}
public function shouldMarkup() {
return true;
}
public function getMarkupText($field) {
$commit_data = $this
->getReleephRequest()
->loadPhabricatorRepositoryCommitData();
if ($commit_data) {
return $commit_data->getCommitMessage();
} else {
return '';
}
}
}

View file

@ -1,118 +0,0 @@
<?php
final class ReleephDiffSizeFieldSpecification
extends ReleephFieldSpecification {
const LINES_WEIGHT = 1;
const PATHS_WEIGHT = 30;
const MAX_POINTS = 1000;
public function getFieldKey() {
return 'commit:size';
}
public function getName() {
return pht('Size');
}
public function renderPropertyViewValue(array $handles) {
$requested_object = $this->getObject()->getRequestedObject();
if (!($requested_object instanceof DifferentialRevision)) {
return null;
}
$diff_rev = $requested_object;
$diffs = id(new DifferentialDiff())->loadAllWhere(
'revisionID = %d AND creationMethod != %s',
$diff_rev->getID(),
'commit');
$all_changesets = array();
$most_recent_changesets = null;
foreach ($diffs as $diff) {
$changesets = id(new DifferentialChangeset())->loadAllWhere(
'diffID = %d',
$diff->getID());
$all_changesets += $changesets;
$most_recent_changesets = $changesets;
}
// The score is based on all changesets for all versions of this diff
$all_changes = $this->countLinesAndPaths($all_changesets);
$points =
self::LINES_WEIGHT * $all_changes['code']['lines'] +
self::PATHS_WEIGHT * count($all_changes['code']['paths']);
// The blurb is just based on the most recent version of the diff
$mr_changes = $this->countLinesAndPaths($most_recent_changesets);
$test_tag = '';
if ($mr_changes['tests']['paths']) {
Javelin::initBehavior('phabricator-tooltips');
require_celerity_resource('aphront-tooltip-css');
$test_blurb = pht(
"%d line(s) and %d path(s) contain changes to test code:\n",
$mr_changes['tests']['lines'],
count($mr_changes['tests']['paths']));
foreach ($mr_changes['tests']['paths'] as $mr_test_path) {
$test_blurb .= sprintf("%s\n", $mr_test_path);
}
$test_tag = javelin_tag(
'span',
array(
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => $test_blurb,
'align' => 'E',
'size' => 'auto',
),
'style' => '',
),
' + tests');
}
$blurb = hsprintf('%s%s.',
pht(
'%d line(s) and %d path(s) over %d diff(s)',
$mr_changes['code']['lines'],
$mr_changes['code']['paths'],
count($diffs)),
$test_tag);
return id(new AphrontProgressBarView())
->setValue($points)
->setMax(self::MAX_POINTS)
->setCaption($blurb)
->render();
}
private function countLinesAndPaths(array $changesets) {
assert_instances_of($changesets, 'DifferentialChangeset');
$lines = 0;
$paths_touched = array();
$test_lines = 0;
$test_paths_touched = array();
foreach ($changesets as $ch) {
if ($this->getReleephProject()->isTestFile($ch->getFilename())) {
$test_lines += $ch->getAddLines() + $ch->getDelLines();
$test_paths_touched[] = $ch->getFilename();
} else {
$lines += $ch->getAddLines() + $ch->getDelLines();
$paths_touched[] = $ch->getFilename();
}
}
return array(
'code' => array(
'lines' => $lines,
'paths' => array_unique($paths_touched),
),
'tests' => array(
'lines' => $test_lines,
'paths' => array_unique($test_paths_touched),
),
);
}
}

View file

@ -1,265 +0,0 @@
<?php
abstract class ReleephFieldSpecification
extends PhabricatorCustomField
implements PhabricatorMarkupInterface {
// TODO: This is temporary, until ReleephFieldSpecification is more conformant
// to PhabricatorCustomField.
private $requestValue;
public function readValueFromRequest(AphrontRequest $request) {
$this->requestValue = $request->getStr($this->getRequiredStorageKey());
return $this;
}
public function shouldAppearInPropertyView() {
return true;
}
public function renderPropertyViewLabel() {
return $this->getName();
}
public function renderPropertyViewValue(array $handles) {
$key = $this->getRequiredStorageKey();
$value = $this->getReleephRequest()->getDetail($key);
if ($value === '') {
return null;
}
return $value;
}
abstract public function getName();
/* -( Storage )------------------------------------------------------------ */
public function getStorageKey() {
return null;
}
public function getRequiredStorageKey() {
$key = $this->getStorageKey();
if ($key === null) {
throw new PhabricatorCustomFieldImplementationIncompleteException($this);
}
if (strpos($key, '.') !== false) {
/**
* Storage keys are reused for form controls, and periods in form control
* names break HTML forms.
*/
throw new Exception(pht("You can't use '%s' in storage keys!", '.'));
}
return $key;
}
public function shouldAppearInEditView() {
return $this->isEditable();
}
final public function isEditable() {
return $this->getStorageKey() !== null;
}
final public function getValue() {
if ($this->requestValue !== null) {
return $this->requestValue;
}
$key = $this->getRequiredStorageKey();
return $this->getReleephRequest()->getDetail($key);
}
final public function setValue($value) {
$key = $this->getRequiredStorageKey();
return $this->getReleephRequest()->setDetail($key, $value);
}
/**
* @throws ReleephFieldParseException, to show an error.
*/
public function validate($value) {
return;
}
/**
* Turn values as they are stored in a ReleephRequest into a text that can be
* rendered as a transactions old/new values.
*/
public function normalizeForTransactionView(
PhabricatorApplicationTransaction $xaction,
$value) {
return $value;
}
/* -( Conduit )------------------------------------------------------------ */
public function getKeyForConduit() {
return $this->getRequiredStorageKey();
}
public function getValueForConduit() {
return $this->getValue();
}
public function setValueFromConduitAPIRequest(ConduitAPIRequest $request) {
$value = idx(
$request->getValue('fields', array()),
$this->getRequiredStorageKey());
$this->validate($value);
$this->setValue($value);
return $this;
}
/* -( Arcanist )----------------------------------------------------------- */
public function renderHelpForArcanist() {
return '';
}
/* -( Context )------------------------------------------------------------ */
private $releephProject;
private $releephBranch;
private $releephRequest;
private $user;
final public function setReleephProject(ReleephProject $rp) {
$this->releephProject = $rp;
return $this;
}
final public function setReleephBranch(ReleephBranch $rb) {
$this->releephRequest = $rb;
return $this;
}
final public function setReleephRequest(ReleephRequest $rr) {
$this->releephRequest = $rr;
return $this;
}
final public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
final public function getReleephProject() {
if (!$this->releephProject) {
return $this->getReleephBranch()->getProduct();
}
return $this->releephProject;
}
final public function getReleephBranch() {
if (!$this->releephBranch) {
return $this->getReleephRequest()->getBranch();
}
return $this->releephBranch;
}
final public function getReleephRequest() {
if (!$this->releephRequest) {
return $this->getObject();
}
return $this->releephRequest;
}
final public function getUser() {
if (!$this->user) {
return $this->getViewer();
}
return $this->user;
}
/* -( Commit Messages )---------------------------------------------------- */
public function shouldAppearOnCommitMessage() {
return false;
}
public function renderLabelForCommitMessage() {
throw new PhabricatorCustomFieldImplementationIncompleteException($this);
}
public function renderValueForCommitMessage() {
throw new PhabricatorCustomFieldImplementationIncompleteException($this);
}
public function shouldAppearOnRevertMessage() {
return false;
}
public function renderLabelForRevertMessage() {
return $this->renderLabelForCommitMessage();
}
public function renderValueForRevertMessage() {
return $this->renderValueForCommitMessage();
}
/* -( Markup Interface )--------------------------------------------------- */
const MARKUP_FIELD_GENERIC = 'releeph:generic-markup-field';
private $engine;
/**
* @{class:ReleephFieldSpecification} implements much of
* @{interface:PhabricatorMarkupInterface} for you. If you return true from
* `shouldMarkup()`, and implement `getMarkupText()` then your text will be
* rendered through the Phabricator markup pipeline.
*
* Output is retrievable with `getMarkupEngineOutput()`.
*/
public function shouldMarkup() {
return false;
}
public function getMarkupText($field) {
throw new PhabricatorCustomFieldImplementationIncompleteException($this);
}
final public function getMarkupEngineOutput() {
return $this->engine->getOutput($this, self::MARKUP_FIELD_GENERIC);
}
final public function setMarkupEngine(PhabricatorMarkupEngine $engine) {
$this->engine = $engine;
$engine->addObject($this, self::MARKUP_FIELD_GENERIC);
return $this;
}
final public function getMarkupFieldKey($field) {
$content = sprintf(
'%s:%s:%s:%s',
$this->getReleephRequest()->getPHID(),
$this->getStorageKey(),
$field,
$this->getMarkupText($field));
return PhabricatorMarkupEngine::digestRemarkupContent($this, $content);
}
final public function newMarkupEngine($field) {
return PhabricatorMarkupEngine::newDifferentialMarkupEngine();
}
final public function didMarkupText(
$field,
$output,
PhutilMarkupEngine $engine) {
return $output;
}
final public function shouldUseMarkupCache($field) {
return true;
}
}

View file

@ -1,142 +0,0 @@
<?php
final class ReleephIntentFieldSpecification
extends ReleephFieldSpecification {
public function getFieldKey() {
return 'intent';
}
public function getName() {
return pht('Intent');
}
public function getRequiredHandlePHIDsForPropertyView() {
$pull = $this->getReleephRequest();
$intents = $pull->getUserIntents();
return array_keys($intents);
}
public function renderPropertyViewValue(array $handles) {
$pull = $this->getReleephRequest();
$intents = $pull->getUserIntents();
$product = $this->getReleephProject();
if (!$intents) {
return null;
}
$pushers = array();
$others = array();
foreach ($intents as $phid => $intent) {
if ($product->isAuthoritativePHID($phid)) {
$pushers[$phid] = $intent;
} else {
$others[$phid] = $intent;
}
}
$intents = $pushers + $others;
$view = id(new PHUIStatusListView());
foreach ($intents as $phid => $intent) {
switch ($intent) {
case ReleephRequest::INTENT_WANT:
$icon = PHUIStatusItemView::ICON_ACCEPT;
$color = 'green';
$label = pht('Want');
break;
case ReleephRequest::INTENT_PASS:
$icon = PHUIStatusItemView::ICON_REJECT;
$color = 'red';
$label = pht('Pass');
break;
default:
$icon = PHUIStatusItemView::ICON_QUESTION;
$color = 'bluegrey';
$label = pht('Unknown Intent (%s)', $intent);
break;
}
$target = $handles[$phid]->renderLink();
if ($product->isAuthoritativePHID($phid)) {
$target = phutil_tag('strong', array(), $target);
}
$view->addItem(
id(new PHUIStatusItemView())
->setIcon($icon, $color, $label)
->setTarget($target));
}
return $view;
}
public function shouldAppearOnCommitMessage() {
return true;
}
public function shouldAppearOnRevertMessage() {
return true;
}
public function renderLabelForCommitMessage() {
return pht('Approved By');
}
public function renderLabelForRevertMessage() {
return pht('Rejected By');
}
public function renderValueForCommitMessage() {
return $this->renderIntentsForCommitMessage(ReleephRequest::INTENT_WANT);
}
public function renderValueForRevertMessage() {
return $this->renderIntentsForCommitMessage(ReleephRequest::INTENT_PASS);
}
private function renderIntentsForCommitMessage($print_intent) {
$intents = $this->getReleephRequest()->getUserIntents();
$requestor = $this->getReleephRequest()->getRequestUserPHID();
$pusher_phids = $this->getReleephProject()->getPushers();
$phids = array_unique($pusher_phids + array_keys($intents));
$handles = id(new PhabricatorHandleQuery())
->setViewer($this->getUser())
->withPHIDs($phids)
->execute();
$tokens = array();
foreach ($phids as $phid) {
$intent = idx($intents, $phid);
if ($intent == $print_intent) {
$name = $handles[$phid]->getName();
$is_pusher = in_array($phid, $pusher_phids);
$is_requestor = $phid == $requestor;
if ($is_pusher) {
if ($is_requestor) {
$token = pht('%s (pusher and requestor)', $name);
} else {
$token = "{$name} (pusher)";
}
} else {
if ($is_requestor) {
$token = pht('%s (requestor)', $name);
} else {
$token = $name;
}
}
$tokens[] = $token;
}
}
return implode(', ', $tokens);
}
}

View file

@ -1,137 +0,0 @@
<?php
/**
* Provides a convenient field for storing a set of levels that you can use to
* filter requests on.
*
* Levels are rendered with names and descriptions in the edit UI, and are
* automatically documented via the "arc request" interface.
*
* See ReleephSeverityFieldSpecification for an example.
*/
abstract class ReleephLevelFieldSpecification
extends ReleephFieldSpecification {
private $error;
abstract public function getLevels();
abstract public function getDefaultLevel();
abstract public function getNameForLevel($level);
abstract public function getDescriptionForLevel($level);
public function getStorageKey() {
throw new PhabricatorCustomFieldImplementationIncompleteException($this);
}
public function renderPropertyViewValue(array $handles) {
return $this->getNameForLevel($this->getValue());
}
public function renderEditControl(array $handles) {
$control_name = $this->getRequiredStorageKey();
$all_levels = $this->getLevels();
$level = $this->getValue();
if (!$level) {
$level = $this->getDefaultLevel();
}
$control = id(new AphrontFormRadioButtonControl())
->setLabel(pht('Level'))
->setName($control_name)
->setValue($level);
if ($this->error) {
$control->setError($this->error);
} else if ($this->getDefaultLevel()) {
$control->setError(true);
}
foreach ($all_levels as $level) {
$name = $this->getNameForLevel($level);
$description = $this->getDescriptionForLevel($level);
$control->addButton($level, $name, $description);
}
return $control;
}
public function renderHelpForArcanist() {
$text = '';
$levels = $this->getLevels();
$default = $this->getDefaultLevel();
foreach ($levels as $level) {
$name = $this->getNameForLevel($level);
$description = $this->getDescriptionForLevel($level);
$default_marker = ' ';
if ($level === $default) {
$default_marker = '*';
}
$text .= " {$default_marker} **{$name}**\n";
$text .= phutil_console_wrap($description."\n", 8);
}
return $text;
}
public function validate($value) {
if ($value === null) {
$this->error = pht('Required');
$label = $this->getName();
throw new ReleephFieldParseException(
$this,
pht('You must provide a %s level.', $label));
}
$levels = $this->getLevels();
if (!in_array($value, $levels)) {
$label = $this->getName();
throw new ReleephFieldParseException(
$this,
pht(
"Level '%s' is not a valid %s level in this project.",
$value,
$label));
}
}
public function setValueFromConduitAPIRequest(ConduitAPIRequest $request) {
$key = $this->getRequiredStorageKey();
$label = $this->getName();
$name = idx($request->getValue('fields', array()), $key);
if (!$name) {
$level = $this->getDefaultLevel();
if (!$level) {
throw new ReleephFieldParseException(
$this,
pht(
'No value given for %s, and no default is given for this level!',
$label));
}
} else {
$level = $this->getLevelByName($name);
}
if (!$level) {
throw new ReleephFieldParseException(
$this,
pht("Unknown %s level name '%s'", $label, $name));
}
$this->setValue($level);
return $this;
}
private $nameMap = array();
public function getLevelByName($name) {
// Build this once
if (!$this->nameMap) {
foreach ($this->getLevels() as $level) {
$level_name = $this->getNameForLevel($level);
$this->nameMap[$level_name] = $level;
}
}
return idx($this->nameMap, $name);
}
}

View file

@ -1,25 +0,0 @@
<?php
final class ReleephOriginalCommitFieldSpecification
extends ReleephFieldSpecification {
public function getFieldKey() {
return 'commit:name';
}
public function getName() {
return pht('Commit');
}
public function getRequiredHandlePHIDsForPropertyView() {
return array(
$this->getReleephRequest()->getRequestCommitPHID(),
);
}
public function renderPropertyViewValue(array $handles) {
return $this->renderHandleList($handles);
}
}

View file

@ -1,86 +0,0 @@
<?php
final class ReleephReasonFieldSpecification
extends ReleephFieldSpecification {
public function getFieldKey() {
return 'reason';
}
public function getName() {
return pht('Reason');
}
public function getStorageKey() {
return 'reason';
}
public function getStyleForPropertyView() {
return 'block';
}
public function getIconForPropertyView() {
return PHUIPropertyListView::ICON_SUMMARY;
}
public function renderPropertyViewValue(array $handles) {
return phutil_tag(
'div',
array(
'class' => 'phabricator-remarkup',
),
$this->getMarkupEngineOutput());
}
private $error = true;
public function renderEditControl(array $handles) {
return id(new AphrontFormTextAreaControl())
->setLabel(pht('Reason'))
->setName('reason')
->setError($this->error)
->setValue($this->getValue());
}
public function validate($reason) {
if (!$reason) {
$this->error = pht('Required');
throw new ReleephFieldParseException(
$this,
pht('You must give a reason for your request.'));
}
}
public function renderHelpForArcanist() {
$text = pht(
'Fully explain why you are requesting this code be included '.
'in the next release.')."\n";
return phutil_console_wrap($text, 8);
}
public function shouldAppearOnCommitMessage() {
return true;
}
public function renderLabelForCommitMessage() {
return pht('Request Reason');
}
public function renderValueForCommitMessage() {
return $this->getValue();
}
public function shouldMarkup() {
return true;
}
public function getMarkupText($field) {
$reason = $this->getValue();
if ($reason) {
return $reason;
} else {
return '';
}
}
}

View file

@ -1,50 +0,0 @@
<?php
final class ReleephRequestorFieldSpecification
extends ReleephFieldSpecification {
public function getFieldKey() {
return 'requestor';
}
public function getName() {
return pht('Requestor');
}
public function getRequiredHandlePHIDsForPropertyView() {
$phids = array();
$phid = $this->getReleephRequest()->getRequestUserPHID();
if ($phid) {
$phids[] = $phid;
}
return $phids;
}
public function renderPropertyViewValue(array $handles) {
return $this->renderHandleList($handles);
}
public function shouldAppearOnCommitMessage() {
return true;
}
public function shouldAppearOnRevertMessage() {
return true;
}
public function renderLabelForCommitMessage() {
return pht('Requested By');
}
public function renderValueForCommitMessage() {
$phid = $this->getReleephRequest()->getRequestUserPHID();
$handle = id(new PhabricatorHandleQuery())
->setViewer($this->getUser())
->withPHIDs(array($phid))
->executeOne();
return $handle->getName();
}
}

View file

@ -1,29 +0,0 @@
<?php
final class ReleephRevisionFieldSpecification
extends ReleephFieldSpecification {
public function getFieldKey() {
return 'revision';
}
public function getName() {
return pht('Revision');
}
public function getRequiredHandlePHIDsForPropertyView() {
$requested_object = $this->getObject()->getRequestedObjectPHID();
if (!($requested_object instanceof DifferentialRevision)) {
return array();
}
return array(
$requested_object->getPHID(),
);
}
public function renderPropertyViewValue(array $handles) {
return $this->renderHandleList($handles);
}
}

View file

@ -1,53 +0,0 @@
<?php
final class ReleephSeverityFieldSpecification
extends ReleephLevelFieldSpecification {
const HOTFIX = 'HOTFIX';
const RELEASE = 'RELEASE';
public function getFieldKey() {
return 'severity';
}
public function getName() {
return pht('Severity');
}
public function getStorageKey() {
return 'releeph:severity';
}
public function getLevels() {
return array(
self::HOTFIX,
self::RELEASE,
);
}
public function getDefaultLevel() {
return self::RELEASE;
}
public function getNameForLevel($level) {
static $names = array(
self::HOTFIX => 'HOTFIX',
self::RELEASE => 'RELEASE',
);
return idx($names, $level, $level);
}
public function getDescriptionForLevel($level) {
static $descriptions;
if ($descriptions === null) {
$descriptions = array(
self::HOTFIX => pht('Needs merging and fixing right now.'),
self::RELEASE => pht('Required for the currently rolling release.'),
);
}
return idx($descriptions, $level);
}
}

View file

@ -1,53 +0,0 @@
<?php
final class ReleephSummaryFieldSpecification
extends ReleephFieldSpecification {
const MAX_SUMMARY_LENGTH = 60;
public function shouldAppearInPropertyView() {
return false;
}
public function getFieldKey() {
return 'summary';
}
public function getName() {
return pht('Summary');
}
public function getStorageKey() {
return 'summary';
}
private $error = false;
public function renderEditControl(array $handles) {
return id(new AphrontFormTextControl())
->setLabel(pht('Summary'))
->setName('summary')
->setError($this->error)
->setValue($this->getValue())
->setCaption(pht('Leave this blank to use the original commit title'));
}
public function renderHelpForArcanist() {
$text = pht(
'A one-line title summarizing this request. '.
'Leave blank to use the original commit title.')."\n";
return phutil_console_wrap($text, 8);
}
public function validate($summary) {
if ($summary && strlen($summary) > self::MAX_SUMMARY_LENGTH) {
$this->error = pht('Too long!');
throw new ReleephFieldParseException(
$this,
pht(
'Please keep your summary to under %d characters.',
self::MAX_SUMMARY_LENGTH));
}
}
}

View file

@ -1,27 +0,0 @@
<?php
final class ReleephRequestMailReceiver extends PhabricatorObjectMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorReleephApplication';
return PhabricatorApplication::isClassInstalled($app_class);
}
protected function getObjectPattern() {
return 'Y[1-9]\d*';
}
protected function loadObject($pattern, PhabricatorUser $viewer) {
$id = (int)substr($pattern, 1);
return id(new ReleephRequestQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
}
protected function getTransactionReplyHandler() {
return new ReleephRequestReplyHandler();
}
}

View file

@ -1,16 +0,0 @@
<?php
final class ReleephRequestReplyHandler
extends PhabricatorApplicationTransactionReplyHandler {
public function validateMailReceiver($mail_receiver) {
if (!($mail_receiver instanceof ReleephRequest)) {
throw new Exception(pht('Mail receiver is not a %s!', 'ReleephRequest'));
}
}
public function getObjectPrefix() {
return 'Y';
}
}

View file

@ -1,41 +0,0 @@
<?php
final class ReleephBranchPHIDType extends PhabricatorPHIDType {
const TYPECONST = 'REBR';
public function getTypeName() {
return pht('Releeph Branch');
}
public function newObject() {
return new ReleephBranch();
}
public function getPHIDTypeApplicationClass() {
return 'PhabricatorReleephApplication';
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new ReleephBranchQuery())
->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$branch = $objects[$phid];
$handle->setURI($branch->getURI());
$handle->setName($branch->getBasename());
$handle->setFullName($branch->getName());
}
}
}

View file

@ -1,40 +0,0 @@
<?php
final class ReleephProductPHIDType extends PhabricatorPHIDType {
const TYPECONST = 'REPR';
public function getTypeName() {
return pht('Releeph Product');
}
public function newObject() {
return new ReleephProject();
}
public function getPHIDTypeApplicationClass() {
return 'PhabricatorReleephApplication';
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new ReleephProductQuery())
->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$product = $objects[$phid];
$handle->setName($product->getName());
$handle->setURI($product->getURI());
}
}
}

View file

@ -1,44 +0,0 @@
<?php
final class ReleephRequestPHIDType extends PhabricatorPHIDType {
const TYPECONST = 'RERQ';
public function getTypeName() {
return pht('Releeph Request');
}
public function newObject() {
return new ReleephRequest();
}
public function getPHIDTypeApplicationClass() {
return 'PhabricatorReleephApplication';
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new ReleephRequestQuery())
->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$request = $objects[$phid];
$id = $request->getID();
$title = $request->getSummaryForDisplay();
$handle->setURI("/RQ{$id}");
$handle->setName($title);
$handle->setFullName("RQ{$id}: {$title}");
}
}
}

View file

@ -1,152 +0,0 @@
<?php
final class ReleephBranchQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $productPHIDs;
private $productIDs;
const STATUS_ALL = 'status-all';
const STATUS_OPEN = 'status-open';
private $status = self::STATUS_ALL;
private $needCutPointCommits;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function needCutPointCommits($need_commits) {
$this->needCutPointCommits = $need_commits;
return $this;
}
public function withStatus($status) {
$this->status = $status;
return $this;
}
public function withProductPHIDs($product_phids) {
$this->productPHIDs = $product_phids;
return $this;
}
protected function loadPage() {
$table = new ReleephBranch();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
protected function willExecute() {
if ($this->productPHIDs !== null) {
$products = id(new ReleephProductQuery())
->setViewer($this->getViewer())
->withPHIDs($this->productPHIDs)
->execute();
if (!$products) {
throw new PhabricatorEmptyQueryException();
}
$this->productIDs = mpull($products, 'getID');
}
}
protected function willFilterPage(array $branches) {
$project_ids = mpull($branches, 'getReleephProjectID');
$projects = id(new ReleephProductQuery())
->withIDs($project_ids)
->setViewer($this->getViewer())
->execute();
foreach ($branches as $key => $branch) {
$project_id = $project_ids[$key];
if (isset($projects[$project_id])) {
$branch->attachProject($projects[$project_id]);
} else {
unset($branches[$key]);
}
}
if ($this->needCutPointCommits) {
$commit_phids = mpull($branches, 'getCutPointCommitPHID');
$commits = id(new DiffusionCommitQuery())
->setViewer($this->getViewer())
->withPHIDs($commit_phids)
->execute();
$commits = mpull($commits, null, 'getPHID');
foreach ($branches as $branch) {
$commit = idx($commits, $branch->getCutPointCommitPHID());
$branch->attachCutPointCommit($commit);
}
}
return $branches;
}
protected function buildWhereClause(AphrontDatabaseConnection $conn) {
$where = array();
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'phid IN (%Ls)',
$this->phids);
}
if ($this->productIDs !== null) {
$where[] = qsprintf(
$conn,
'releephProjectID IN (%Ld)',
$this->productIDs);
}
$status = $this->status;
switch ($status) {
case self::STATUS_ALL:
break;
case self::STATUS_OPEN:
$where[] = qsprintf(
$conn,
'isActive = 1');
break;
default:
throw new Exception(pht("Unknown status constant '%s'!", $status));
}
$where[] = $this->buildPagingClause($conn);
return $this->formatWhereClause($conn, $where);
}
public function getQueryApplicationClass() {
return 'PhabricatorReleephApplication';
}
}

View file

@ -1,200 +0,0 @@
<?php
final class ReleephBranchSearchEngine
extends PhabricatorApplicationSearchEngine {
private $product;
public function getResultTypeDescription() {
return pht('Releeph Branches');
}
public function canUseInPanelContext() {
return false;
}
public function getApplicationClassName() {
return 'PhabricatorReleephApplication';
}
public function setProduct(ReleephProject $product) {
$this->product = $product;
return $this;
}
public function getProduct() {
return $this->product;
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter('active', $request->getStr('active'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new ReleephBranchQuery())
->needCutPointCommits(true)
->withProductPHIDs(array($this->getProduct()->getPHID()));
$active = $saved->getParameter('active');
$value = idx($this->getActiveValues(), $active);
if ($value !== null) {
$query->withStatus($value);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
$form->appendChild(
id(new AphrontFormSelectControl())
->setName('active')
->setLabel(pht('Show Branches'))
->setValue($saved_query->getParameter('active'))
->setOptions($this->getActiveOptions()));
}
protected function getURI($path) {
return '/releeph/product/'.$this->getProduct()->getID().'/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'open' => pht('Open'),
'all' => pht('All'),
);
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'open':
return $query
->setParameter('active', 'open');
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
private function getActiveOptions() {
return array(
'open' => pht('Open Branches'),
'all' => pht('Open and Closed Branches'),
);
}
private function getActiveValues() {
return array(
'open' => ReleephBranchQuery::STATUS_OPEN,
'all' => ReleephBranchQuery::STATUS_ALL,
);
}
protected function renderResultList(
array $branches,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($branches, 'ReleephBranch');
$viewer = $this->getRequest()->getUser();
$products = mpull($branches, 'getProduct');
$repo_phids = mpull($products, 'getRepositoryPHID');
if ($repo_phids) {
$repos = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->withPHIDs($repo_phids)
->execute();
$repos = mpull($repos, null, 'getPHID');
} else {
$repos = array();
}
$requests = array();
if ($branches) {
$requests = id(new ReleephRequestQuery())
->setViewer($viewer)
->withBranchIDs(mpull($branches, 'getID'))
->withStatus(ReleephRequestQuery::STATUS_OPEN)
->execute();
$requests = mgroup($requests, 'getBranchID');
}
$list = id(new PHUIObjectItemListView())
->setUser($viewer);
foreach ($branches as $branch) {
$diffusion_href = null;
$repo = idx($repos, $branch->getProduct()->getRepositoryPHID());
if ($repo) {
$drequest = DiffusionRequest::newFromDictionary(
array(
'user' => $viewer,
'repository' => $repo,
));
$diffusion_href = $drequest->generateURI(
array(
'action' => 'branch',
'branch' => $branch->getName(),
));
}
$branch_link = $branch->getName();
if ($diffusion_href) {
$branch_link = phutil_tag(
'a',
array(
'href' => $diffusion_href,
),
$branch_link);
}
$item = id(new PHUIObjectItemView())
->setHeader($branch->getDisplayName())
->setHref($this->getApplicationURI('branch/'.$branch->getID().'/'))
->addAttribute($branch_link);
if (!$branch->getIsActive()) {
$item->setDisabled(true);
}
$commit = $branch->getCutPointCommit();
if ($commit) {
$item->addIcon(
'none',
phabricator_datetime($commit->getEpoch(), $viewer));
}
$open_count = count(idx($requests, $branch->getID(), array()));
if ($open_count) {
$item->setStatusIcon('fa-code-fork orange');
$item->addIcon(
'fa-code-fork',
pht(
'%s Open Pull Request(s)',
new PhutilNumber($open_count)));
}
$list->addItem($item);
}
return id(new PhabricatorApplicationSearchResultView())
->setObjectList($list);
}
}

View file

@ -1,10 +0,0 @@
<?php
final class ReleephBranchTransactionQuery
extends PhabricatorApplicationTransactionQuery {
public function getTemplateApplicationTransaction() {
return new ReleephBranchTransaction();
}
}

View file

@ -1,144 +0,0 @@
<?php
final class ReleephProductQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $active;
private $ids;
private $phids;
private $repositoryPHIDs;
const ORDER_ID = 'order-id';
const ORDER_NAME = 'order-name';
public function withActive($active) {
$this->active = $active;
return $this;
}
public function setOrder($order) {
switch ($order) {
case self::ORDER_ID:
$this->setOrderVector(array('id'));
break;
case self::ORDER_NAME:
$this->setOrderVector(array('name'));
break;
default:
throw new Exception(pht('Order "%s" not supported.', $order));
}
return $this;
}
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withRepositoryPHIDs(array $repository_phids) {
$this->repositoryPHIDs = $repository_phids;
return $this;
}
protected function loadPage() {
$table = new ReleephProject();
$conn_r = $table->establishConnection('r');
$rows = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($rows);
}
protected function willFilterPage(array $projects) {
assert_instances_of($projects, 'ReleephProject');
$repository_phids = mpull($projects, 'getRepositoryPHID');
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($this->getViewer())
->withPHIDs($repository_phids)
->execute();
$repositories = mpull($repositories, null, 'getPHID');
foreach ($projects as $key => $project) {
$repo = idx($repositories, $project->getRepositoryPHID());
if (!$repo) {
unset($projects[$key]);
continue;
}
$project->attachRepository($repo);
}
return $projects;
}
protected function buildWhereClause(AphrontDatabaseConnection $conn) {
$where = array();
if ($this->active !== null) {
$where[] = qsprintf(
$conn,
'isActive = %d',
(int)$this->active);
}
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'id IN (%Ls)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'phid IN (%Ls)',
$this->phids);
}
if ($this->repositoryPHIDs !== null) {
$where[] = qsprintf(
$conn,
'repositoryPHID IN (%Ls)',
$this->repositoryPHIDs);
}
$where[] = $this->buildPagingClause($conn);
return $this->formatWhereClause($conn, $where);
}
public function getOrderableColumns() {
return parent::getOrderableColumns() + array(
'name' => array(
'column' => 'name',
'unique' => true,
'reverse' => true,
'type' => 'string',
),
);
}
protected function newPagingMapFromPartialObject($object) {
return array(
'id' => (int)$object->getID(),
'name' => $object->getName(),
);
}
public function getQueryApplicationClass() {
return 'PhabricatorReleephApplication';
}
}

View file

@ -1,134 +0,0 @@
<?php
final class ReleephProductSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Releeph Products');
}
public function getApplicationClassName() {
return 'PhabricatorReleephApplication';
}
public function canUseInPanelContext() {
return false;
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter('active', $request->getStr('active'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new ReleephProductQuery())
->setOrder(ReleephProductQuery::ORDER_NAME);
$active = $saved->getParameter('active');
$value = idx($this->getActiveValues(), $active);
if ($value !== null) {
$query->withActive($value);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
$form->appendChild(
id(new AphrontFormSelectControl())
->setName('active')
->setLabel(pht('Show Products'))
->setValue($saved_query->getParameter('active'))
->setOptions($this->getActiveOptions()));
}
protected function getURI($path) {
return '/releeph/project/'.$path;
}
protected function getBuiltinQueryNames() {
return array(
'active' => pht('Active'),
'all' => pht('All'),
);
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'active':
return $query
->setParameter('active', 'active');
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
private function getActiveOptions() {
return array(
'all' => pht('Active and Inactive Products'),
'active' => pht('Active Products'),
'inactive' => pht('Inactive Products'),
);
}
private function getActiveValues() {
return array(
'all' => null,
'active' => 1,
'inactive' => 0,
);
}
protected function renderResultList(
array $products,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($products, 'ReleephProject');
$viewer = $this->requireViewer();
$list = id(new PHUIObjectItemListView())
->setUser($viewer);
foreach ($products as $product) {
$id = $product->getID();
$item = id(new PHUIObjectItemView())
->setHeader($product->getName())
->setHref($this->getApplicationURI("product/{$id}/"));
if (!$product->getIsActive()) {
$item->setDisabled(true);
$item->addIcon('none', pht('Inactive'));
}
$repo = $product->getRepository();
$item->addAttribute(
phutil_tag(
'a',
array(
'href' => $repo->getURI(),
),
$repo->getMonogram()));
$list->addItem($item);
}
$result = new PhabricatorApplicationSearchResultView();
$result->setObjectList($list);
return $result;
}
}

View file

@ -1,10 +0,0 @@
<?php
final class ReleephProductTransactionQuery
extends PhabricatorApplicationTransactionQuery {
public function getTemplateApplicationTransaction() {
return new ReleephProductTransaction();
}
}

View file

@ -1,247 +0,0 @@
<?php
final class ReleephRequestQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $requestedCommitPHIDs;
private $ids;
private $phids;
private $severities;
private $requestorPHIDs;
private $branchIDs;
private $requestedObjectPHIDs;
const STATUS_ALL = 'status-all';
const STATUS_OPEN = 'status-open';
const STATUS_REQUESTED = 'status-requested';
const STATUS_NEEDS_PULL = 'status-needs-pull';
const STATUS_REJECTED = 'status-rejected';
const STATUS_ABANDONED = 'status-abandoned';
const STATUS_PULLED = 'status-pulled';
const STATUS_NEEDS_REVERT = 'status-needs-revert';
const STATUS_REVERTED = 'status-reverted';
private $status = self::STATUS_ALL;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withBranchIDs(array $branch_ids) {
$this->branchIDs = $branch_ids;
return $this;
}
public function withStatus($status) {
$this->status = $status;
return $this;
}
public function withRequestedCommitPHIDs(array $requested_commit_phids) {
$this->requestedCommitPHIDs = $requested_commit_phids;
return $this;
}
public function withRequestorPHIDs(array $phids) {
$this->requestorPHIDs = $phids;
return $this;
}
public function withSeverities(array $severities) {
$this->severities = $severities;
return $this;
}
public function withRequestedObjectPHIDs(array $phids) {
$this->requestedObjectPHIDs = $phids;
return $this;
}
protected function loadPage() {
$table = new ReleephRequest();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
protected function willFilterPage(array $requests) {
// Load requested objects: you must be able to see an object to see
// requests for it.
$object_phids = mpull($requests, 'getRequestedObjectPHID');
$objects = id(new PhabricatorObjectQuery())
->setViewer($this->getViewer())
->setParentQuery($this)
->withPHIDs($object_phids)
->execute();
foreach ($requests as $key => $request) {
$object_phid = $request->getRequestedObjectPHID();
$object = idx($objects, $object_phid);
if (!$object) {
unset($requests[$key]);
continue;
}
$request->attachRequestedObject($object);
}
if ($this->severities) {
$severities = array_fuse($this->severities);
foreach ($requests as $key => $request) {
// NOTE: Facebook uses a custom field here.
if (ReleephDefaultFieldSelector::isFacebook()) {
$severity = $request->getDetail('severity');
} else {
$severity = $request->getDetail('releeph:severity');
}
if (empty($severities[$severity])) {
unset($requests[$key]);
}
}
}
$branch_ids = array_unique(mpull($requests, 'getBranchID'));
$branches = id(new ReleephBranchQuery())
->withIDs($branch_ids)
->setViewer($this->getViewer())
->execute();
$branches = mpull($branches, null, 'getID');
foreach ($requests as $key => $request) {
$branch = idx($branches, $request->getBranchID());
if (!$branch) {
unset($requests[$key]);
continue;
}
$request->attachBranch($branch);
}
// TODO: These should be serviced by the query, but are not currently
// denormalized anywhere. For now, filter them here instead. Note that
// we must perform this filtering *after* querying and attaching branches,
// because request status depends on the product.
$keep_status = array_fuse($this->getKeepStatusConstants());
if ($keep_status) {
foreach ($requests as $key => $request) {
if (empty($keep_status[$request->getStatus()])) {
unset($requests[$key]);
}
}
}
return $requests;
}
protected function buildWhereClause(AphrontDatabaseConnection $conn) {
$where = array();
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'phid IN (%Ls)',
$this->phids);
}
if ($this->branchIDs !== null) {
$where[] = qsprintf(
$conn,
'branchID IN (%Ld)',
$this->branchIDs);
}
if ($this->requestedCommitPHIDs !== null) {
$where[] = qsprintf(
$conn,
'requestCommitPHID IN (%Ls)',
$this->requestedCommitPHIDs);
}
if ($this->requestorPHIDs !== null) {
$where[] = qsprintf(
$conn,
'requestUserPHID IN (%Ls)',
$this->requestorPHIDs);
}
if ($this->requestedObjectPHIDs !== null) {
$where[] = qsprintf(
$conn,
'requestedObjectPHID IN (%Ls)',
$this->requestedObjectPHIDs);
}
$where[] = $this->buildPagingClause($conn);
return $this->formatWhereClause($conn, $where);
}
private function getKeepStatusConstants() {
switch ($this->status) {
case self::STATUS_ALL:
return array();
case self::STATUS_OPEN:
return array(
ReleephRequestStatus::STATUS_REQUESTED,
ReleephRequestStatus::STATUS_NEEDS_PICK,
ReleephRequestStatus::STATUS_NEEDS_REVERT,
);
case self::STATUS_REQUESTED:
return array(
ReleephRequestStatus::STATUS_REQUESTED,
);
case self::STATUS_NEEDS_PULL:
return array(
ReleephRequestStatus::STATUS_NEEDS_PICK,
);
case self::STATUS_REJECTED:
return array(
ReleephRequestStatus::STATUS_REJECTED,
);
case self::STATUS_ABANDONED:
return array(
ReleephRequestStatus::STATUS_ABANDONED,
);
case self::STATUS_PULLED:
return array(
ReleephRequestStatus::STATUS_PICKED,
);
case self::STATUS_NEEDS_REVERT:
return array(
ReleephRequestStatus::STATUS_NEEDS_REVERT,
);
case self::STATUS_REVERTED:
return array(
ReleephRequestStatus::STATUS_REVERTED,
);
default:
throw new Exception(pht("Unknown status '%s'!", $this->status));
}
}
public function getQueryApplicationClass() {
return 'PhabricatorReleephApplication';
}
}

View file

@ -1,225 +0,0 @@
<?php
final class ReleephRequestSearchEngine
extends PhabricatorApplicationSearchEngine {
private $branch;
private $baseURI;
public function getResultTypeDescription() {
return pht('Releeph Pull Requests');
}
public function getApplicationClassName() {
return 'PhabricatorReleephApplication';
}
public function canUseInPanelContext() {
return false;
}
public function setBranch(ReleephBranch $branch) {
$this->branch = $branch;
return $this;
}
public function getBranch() {
return $this->branch;
}
public function setBaseURI($base_uri) {
$this->baseURI = $base_uri;
return $this;
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter('status', $request->getStr('status'));
$saved->setParameter('severity', $request->getStr('severity'));
$saved->setParameter(
'requestorPHIDs',
$this->readUsersFromRequest($request, 'requestors'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new ReleephRequestQuery())
->withBranchIDs(array($this->getBranch()->getID()));
$status = $saved->getParameter('status');
$status = idx($this->getStatusValues(), $status);
if ($status) {
$query->withStatus($status);
}
$severity = $saved->getParameter('severity');
if ($severity) {
$query->withSeverities(array($severity));
}
$requestor_phids = $saved->getParameter('requestorPHIDs');
if ($requestor_phids) {
$query->withRequestorPHIDs($requestor_phids);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
$requestor_phids = $saved_query->getParameter('requestorPHIDs', array());
$form
->appendChild(
id(new AphrontFormSelectControl())
->setName('status')
->setLabel(pht('Status'))
->setValue($saved_query->getParameter('status'))
->setOptions($this->getStatusOptions()))
->appendChild(
id(new AphrontFormSelectControl())
->setName('severity')
->setLabel(pht('Severity'))
->setValue($saved_query->getParameter('severity'))
->setOptions($this->getSeverityOptions()))
->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('requestors')
->setLabel(pht('Requestors'))
->setValue($requestor_phids));
}
protected function getURI($path) {
return $this->baseURI.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'open' => pht('Open Requests'),
'all' => pht('All Requests'),
);
if ($this->requireViewer()->isLoggedIn()) {
$names['requested'] = pht('Requested');
}
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'open':
return $query->setParameter('status', 'open');
case 'all':
return $query;
case 'requested':
return $query->setParameter(
'requestorPHIDs',
array($this->requireViewer()->getPHID()));
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
private function getStatusOptions() {
return array(
'' => pht('(All Requests)'),
'open' => pht('Open Requests'),
'requested' => pht('Pull Requested'),
'needs-pull' => pht('Needs Pull'),
'rejected' => pht('Rejected'),
'abandoned' => pht('Abandoned'),
'pulled' => pht('Pulled'),
'needs-revert' => pht('Needs Revert'),
'reverted' => pht('Reverted'),
);
}
private function getStatusValues() {
return array(
'open' => ReleephRequestQuery::STATUS_OPEN,
'requested' => ReleephRequestQuery::STATUS_REQUESTED,
'needs-pull' => ReleephRequestQuery::STATUS_NEEDS_PULL,
'rejected' => ReleephRequestQuery::STATUS_REJECTED,
'abandoned' => ReleephRequestQuery::STATUS_ABANDONED,
'pulled' => ReleephRequestQuery::STATUS_PULLED,
'needs-revert' => ReleephRequestQuery::STATUS_NEEDS_REVERT,
'reverted' => ReleephRequestQuery::STATUS_REVERTED,
);
}
private function getSeverityOptions() {
if (ReleephDefaultFieldSelector::isFacebook()) {
return array(
'' => pht('(All Severities)'),
11 => pht('HOTFIX'),
12 => pht('PIGGYBACK'),
13 => pht('RELEASE'),
14 => pht('DAILY'),
15 => pht('PARKING'),
);
} else {
return array(
'' => pht('(All Severities)'),
ReleephSeverityFieldSpecification::HOTFIX => pht('Hotfix'),
ReleephSeverityFieldSpecification::RELEASE => pht('Release'),
);
}
}
protected function renderResultList(
array $requests,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($requests, 'ReleephRequest');
$viewer = $this->requireViewer();
// TODO: This is generally a bit sketchy, but we don't do this kind of
// thing elsewhere at the moment. For the moment it shouldn't be hugely
// costly, and we can batch things later. Generally, this commits fewer
// sins than the old code did.
$engine = id(new PhabricatorMarkupEngine())
->setViewer($viewer);
$list = array();
foreach ($requests as $pull) {
$field_list = PhabricatorCustomField::getObjectFields(
$pull,
PhabricatorCustomField::ROLE_VIEW);
$field_list
->setViewer($viewer)
->readFieldsFromStorage($pull);
foreach ($field_list->getFields() as $field) {
if ($field->shouldMarkup()) {
$field->setMarkupEngine($engine);
}
}
$list[] = id(new ReleephRequestView())
->setUser($viewer)
->setCustomFields($field_list)
->setPullRequest($pull)
->setIsListView(true);
}
// This is quite sketchy, but the list has not actually rendered yet, so
// this still allows us to batch the markup rendering.
$engine->process();
return id(new PhabricatorApplicationSearchResultView())
->setContent($list);
}
}

View file

@ -1,10 +0,0 @@
<?php
final class ReleephRequestTransactionQuery
extends PhabricatorApplicationTransactionQuery {
public function getTemplateApplicationTransaction() {
return new ReleephRequestTransaction();
}
}

View file

@ -1,188 +0,0 @@
<?php
final class ReleephBranch extends ReleephDAO
implements
PhabricatorApplicationTransactionInterface,
PhabricatorPolicyInterface {
protected $releephProjectID;
protected $isActive;
protected $createdByUserPHID;
// The immutable name of this branch ('releases/foo-2013.01.24')
protected $name;
protected $basename;
// The symbolic name of this branch (LATEST, PRODUCTION, RC, ...)
// See SYMBOLIC_NAME_NOTE below
protected $symbolicName;
// Where to cut the branch
protected $cutPointCommitPHID;
protected $details = array();
private $project = self::ATTACHABLE;
private $cutPointCommit = self::ATTACHABLE;
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'details' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'basename' => 'text64',
'isActive' => 'bool',
'symbolicName' => 'text64?',
'name' => 'text128',
),
self::CONFIG_KEY_SCHEMA => array(
'releephProjectID' => array(
'columns' => array('releephProjectID', 'symbolicName'),
'unique' => true,
),
'releephProjectID_2' => array(
'columns' => array('releephProjectID', 'basename'),
'unique' => true,
),
'releephProjectID_name' => array(
'columns' => array('releephProjectID', 'name'),
'unique' => true,
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(ReleephBranchPHIDType::TYPECONST);
}
public function getDetail($key, $default = null) {
return idx($this->getDetails(), $key, $default);
}
public function setDetail($key, $value) {
$this->details[$key] = $value;
return $this;
}
protected function willWriteData(array &$data) {
// If symbolicName is omitted, set it to the basename.
//
// This means that we can enforce symbolicName as a UNIQUE column in the
// DB. We'll interpret symbolicName === basename as meaning "no symbolic
// name".
//
// SYMBOLIC_NAME_NOTE
if (!$data['symbolicName']) {
$data['symbolicName'] = $data['basename'];
}
parent::willWriteData($data);
}
public function getSymbolicName() {
// See SYMBOLIC_NAME_NOTE above for why this is needed
if ($this->symbolicName == $this->getBasename()) {
return '';
}
return $this->symbolicName;
}
public function setSymbolicName($name) {
if ($name) {
parent::setSymbolicName($name);
} else {
parent::setSymbolicName($this->getBasename());
}
return $this;
}
public function getDisplayName() {
if ($sn = $this->getSymbolicName()) {
return $sn;
}
return $this->getBasename();
}
public function getDisplayNameWithDetail() {
$n = $this->getBasename();
if ($sn = $this->getSymbolicName()) {
return "{$sn} ({$n})";
} else {
return $n;
}
}
public function getURI($path = null) {
$components = array(
'/releeph/branch',
$this->getID(),
$path,
);
return implode('/', $components);
}
public function isActive() {
return $this->getIsActive();
}
public function attachProject(ReleephProject $project) {
$this->project = $project;
return $this;
}
public function getProject() {
return $this->assertAttached($this->project);
}
public function getProduct() {
return $this->getProject();
}
public function attachCutPointCommit(
PhabricatorRepositoryCommit $commit = null) {
$this->cutPointCommit = $commit;
return $this;
}
public function getCutPointCommit() {
return $this->assertAttached($this->cutPointCommit);
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new ReleephBranchEditor();
}
public function getApplicationTransactionTemplate() {
return new ReleephBranchTransaction();
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return $this->getProduct()->getCapabilities();
}
public function getPolicy($capability) {
return $this->getProduct()->getPolicy($capability);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getProduct()->hasAutomaticCapability($capability, $viewer);
}
public function describeAutomaticCapability($capability) {
return pht(
'Release branches have the same policies as the product they are a '.
'part of.');
}
}

View file

@ -1,14 +0,0 @@
<?php
final class ReleephBranchTransaction
extends PhabricatorApplicationTransaction {
public function getApplicationName() {
return 'releeph';
}
public function getApplicationTransactionType() {
return ReleephBranchPHIDType::TYPECONST;
}
}

View file

@ -1,9 +0,0 @@
<?php
abstract class ReleephDAO extends PhabricatorLiskDAO {
public function getApplicationName() {
return 'releeph';
}
}

View file

@ -1,108 +0,0 @@
<?php
final class ReleephProductTransaction
extends PhabricatorApplicationTransaction {
const TYPE_ACTIVE = 'releeph:product:active';
public function getApplicationName() {
return 'releeph';
}
public function getApplicationTransactionType() {
return ReleephProductPHIDType::TYPECONST;
}
public function getColor() {
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_ACTIVE:
if ($new) {
return 'green';
} else {
return 'black';
}
break;
}
return parent::getColor();
}
public function getIcon() {
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_ACTIVE:
if ($new) {
return 'fa-pencil';
} else {
return 'fa-times';
}
break;
}
return parent::getIcon();
}
public function getTitle() {
$author_phid = $this->getAuthorPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_ACTIVE:
if ($new) {
return pht(
'%s activated this product.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s deactivated this product.',
$this->renderHandleLink($author_phid));
}
break;
}
return parent::getTitle();
}
public function getTitleForFeed() {
$author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_ACTIVE:
if ($new) {
return pht(
'%s activated release product %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
} else {
return pht(
'%s deactivated release product %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
}
break;
}
return parent::getTitleForFeed();
}
public function getNoEffectDescription() {
switch ($this->getTransactionType()) {
case self::TYPE_ACTIVE:
return pht('The product is already in that state.');
}
return parent::getNoEffectDescription();
}
}

View file

@ -1,145 +0,0 @@
<?php
final class ReleephProject extends ReleephDAO
implements
PhabricatorApplicationTransactionInterface,
PhabricatorPolicyInterface {
const DEFAULT_BRANCH_NAMESPACE = 'releeph-releases';
const SYSTEM_AGENT_USERNAME_PREFIX = 'releeph-agent-';
protected $name;
// Specifying the place to pick from is a requirement for svn, though not
// for git. It's always useful though for reasoning about what revs have
// been picked and which haven't.
protected $trunkBranch;
protected $repositoryPHID;
protected $isActive;
protected $createdByUserPHID;
protected $details = array();
private $repository = self::ATTACHABLE;
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'details' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'name' => 'text128',
'trunkBranch' => 'text255',
'isActive' => 'bool',
),
self::CONFIG_KEY_SCHEMA => array(
'projectName' => array(
'columns' => array('name'),
'unique' => true,
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(ReleephProductPHIDType::TYPECONST);
}
public function getDetail($key, $default = null) {
return idx($this->details, $key, $default);
}
public function getURI($path = null) {
$components = array(
'/releeph/product',
$this->getID(),
$path,
);
return implode('/', $components);
}
public function setDetail($key, $value) {
$this->details[$key] = $value;
return $this;
}
public function getPushers() {
return $this->getDetail('pushers', array());
}
public function isPusher(PhabricatorUser $user) {
// TODO Deprecate this once `isPusher` is out of the Facebook codebase.
return $this->isAuthoritative($user);
}
public function isAuthoritative(PhabricatorUser $user) {
return $this->isAuthoritativePHID($user->getPHID());
}
public function isAuthoritativePHID($phid) {
$pushers = $this->getPushers();
if (!$pushers) {
return true;
} else {
return in_array($phid, $pushers);
}
}
public function attachRepository(PhabricatorRepository $repository) {
$this->repository = $repository;
return $this;
}
public function getRepository() {
return $this->assertAttached($this->repository);
}
public function getReleephFieldSelector() {
return new ReleephDefaultFieldSelector();
}
public function isTestFile($filename) {
$test_paths = $this->getDetail('testPaths', array());
foreach ($test_paths as $test_path) {
if (preg_match($test_path, $filename)) {
return true;
}
}
return false;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new ReleephProductEditor();
}
public function getApplicationTransactionTemplate() {
return new ReleephProductTransaction();
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
return PhabricatorPolicies::POLICY_USER;
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
}

View file

@ -1,354 +0,0 @@
<?php
final class ReleephRequest extends ReleephDAO
implements
PhabricatorApplicationTransactionInterface,
PhabricatorPolicyInterface,
PhabricatorCustomFieldInterface {
protected $branchID;
protected $requestUserPHID;
protected $details = array();
protected $userIntents = array();
protected $inBranch;
protected $pickStatus;
protected $mailKey;
/**
* The object which is being requested. Normally this is a commit, but it
* might also be a revision. In the future, it could be a repository branch
* or an external object (like a GitHub pull request).
*/
protected $requestedObjectPHID;
// Information about the thing being requested
protected $requestCommitPHID;
// Information about the last commit to the releeph branch
protected $commitIdentifier;
protected $commitPHID;
private $customFields = self::ATTACHABLE;
private $branch = self::ATTACHABLE;
private $requestedObject = self::ATTACHABLE;
/* -( Constants and helper methods )--------------------------------------- */
const INTENT_WANT = 'want';
const INTENT_PASS = 'pass';
const PICK_PENDING = 1; // old
const PICK_FAILED = 2;
const PICK_OK = 3;
const PICK_MANUAL = 4; // old
const REVERT_OK = 5;
const REVERT_FAILED = 6;
public function shouldBeInBranch() {
return
$this->getPusherIntent() == self::INTENT_WANT &&
/**
* We use "!= pass" instead of "== want" in case the requestor intent is
* not present. In other words, only revert if the requestor explicitly
* passed.
*/
$this->getRequestorIntent() != self::INTENT_PASS;
}
/**
* Will return INTENT_WANT if any pusher wants this request, and no pusher
* passes on this request.
*/
public function getPusherIntent() {
$product = $this->getBranch()->getProduct();
if (!$product->getPushers()) {
return self::INTENT_WANT;
}
$found_pusher_want = false;
foreach ($this->userIntents as $phid => $intent) {
if ($product->isAuthoritativePHID($phid)) {
if ($intent == self::INTENT_PASS) {
return self::INTENT_PASS;
}
$found_pusher_want = true;
}
}
if ($found_pusher_want) {
return self::INTENT_WANT;
} else {
return null;
}
}
public function getRequestorIntent() {
return idx($this->userIntents, $this->requestUserPHID);
}
public function getStatus() {
return $this->calculateStatus();
}
public function getMonogram() {
return 'Y'.$this->getID();
}
public function getBranch() {
return $this->assertAttached($this->branch);
}
public function attachBranch(ReleephBranch $branch) {
$this->branch = $branch;
return $this;
}
public function getRequestedObject() {
return $this->assertAttached($this->requestedObject);
}
public function attachRequestedObject($object) {
$this->requestedObject = $object;
return $this;
}
private function calculateStatus() {
if ($this->shouldBeInBranch()) {
if ($this->getInBranch()) {
return ReleephRequestStatus::STATUS_PICKED;
} else {
return ReleephRequestStatus::STATUS_NEEDS_PICK;
}
} else {
if ($this->getInBranch()) {
return ReleephRequestStatus::STATUS_NEEDS_REVERT;
} else {
$intent_pass = self::INTENT_PASS;
$intent_want = self::INTENT_WANT;
$has_been_in_branch = $this->getCommitIdentifier();
// Regardless of why we reverted something, always say reverted if it
// was once in the branch.
if ($has_been_in_branch) {
return ReleephRequestStatus::STATUS_REVERTED;
} else if ($this->getPusherIntent() === $intent_pass) {
// Otherwise, if it has never been in the branch, explicitly say why:
return ReleephRequestStatus::STATUS_REJECTED;
} else if ($this->getRequestorIntent() === $intent_want) {
return ReleephRequestStatus::STATUS_REQUESTED;
} else {
return ReleephRequestStatus::STATUS_ABANDONED;
}
}
}
}
/* -( Lisk mechanics )----------------------------------------------------- */
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'details' => self::SERIALIZATION_JSON,
'userIntents' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'requestCommitPHID' => 'phid?',
'commitIdentifier' => 'text40?',
'commitPHID' => 'phid?',
'pickStatus' => 'uint32?',
'inBranch' => 'bool',
'mailKey' => 'bytes20',
'userIntents' => 'text?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_phid' => null,
'phid' => array(
'columns' => array('phid'),
'unique' => true,
),
'requestIdentifierBranch' => array(
'columns' => array('requestCommitPHID', 'branchID'),
'unique' => true,
),
'branchID' => array(
'columns' => array('branchID'),
),
'key_requestedObject' => array(
'columns' => array('requestedObjectPHID'),
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
ReleephRequestPHIDType::TYPECONST);
}
public function save() {
if (!$this->getMailKey()) {
$this->setMailKey(Filesystem::readRandomCharacters(20));
}
return parent::save();
}
/* -( Helpful accessors )--------------------------------------------------- */
public function getDetail($key, $default = null) {
return idx($this->getDetails(), $key, $default);
}
public function setDetail($key, $value) {
$this->details[$key] = $value;
return $this;
}
/**
* Get the commit PHIDs this request is requesting.
*
* NOTE: For now, this always returns one PHID.
*
* @return list<phid> Commit PHIDs requested by this request.
*/
public function getCommitPHIDs() {
return array(
$this->requestCommitPHID,
);
}
public function getReason() {
// Backward compatibility: reason used to be called comments
$reason = $this->getDetail('reason');
if (!$reason) {
return $this->getDetail('comments');
}
return $reason;
}
/**
* Allow a null summary, and fall back to the title of the commit.
*/
public function getSummaryForDisplay() {
$summary = $this->getDetail('summary');
if (!strlen($summary)) {
$commit = $this->loadPhabricatorRepositoryCommit();
if ($commit) {
$summary = $commit->getSummary();
}
}
if (!strlen($summary)) {
$summary = pht('None');
}
return $summary;
}
/* -( Loading external objects )------------------------------------------- */
public function loadPhabricatorRepositoryCommit() {
return id(new PhabricatorRepositoryCommit())->loadOneWhere(
'phid = %s',
$this->getRequestCommitPHID());
}
public function loadPhabricatorRepositoryCommitData() {
$commit = $this->loadPhabricatorRepositoryCommit();
if ($commit) {
return id(new PhabricatorRepositoryCommitData())->loadOneWhere(
'commitID = %d',
$commit->getID());
}
}
/* -( State change helpers )----------------------------------------------- */
public function setUserIntent(PhabricatorUser $user, $intent) {
$this->userIntents[$user->getPHID()] = $intent;
return $this;
}
/* -( Migrating to status-less ReleephRequests )--------------------------- */
protected function didReadData() {
if ($this->userIntents === null) {
$this->userIntents = array();
}
}
public function setStatus($value) {
throw new Exception(pht('`%s` is now deprecated!', 'status'));
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new ReleephRequestTransactionalEditor();
}
public function getApplicationTransactionTemplate() {
return new ReleephRequestTransaction();
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
return $this->getBranch()->getPolicy($capability);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getBranch()->hasAutomaticCapability($capability, $viewer);
}
public function describeAutomaticCapability($capability) {
return pht(
'Pull requests have the same policies as the branches they are '.
'requested against.');
}
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
public function getCustomFieldSpecificationForRole($role) {
return PhabricatorEnv::getEnvConfig('releeph.fields');
}
public function getCustomFieldBaseClass() {
return 'ReleephFieldSpecification';
}
public function getCustomFields() {
return $this->assertAttached($this->customFields);
}
public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
$this->customFields = $fields;
return $this;
}
}

View file

@ -1,275 +0,0 @@
<?php
final class ReleephRequestTransaction
extends PhabricatorApplicationTransaction {
const TYPE_REQUEST = 'releeph:request';
const TYPE_USER_INTENT = 'releeph:user_intent';
const TYPE_EDIT_FIELD = 'releeph:edit_field';
const TYPE_PICK_STATUS = 'releeph:pick_status';
const TYPE_COMMIT = 'releeph:commit';
const TYPE_DISCOVERY = 'releeph:discovery';
const TYPE_MANUAL_IN_BRANCH = 'releeph:manual';
public function getApplicationName() {
return 'releeph';
}
public function getApplicationTransactionType() {
return ReleephRequestPHIDType::TYPECONST;
}
public function getApplicationTransactionCommentObject() {
return new ReleephRequestTransactionComment();
}
public function hasChangeDetails() {
switch ($this->getTransactionType()) {
default;
break;
}
return parent::hasChangeDetails();
}
public function getRequiredHandlePHIDs() {
$phids = parent::getRequiredHandlePHIDs();
$phids[] = $this->getObjectPHID();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_REQUEST:
case self::TYPE_DISCOVERY:
$phids[] = $new;
break;
case self::TYPE_EDIT_FIELD:
self::searchForPHIDs($this->getOldValue(), $phids);
self::searchForPHIDs($this->getNewValue(), $phids);
break;
}
return $phids;
}
public function getTitle() {
$author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_REQUEST:
return pht(
'%s requested %s',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($new));
break;
case self::TYPE_USER_INTENT:
return $this->getIntentTitle();
break;
case self::TYPE_EDIT_FIELD:
$field = newv($this->getMetadataValue('fieldClass'), array());
$name = $field->getName();
$markup = $name;
if ($this->getRenderingTarget() ===
parent::TARGET_HTML) {
$markup = hsprintf('<em>%s</em>', $name);
}
return pht(
'%s changed the %s to "%s"',
$this->renderHandleLink($author_phid),
$markup,
$field->normalizeForTransactionView($this, $new));
break;
case self::TYPE_PICK_STATUS:
switch ($new) {
case ReleephRequest::PICK_OK:
return pht('%s found this request picks without error',
$this->renderHandleLink($author_phid));
case ReleephRequest::REVERT_OK:
return pht('%s found this request reverts without error',
$this->renderHandleLink($author_phid));
case ReleephRequest::PICK_FAILED:
return pht("%s couldn't pick this request",
$this->renderHandleLink($author_phid));
case ReleephRequest::REVERT_FAILED:
return pht("%s couldn't revert this request",
$this->renderHandleLink($author_phid));
}
break;
case self::TYPE_COMMIT:
$action_type = $this->getMetadataValue('action');
switch ($action_type) {
case 'pick':
return pht(
'%s picked this request and committed the result upstream',
$this->renderHandleLink($author_phid));
break;
case 'revert':
return pht(
'%s reverted this request and committed the result upstream',
$this->renderHandleLink($author_phid));
break;
}
break;
case self::TYPE_MANUAL_IN_BRANCH:
$action = $new ? pht('picked') : pht('reverted');
return pht(
'%s marked this request as manually %s',
$this->renderHandleLink($author_phid),
$action);
break;
case self::TYPE_DISCOVERY:
return pht('%s discovered this commit as %s',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($new));
break;
default:
return parent::getTitle();
break;
}
}
public function getActionName() {
switch ($this->getTransactionType()) {
case self::TYPE_REQUEST:
return pht('Requested');
case self::TYPE_COMMIT:
$action_type = $this->getMetadataValue('action');
switch ($action_type) {
case 'pick':
return pht('Picked');
case 'revert':
return pht('Reverted');
}
}
return parent::getActionName();
}
public function getColor() {
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_USER_INTENT:
switch ($new) {
case ReleephRequest::INTENT_WANT:
return PhabricatorTransactions::COLOR_GREEN;
case ReleephRequest::INTENT_PASS:
return PhabricatorTransactions::COLOR_RED;
}
}
return parent::getColor();
}
private static function searchForPHIDs($thing, array &$phids) {
/**
* To implement something like getRequiredHandlePHIDs() in a
* ReleephFieldSpecification, we'd have to provide the field with its
* ReleephRequest (so that it could load the PHIDs from the
* ReleephRequest's storage, and return them.)
*
* We don't have fields initialized with their ReleephRequests, but we can
* make a good guess at what handles will be needed for rendering the field
* in this transaction by inspecting the old and new values.
*/
if (!is_array($thing)) {
$thing = array($thing);
}
foreach ($thing as $value) {
if (phid_get_type($value) !==
PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN) {
$phids[] = $value;
}
}
}
private function getIntentTitle() {
$author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID();
$new = $this->getNewValue();
$is_pusher = $this->getMetadataValue('isPusher');
switch ($new) {
case ReleephRequest::INTENT_WANT:
if ($is_pusher) {
return pht(
'%s approved this request',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s wanted this request',
$this->renderHandleLink($author_phid));
}
case ReleephRequest::INTENT_PASS:
if ($is_pusher) {
return pht(
'%s rejected this request',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s passed on this request',
$this->renderHandleLink($author_phid));
}
}
}
public function shouldHide() {
$type = $this->getTransactionType();
if ($type === self::TYPE_USER_INTENT &&
$this->getMetadataValue('isRQCreate')) {
return true;
}
if ($this->isBoringPickStatus()) {
return true;
}
// ReleephSummaryFieldSpecification is usually blank when an RQ is created,
// creating a transaction change from null to "". Hide these!
if ($type === self::TYPE_EDIT_FIELD) {
if ($this->getOldValue() === null && $this->getNewValue() === '') {
return true;
}
}
return parent::shouldHide();
}
public function isBoringPickStatus() {
$type = $this->getTransactionType();
if ($type === self::TYPE_PICK_STATUS) {
$new = $this->getNewValue();
if ($new === ReleephRequest::PICK_OK ||
$new === ReleephRequest::REVERT_OK) {
return true;
}
}
return false;
}
}

View file

@ -1,10 +0,0 @@
<?php
final class ReleephRequestTransactionComment
extends PhabricatorApplicationTransactionComment {
public function getApplicationTransactionObject() {
return new ReleephRequestTransaction();
}
}

View file

@ -1,245 +0,0 @@
<?php
final class ReleephRequestView extends AphrontView {
private $pullRequest;
private $customFields;
private $isListView;
public function setIsListView($is_list_view) {
$this->isListView = $is_list_view;
return $this;
}
public function getIsListView() {
return $this->isListView;
}
public function setCustomFields(PhabricatorCustomFieldList $custom_fields) {
$this->customFields = $custom_fields;
return $this;
}
public function getCustomFields() {
return $this->customFields;
}
public function setPullRequest(ReleephRequest $pull_request) {
$this->pullRequest = $pull_request;
return $this;
}
public function getPullRequest() {
return $this->pullRequest;
}
public function render() {
$viewer = $this->getUser();
$field_list = $this->getCustomFields();
$pull = $this->getPullRequest();
$header = $this->buildHeader($pull);
$action_list = $this->buildActionList($pull);
$property_list = id(new PHUIPropertyListView())
->setUser($viewer)
->setActionList($action_list);
$field_list->appendFieldsToPropertyList(
$pull,
$viewer,
$property_list);
$warnings = $this->getWarnings($pull);
if ($this->getIsListView()) {
Javelin::initBehavior('releeph-request-state-change');
}
return id(new PHUIObjectBoxView())
->setHeader($header)
->setFormErrors($warnings)
->addSigil('releeph-request-box')
->setMetadata(array('uri' => '/'.$pull->getMonogram()))
->appendChild($property_list);
}
private function buildHeader(ReleephRequest $pull) {
$header_text = $pull->getSummaryForDisplay();
if ($this->getIsListView()) {
$header_text = phutil_tag(
'a',
array(
'href' => '/'.$pull->getMonogram(),
),
$header_text);
}
$header = id(new PHUIHeaderView())
->setHeader($header_text)
->setUser($this->getUser())
->setPolicyObject($pull);
switch ($pull->getStatus()) {
case ReleephRequestStatus::STATUS_REQUESTED:
$icon = 'open';
$color = null;
break;
case ReleephRequestStatus::STATUS_REJECTED:
$icon = 'reject';
$color = 'red';
break;
case ReleephRequestStatus::STATUS_PICKED:
$icon = 'accept';
$color = 'green';
break;
case ReleephRequestStatus::STATUS_REVERTED:
case ReleephRequestStatus::STATUS_ABANDONED:
$icon = 'reject';
$color = 'dark';
break;
case ReleephRequestStatus::STATUS_NEEDS_PICK:
$icon = 'warning';
$color = 'green';
break;
case ReleephRequestStatus::STATUS_NEEDS_REVERT:
$icon = 'warning';
$color = 'red';
break;
default:
$icon = 'question';
$color = null;
break;
}
$text = ReleephRequestStatus::getStatusDescriptionFor($pull->getStatus());
$header->setStatus($icon, $color, $text);
return $header;
}
private function buildActionList(ReleephRequest $pull) {
$viewer = $this->getUser();
$id = $pull->getID();
$edit_uri = '/releeph/request/edit/'.$id.'/';
$view = id(new PhabricatorActionListView())
->setUser($viewer);
$product = $pull->getBranch()->getProduct();
$viewer_is_pusher = $product->isAuthoritativePHID($viewer->getPHID());
$viewer_is_requestor = ($pull->getRequestUserPHID() == $viewer->getPHID());
if ($viewer_is_pusher) {
$yes_text = pht('Approve Pull');
$no_text = pht('Reject Pull');
$yes_icon = 'fa-check';
$no_icon = 'fa-times';
} else if ($viewer_is_requestor) {
$yes_text = pht('Request Pull');
$no_text = pht('Cancel Pull');
$yes_icon = 'fa-check';
$no_icon = 'fa-times';
} else {
$yes_text = pht('Support Pull');
$no_text = pht('Discourage Pull');
$yes_icon = 'fa-thumbs-o-up';
$no_icon = 'fa-thumbs-o-down';
}
$yes_href = '/releeph/request/action/want/'.$id.'/';
$no_href = '/releeph/request/action/pass/'.$id.'/';
$intents = $pull->getUserIntents();
$current_intent = idx($intents, $viewer->getPHID());
$yes_disabled = ($current_intent == ReleephRequest::INTENT_WANT);
$no_disabled = ($current_intent == ReleephRequest::INTENT_PASS);
$use_workflow = (!$this->getIsListView());
$view->addAction(
id(new PhabricatorActionView())
->setName($yes_text)
->setHref($yes_href)
->setWorkflow($use_workflow)
->setRenderAsForm($use_workflow)
->setDisabled($yes_disabled)
->addSigil('releeph-request-state-change')
->addSigil('want')
->setIcon($yes_icon));
$view->addAction(
id(new PhabricatorActionView())
->setName($no_text)
->setHref($no_href)
->setWorkflow($use_workflow)
->setRenderAsForm($use_workflow)
->setDisabled($no_disabled)
->addSigil('releeph-request-state-change')
->addSigil('pass')
->setIcon($no_icon));
if ($viewer_is_pusher || $viewer_is_requestor) {
$pulled_href = '/releeph/request/action/mark-manually-picked/'.$id.'/';
$revert_href = '/releeph/request/action/mark-manually-reverted/'.$id.'/';
if ($pull->getInBranch()) {
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Mark as Reverted'))
->setHref($revert_href)
->setWorkflow($use_workflow)
->setRenderAsForm($use_workflow)
->addSigil('releeph-request-state-change')
->addSigil('mark-manually-reverted')
->setIcon($no_icon));
} else {
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Mark as Pulled'))
->setHref($pulled_href)
->setWorkflow($use_workflow)
->setRenderAsForm($use_workflow)
->addSigil('releeph-request-state-change')
->addSigil('mark-manually-picked')
->setIcon('fa-exclamation-triangle'));
}
}
if (!$this->getIsListView()) {
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Pull Request'))
->setIcon('fa-pencil')
->setHref($edit_uri));
}
return $view;
}
private function getWarnings(ReleephRequest $pull) {
$warnings = array();
switch ($pull->getStatus()) {
case ReleephRequestStatus::STATUS_NEEDS_PICK:
if ($pull->getPickStatus() == ReleephRequest::PICK_FAILED) {
$warnings[] = pht('Last pull failed!');
}
break;
case ReleephRequestStatus::STATUS_NEEDS_REVERT:
if ($pull->getPickStatus() == ReleephRequest::REVERT_FAILED) {
$warnings[] = pht('Last revert failed!');
}
break;
}
return $warnings;
}
}

View file

@ -1,62 +0,0 @@
<?php
final class ReleephBranchPreviewView extends AphrontFormControl {
private $statics = array();
private $dynamics = array();
public function addControl($param_name, AphrontFormControl $control) {
$celerity_id = celerity_generate_unique_node_id();
$control->setID($celerity_id);
$this->dynamics[$param_name] = $celerity_id;
return $this;
}
public function addStatic($param_name, $value) {
$this->statics[$param_name] = $value;
return $this;
}
protected function getCustomControlClass() {
require_celerity_resource('releeph-preview-branch');
return 'releeph-preview-branch';
}
protected function renderInput() {
static $required_params = array(
'repositoryPHID',
'projectName',
'isSymbolic',
'template',
);
$all_params = array_merge($this->statics, $this->dynamics);
foreach ($required_params as $param_name) {
if (idx($all_params, $param_name) === null) {
throw new Exception(
pht(
"'%s' is not set as either a static or dynamic!",
$param_name));
}
}
$output_id = celerity_generate_unique_node_id();
Javelin::initBehavior('releeph-preview-branch', array(
'uri' => '/releeph/branch/preview/',
'outputID' => $output_id,
'params' => array(
'static' => $this->statics,
'dynamic' => $this->dynamics,
),
));
return phutil_tag(
'div',
array(
'id' => $output_id,
),
'');
}
}

View file

@ -1,195 +0,0 @@
<?php
final class ReleephBranchTemplate extends Phobject {
const KEY = 'releeph.default-branch-template';
private $commitHandle;
private $branchDate = null;
private $projectName;
private $isSymbolic;
public static function getDefaultTemplate() {
return PhabricatorEnv::getEnvConfig(self::KEY);
}
public static function getRequiredDefaultTemplate() {
$template = self::getDefaultTemplate();
if (!$template) {
throw new Exception(pht(
"Config setting '%s' must be set, ".
"or you must provide a branch-template for each project!",
self::KEY));
}
return $template;
}
public static function getFakeCommitHandleFor(
$repository_phid,
PhabricatorUser $viewer) {
$repository = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->withPHIDs(array($repository_phid))
->executeOne();
$fake_handle = 'SOFAKE';
if ($repository) {
$fake_handle = id(new PhabricatorObjectHandle())
->setName($repository->formatCommitName('100000000000'));
}
return $fake_handle;
}
public function setCommitHandle(PhabricatorObjectHandle $handle) {
$this->commitHandle = $handle;
return $this;
}
public function setBranchDate($branch_date) {
$this->branchDate = $branch_date;
return $this;
}
public function setReleephProjectName($project_name) {
$this->projectName = $project_name;
return $this;
}
public function setSymbolic($is_symbolic) {
$this->isSymbolic = $is_symbolic;
return $this;
}
public function interpolate($template) {
if (!$this->projectName) {
return array('', array());
}
list($name, $name_errors) = $this->interpolateInner(
$template,
$this->isSymbolic);
if ($this->isSymbolic) {
return array($name, $name_errors);
} else {
$validate_errors = $this->validateAsBranchName($name);
$errors = array_merge($name_errors, $validate_errors);
return array($name, $errors);
}
}
/*
* xsprintf() would be useful here, but that's for formatting concrete lists
* of things in a certain way...
*
* animal_printf('%A %A %A', $dog1, $dog2, $dog3);
*
* ...rather than interpolating percent-control-strings like strftime does.
*/
private function interpolateInner($template, $is_symbolic) {
$name = $template;
$errors = array();
$safe_project_name = str_replace(' ', '-', $this->projectName);
$short_commit_id = last(
preg_split('/r[A-Z]+/', $this->commitHandle->getName()));
$interpolations = array();
for ($ii = 0; $ii < strlen($name); $ii++) {
$char = substr($name, $ii, 1);
$prev = null;
if ($ii > 0) {
$prev = substr($name, $ii - 1, 1);
}
$next = substr($name, $ii + 1, 1);
if ($next && $char == '%' && $prev != '%') {
$interpolations[$ii] = $next;
}
}
$variable_interpolations = array();
$reverse_interpolations = $interpolations;
krsort($reverse_interpolations);
if ($this->branchDate) {
$branch_date = $this->branchDate;
} else {
$branch_date = $this->commitHandle->getTimestamp();
}
foreach ($reverse_interpolations as $position => $code) {
$replacement = null;
switch ($code) {
case 'v':
$replacement = $this->commitHandle->getName();
$is_variable = true;
break;
case 'V':
$replacement = $short_commit_id;
$is_variable = true;
break;
case 'P':
$replacement = $safe_project_name;
$is_variable = false;
break;
case 'p':
$replacement = strtolower($safe_project_name);
$is_variable = false;
break;
default:
// Format anything else using strftime()
$replacement = strftime("%{$code}", $branch_date);
$is_variable = true;
break;
}
if ($is_variable) {
$variable_interpolations[] = $code;
}
$name = substr_replace($name, $replacement, $position, 2);
}
if (!$is_symbolic && !$variable_interpolations) {
$errors[] = pht("Include additional interpolations that aren't static!");
}
return array($name, $errors);
}
private function validateAsBranchName($name) {
$errors = array();
if (preg_match('{^/}', $name) || preg_match('{/$}', $name)) {
$errors[] = pht("Branches cannot begin or end with '%s'", '/');
}
if (preg_match('{//+}', $name)) {
$errors[] = pht("Branches cannot contain multiple consecutive '%s'", '/');
}
$parts = array_filter(explode('/', $name));
foreach ($parts as $index => $part) {
$part_error = null;
if (preg_match('{^\.}', $part) || preg_match('{\.$}', $part)) {
$errors[] = pht("Path components cannot begin or end with '%s'", '.');
} else if (preg_match('{^(?!\w)}', $part)) {
$errors[] = pht('Path components must begin with an alphanumeric.');
} else if (!preg_match('{^\w ([\w-_%\.]* [\w-_%])?$}x', $part)) {
$errors[] = pht(
"Path components may only contain alphanumerics ".
"or '%s', '%s' or '%s'.",
'-',
'_',
'.');
}
}
return $errors;
}
}

View file

@ -1,60 +0,0 @@
<?php
final class ReleephRequestTypeaheadControl extends AphrontFormControl {
const PLACEHOLDER = 'Type a commit id or first line of commit message...';
private $repo;
private $startTime;
public function setRepo(PhabricatorRepository $repo) {
$this->repo = $repo;
return $this;
}
public function setStartTime($epoch) {
$this->startTime = $epoch;
return $this;
}
protected function getCustomControlClass() {
return 'releeph-request-typeahead';
}
protected function renderInput() {
$id = celerity_generate_unique_node_id();
$div = phutil_tag(
'div',
array(
'style' => 'position: relative;',
'id' => $id,
),
phutil_tag(
'input',
array(
'autocomplete' => 'off',
'type' => 'text',
'name' => $this->getName(),
),
''));
require_celerity_resource('releeph-request-typeahead-css');
Javelin::initBehavior('releeph-request-typeahead', array(
'id' => $id,
'src' => '/releeph/request/typeahead/',
'placeholder' => self::PLACEHOLDER,
'value' => $this->getValue(),
'aux' => array(
'repo' => $this->repo->getID(),
'callsign' => $this->repo->getCallsign(),
'since' => $this->startTime,
'limit' => 16,
),
));
return $div;
}
}

View file

@ -272,10 +272,6 @@
"name": "Projects", "name": "Projects",
"include": "(^src/applications/project/)" "include": "(^src/applications/project/)"
}, },
"releeph": {
"name": "Releeph",
"include": "(^src/applications/releeph/)"
},
"remarkup": { "remarkup": {
"name": "Remarkup", "name": "Remarkup",
"include": [ "include": [

View file

@ -93,7 +93,6 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
'db.conpherence' => array(), 'db.conpherence' => array(),
'db.config' => array(), 'db.config' => array(),
'db.token' => array(), 'db.token' => array(),
'db.releeph' => array(),
'db.phlux' => array(), 'db.phlux' => array(),
'db.phortune' => array(), 'db.phortune' => array(),
'db.phrequent' => array(), 'db.phrequent' => array(),

View file

@ -1,45 +0,0 @@
/**
* @provides releeph-core
*/
/* Colors: match differential colors */
.releeph-request-comment {
border-color: #ddd;
}
.releeph-request-comment-pusher {
background: #8DEE8D;
border-color: #096;
}
.releeph-request-comment-pusher div {
background: #8DEE8D;
}
/* The diff size bar */
.diff-bar {
border: 0px;
}
.diff-bar div {
width: 100px;
border: 1px solid;
border-top-color: #A4A4A4;
border-right-color: #BBB;
border-bottom-color: #D5D5D5;
border-left-color: #BBB;
background: white;
float: left;
margin-right: 1em;
}
.diff-bar div div {
height: 10px;
}
.diff-bar span {
color: #555;
}

View file

@ -1,30 +0,0 @@
/**
* @provides releeph-preview-branch
*/
.releeph-preview-branch {
min-height: 4em;
position: relative;
}
.releeph-preview-branch .error {
padding-left: 22px;
background-repeat: no-repeat;
background-size: 16px auto;
float: left;
position: absolute;
top: 2.5em;
/* TODO: This had a background that's still at Facebook? */
}
.releeph-preview-branch .name {
clear: both;
float: left;
position: absolute;
font-family: monospace;
font-size: 9pt !important;
background: white;
top: 0.7em;
padding: 2px;
}

View file

@ -1,17 +0,0 @@
/**
* @provides releeph-request-differential-create-dialog
*/
.releeph-request-differential-create-dialog h1 {
color: gray;
font-style: italic;
font-size: 16px;
margin-top: 0.8em;
}
.releeph-request-differential-create-dialog a {
font-weight: bold;
margin-left: 2em;
display: block;
margin-top: 1em;
}

View file

@ -1,27 +0,0 @@
/**
* @provides releeph-request-typeahead-css
*/
.releeph-request-typeahead .commit-id {
color: #aaf; /* blue... */
font-family: monospace;
font-size: 100%;
display: block;
float: left;
}
.releeph-request-typeahead .author-info {
color: #080; /* ...and green, for search results! */
text-align: right;
display: block;
float: right;
padding-left: 1em;
}
.releeph-request-typeahead .focused .author-info {
color: #8b8;
}
.releeph-request-typeahead .summary {
clear: both;
}

Some files were not shown because too many files have changed in this diff Show more