Page MenuHomePhabricator

D19443.id46508.diff
No OneTemporary

D19443.id46508.diff

diff --git a/resources/sql/autopatches/20180430.repo_identity.sql b/resources/sql/autopatches/20180430.repo_identity.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20180430.repo_identity.sql
@@ -0,0 +1,14 @@
+CREATE TABLE {$NAMESPACE}_repository.repository_identity (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARBINARY(64) NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ automaticGuessedUserPHID VARBINARY(64) DEFAULT NULL,
+ manuallySetUserPHID VARBINARY(64) DEFAULT NULL,
+ currentEffectiveUserPHID VARBINARY(64) DEFAULT NULL,
+ identityNameHash BINARY(12) NOT NULL,
+ identityNameRaw LONGBLOB NOT NULL,
+ identityNameEncoding VARCHAR(16) DEFAULT NULL COLLATE {$COLLATE_TEXT},
+ UNIQUE KEY `key_phid` (phid),
+ UNIQUE KEY `key_identity` (identityNameHash)
+) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT};
diff --git a/resources/sql/autopatches/20180504.repo_identity.author.sql b/resources/sql/autopatches/20180504.repo_identity.author.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20180504.repo_identity.author.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_repository.repository_identity
+ ADD COLUMN authorPHID VARBINARY(64) NOT NULL;
diff --git a/resources/sql/autopatches/20180504.repo_identity.xaction.sql b/resources/sql/autopatches/20180504.repo_identity.xaction.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20180504.repo_identity.xaction.sql
@@ -0,0 +1,19 @@
+CREATE TABLE {$NAMESPACE}_repository.repository_identitytransaction (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ 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 UNSIGNED NOT NULL,
+ transactionType VARCHAR(32) NOT NULL,
+ oldValue LONGTEXT NOT NULL,
+ newValue LONGTEXT NOT NULL,
+ contentSource LONGTEXT NOT NULL,
+ metadata LONGTEXT NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ UNIQUE KEY `key_phid` (`phid`),
+ KEY `key_object` (`objectPHID`)
+) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE {$COLLATE_TEXT};
diff --git a/resources/sql/autopatches/20180509.repo_identity.commits.sql b/resources/sql/autopatches/20180509.repo_identity.commits.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20180509.repo_identity.commits.sql
@@ -0,0 +1,3 @@
+ALTER TABLE {$NAMESPACE}_repository.repository_commit
+ ADD COLUMN authorIdentityPHID VARBINARY(64) DEFAULT NULL,
+ ADD COLUMN committerIdentityPHID VARBINARY(64) DEFAULT NULL;
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -814,6 +814,13 @@
'DiffusionHistoryTableView' => 'applications/diffusion/view/DiffusionHistoryTableView.php',
'DiffusionHistoryView' => 'applications/diffusion/view/DiffusionHistoryView.php',
'DiffusionHovercardEngineExtension' => 'applications/diffusion/engineextension/DiffusionHovercardEngineExtension.php',
+ 'DiffusionIdentityAssigneeDatasource' => 'applications/diffusion/typeahead/DiffusionIdentityAssigneeDatasource.php',
+ 'DiffusionIdentityAssigneeEditField' => 'applications/diffusion/editfield/DiffusionIdentityAssigneeEditField.php',
+ 'DiffusionIdentityAssigneeSearchField' => 'applications/diffusion/searchfield/DiffusionIdentityAssigneeSearchField.php',
+ 'DiffusionIdentityEditController' => 'applications/diffusion/controller/DiffusionIdentityEditController.php',
+ 'DiffusionIdentityListController' => 'applications/diffusion/controller/DiffusionIdentityListController.php',
+ 'DiffusionIdentityUnassignedDatasource' => 'applications/diffusion/typeahead/DiffusionIdentityUnassignedDatasource.php',
+ 'DiffusionIdentityViewController' => 'applications/diffusion/controller/DiffusionIdentityViewController.php',
'DiffusionInlineCommentController' => 'applications/diffusion/controller/DiffusionInlineCommentController.php',
'DiffusionInlineCommentPreviewController' => 'applications/diffusion/controller/DiffusionInlineCommentPreviewController.php',
'DiffusionInternalGitRawDiffQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionInternalGitRawDiffQueryConduitAPIMethod.php',
@@ -935,6 +942,8 @@
'DiffusionRepositoryEditUpdateController' => 'applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php',
'DiffusionRepositoryFunctionDatasource' => 'applications/diffusion/typeahead/DiffusionRepositoryFunctionDatasource.php',
'DiffusionRepositoryHistoryManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryHistoryManagementPanel.php',
+ 'DiffusionRepositoryIdentityEditor' => 'applications/diffusion/editor/DiffusionRepositoryIdentityEditor.php',
+ 'DiffusionRepositoryIdentitySearchEngine' => 'applications/diffusion/query/DiffusionRepositoryIdentitySearchEngine.php',
'DiffusionRepositoryListController' => 'applications/diffusion/controller/DiffusionRepositoryListController.php',
'DiffusionRepositoryManageController' => 'applications/diffusion/controller/DiffusionRepositoryManageController.php',
'DiffusionRepositoryManagePanelsController' => 'applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php',
@@ -4078,6 +4087,16 @@
'PhabricatorRepositoryGitLFSRefQuery' => 'applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php',
'PhabricatorRepositoryGraphCache' => 'applications/repository/graphcache/PhabricatorRepositoryGraphCache.php',
'PhabricatorRepositoryGraphStream' => 'applications/repository/daemon/PhabricatorRepositoryGraphStream.php',
+ 'PhabricatorRepositoryIdentity' => 'applications/repository/storage/PhabricatorRepositoryIdentity.php',
+ 'PhabricatorRepositoryIdentityAssignTransaction' => 'applications/repository/xaction/PhabricatorRepositoryIdentityAssignTransaction.php',
+ 'PhabricatorRepositoryIdentityChangeWorker' => 'applications/repository/worker/PhabricatorRepositoryIdentityChangeWorker.php',
+ 'PhabricatorRepositoryIdentityEditEngine' => 'applications/repository/engine/PhabricatorRepositoryIdentityEditEngine.php',
+ 'PhabricatorRepositoryIdentityFerretEngine' => 'applications/repository/search/PhabricatorRepositoryIdentityFerretEngine.php',
+ 'PhabricatorRepositoryIdentityPHIDType' => 'applications/repository/phid/PhabricatorRepositoryIdentityPHIDType.php',
+ 'PhabricatorRepositoryIdentityQuery' => 'applications/repository/query/PhabricatorRepositoryIdentityQuery.php',
+ 'PhabricatorRepositoryIdentityTransaction' => 'applications/repository/storage/PhabricatorRepositoryIdentityTransaction.php',
+ 'PhabricatorRepositoryIdentityTransactionQuery' => 'applications/repository/query/PhabricatorRepositoryIdentityTransactionQuery.php',
+ 'PhabricatorRepositoryIdentityTransactionType' => 'applications/repository/xaction/PhabricatorRepositoryIdentityTransactionType.php',
'PhabricatorRepositoryManagementCacheWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementCacheWorkflow.php',
'PhabricatorRepositoryManagementClusterizeWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php',
'PhabricatorRepositoryManagementDiscoverWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementDiscoverWorkflow.php',
@@ -6144,6 +6163,13 @@
'DiffusionHistoryTableView' => 'DiffusionHistoryView',
'DiffusionHistoryView' => 'DiffusionView',
'DiffusionHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
+ 'DiffusionIdentityAssigneeDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
+ 'DiffusionIdentityAssigneeEditField' => 'PhabricatorTokenizerEditField',
+ 'DiffusionIdentityAssigneeSearchField' => 'PhabricatorSearchTokenizerField',
+ 'DiffusionIdentityEditController' => 'DiffusionController',
+ 'DiffusionIdentityListController' => 'DiffusionController',
+ 'DiffusionIdentityUnassignedDatasource' => 'PhabricatorTypeaheadDatasource',
+ 'DiffusionIdentityViewController' => 'DiffusionController',
'DiffusionInlineCommentController' => 'PhabricatorInlineCommentController',
'DiffusionInlineCommentPreviewController' => 'PhabricatorInlineCommentPreviewController',
'DiffusionInternalGitRawDiffQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
@@ -6264,6 +6290,8 @@
'DiffusionRepositoryEditUpdateController' => 'DiffusionRepositoryManageController',
'DiffusionRepositoryFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'DiffusionRepositoryHistoryManagementPanel' => 'DiffusionRepositoryManagementPanel',
+ 'DiffusionRepositoryIdentityEditor' => 'PhabricatorApplicationTransactionEditor',
+ 'DiffusionRepositoryIdentitySearchEngine' => 'PhabricatorApplicationSearchEngine',
'DiffusionRepositoryListController' => 'DiffusionController',
'DiffusionRepositoryManageController' => 'DiffusionController',
'DiffusionRepositoryManagePanelsController' => 'DiffusionRepositoryManageController',
@@ -9961,6 +9989,20 @@
'PhabricatorRepositoryGitLFSRefQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorRepositoryGraphCache' => 'Phobject',
'PhabricatorRepositoryGraphStream' => 'Phobject',
+ 'PhabricatorRepositoryIdentity' => array(
+ 'PhabricatorRepositoryDAO',
+ 'PhabricatorPolicyInterface',
+ 'PhabricatorApplicationTransactionInterface',
+ ),
+ 'PhabricatorRepositoryIdentityAssignTransaction' => 'PhabricatorRepositoryIdentityTransactionType',
+ 'PhabricatorRepositoryIdentityChangeWorker' => 'PhabricatorWorker',
+ 'PhabricatorRepositoryIdentityEditEngine' => 'PhabricatorEditEngine',
+ 'PhabricatorRepositoryIdentityFerretEngine' => 'PhabricatorFerretEngine',
+ 'PhabricatorRepositoryIdentityPHIDType' => 'PhabricatorPHIDType',
+ 'PhabricatorRepositoryIdentityQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'PhabricatorRepositoryIdentityTransaction' => 'PhabricatorModularTransaction',
+ 'PhabricatorRepositoryIdentityTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
+ 'PhabricatorRepositoryIdentityTransactionType' => 'PhabricatorModularTransactionType',
'PhabricatorRepositoryManagementCacheWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
'PhabricatorRepositoryManagementClusterizeWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
'PhabricatorRepositoryManagementDiscoverWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
diff --git a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php
--- a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php
+++ b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php
@@ -124,6 +124,15 @@
'(?P<repositoryCallsign>[A-Z]+)' => $repository_routes,
'(?P<repositoryID>[1-9]\d*)' => $repository_routes,
+ 'identity/' => array(
+ $this->getQueryRoutePattern() =>
+ 'DiffusionIdentityListController',
+ $this->getEditRoutePattern('edit/') =>
+ 'DiffusionIdentityEditController',
+ 'view/(?P<id>[^/]+)/' =>
+ 'DiffusionIdentityViewController',
+ ),
+
'inline/' => array(
'edit/(?P<phid>[^/]+)/' => 'DiffusionInlineCommentController',
'preview/(?P<phid>[^/]+)/'
diff --git a/src/applications/diffusion/controller/DiffusionIdentityEditController.php b/src/applications/diffusion/controller/DiffusionIdentityEditController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/diffusion/controller/DiffusionIdentityEditController.php
@@ -0,0 +1,12 @@
+<?php
+
+final class DiffusionIdentityEditController
+ extends DiffusionController {
+
+ public function handleRequest(AphrontRequest $request) {
+ return id(new PhabricatorRepositoryIdentityEditEngine())
+ ->setController($this)
+ ->buildResponse();
+ }
+
+}
diff --git a/src/applications/diffusion/controller/DiffusionIdentityListController.php b/src/applications/diffusion/controller/DiffusionIdentityListController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/diffusion/controller/DiffusionIdentityListController.php
@@ -0,0 +1,22 @@
+<?php
+
+final class DiffusionIdentityListController
+ extends DiffusionController {
+
+ public function handleRequest(AphrontRequest $request) {
+ return id(new DiffusionRepositoryIdentitySearchEngine())
+ ->setController($this)
+ ->buildResponse();
+ }
+
+ protected function buildApplicationCrumbs() {
+ $crumbs = parent::buildApplicationCrumbs();
+
+ id(new PhabricatorRepositoryIdentityEditEngine())
+ ->setViewer($this->getViewer())
+ ->addActionToCrumbs($crumbs);
+
+ return $crumbs;
+ }
+
+}
diff --git a/src/applications/diffusion/controller/DiffusionIdentityViewController.php b/src/applications/diffusion/controller/DiffusionIdentityViewController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/diffusion/controller/DiffusionIdentityViewController.php
@@ -0,0 +1,136 @@
+<?php
+
+final class DiffusionIdentityViewController
+ extends DiffusionController {
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $request->getViewer();
+
+ $id = $request->getURIData('id');
+ $identity = id(new PhabricatorRepositoryIdentityQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($id))
+ ->executeOne();
+ if (!$identity) {
+ return new Aphront404Response();
+ }
+
+ $title = pht('Identity %d', $identity->getID());
+
+ $curtain = $this->buildCurtain($identity);
+
+ $header = id(new PHUIHeaderView())
+ ->setUser($viewer)
+ ->setHeader($identity->getIdentityShortName())
+ ->setHeaderIcon('fa-globe')
+ ->setPolicyObject($identity);
+
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addTextCrumb($identity->getID());
+ $crumbs->setBorder(true);
+
+ $timeline = $this->buildTransactionTimeline(
+ $identity,
+ new PhabricatorRepositoryIdentityTransactionQuery());
+ $timeline->setShouldTerminate(true);
+
+ $properties = $this->buildPropertyList($identity);
+
+ $view = id(new PHUITwoColumnView())
+ ->setHeader($header)
+ ->setCurtain($curtain)
+ ->setMainColumn(array(
+ $properties,
+ $timeline,
+ ));
+
+ return $this->newPage()
+ ->setTitle($title)
+ ->setCrumbs($crumbs)
+ ->appendChild(
+ array(
+ $view,
+ ));
+ }
+
+ private function buildCurtain(PhabricatorRepositoryIdentity $identity) {
+ $viewer = $this->getViewer();
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $identity,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $id = $identity->getID();
+ $edit_uri = $this->getApplicationURI("identity/edit/{$id}/");
+
+ $curtain = $this->newCurtainView($identity);
+
+ $curtain->addAction(
+ id(new PhabricatorActionView())
+ ->setIcon('fa-pencil')
+ ->setName(pht('Edit Identity'))
+ ->setHref($edit_uri)
+ ->setWorkflow(!$can_edit)
+ ->setDisabled(!$can_edit));
+
+ return $curtain;
+ }
+
+ private function buildPropertyList(
+ PhabricatorRepositoryIdentity $identity) {
+
+ $viewer = $this->getViewer();
+
+ $properties = id(new PHUIPropertyListView())
+ ->setUser($viewer);
+
+ $effective_phid = $identity->getCurrentEffectiveUserPHID();
+ $automatic_phid = $identity->getAutomaticGuessedUserPHID();
+ $manual_phid = $identity->getManuallySetUserPHID();
+
+ if ($effective_phid) {
+ $tag = id(new PHUITagView())
+ ->setType(PHUITagView::TYPE_SHADE)
+ ->setColor('green')
+ ->setIcon('fa-check')
+ ->setName('Assigned');
+ } else {
+ $tag = id(new PHUITagView())
+ ->setType(PHUITagView::TYPE_SHADE)
+ ->setColor('indigo')
+ ->setIcon('fa-bomb')
+ ->setName('Unassigned');
+ }
+ $properties->addProperty(
+ pht('Effective User'),
+ $this->buildPropertyValue($effective_phid));
+ $properties->addProperty(
+ pht('Automatically Detected User'),
+ $this->buildPropertyValue($automatic_phid));
+ $properties->addProperty(
+ pht('Manually Set User'),
+ $this->buildPropertyValue($manual_phid));
+
+ $header = id(new PHUIHeaderView())
+ ->setHeader(array(pht('Identity Assignments'), $tag));
+
+ return id(new PHUIObjectBoxView())
+ ->setHeader($header)
+ ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
+ ->addPropertyList($properties);
+ }
+
+ private function buildPropertyValue($value) {
+ $viewer = $this->getViewer();
+
+ // TODO: there must be a way to render the fancy "Unassigned" tag
+ if ($value == DiffusionIdentityUnassignedDatasource::FUNCTION_TOKEN) {
+ return pht('<Explicitly Unassigned>');
+ } else if (!$value) {
+ return null;
+ } else {
+ return $viewer->renderHandle($value);
+ }
+ }
+}
diff --git a/src/applications/diffusion/editfield/DiffusionIdentityAssigneeEditField.php b/src/applications/diffusion/editfield/DiffusionIdentityAssigneeEditField.php
new file mode 100644
--- /dev/null
+++ b/src/applications/diffusion/editfield/DiffusionIdentityAssigneeEditField.php
@@ -0,0 +1,22 @@
+<?php
+
+final class DiffusionIdentityAssigneeEditField
+ extends PhabricatorTokenizerEditField {
+
+ protected function newDatasource() {
+ return new DiffusionIdentityAssigneeDatasource();
+ }
+
+ protected function newHTTPParameterType() {
+ return new AphrontUserListHTTPParameterType();
+ }
+
+ protected function newConduitParameterType() {
+ if ($this->getIsSingleValue()) {
+ return new ConduitUserParameterType();
+ } else {
+ return new ConduitUserListParameterType();
+ }
+ }
+
+}
diff --git a/src/applications/diffusion/editor/DiffusionRepositoryIdentityEditor.php b/src/applications/diffusion/editor/DiffusionRepositoryIdentityEditor.php
new file mode 100644
--- /dev/null
+++ b/src/applications/diffusion/editor/DiffusionRepositoryIdentityEditor.php
@@ -0,0 +1,26 @@
+<?php
+
+final class DiffusionRepositoryIdentityEditor
+ extends PhabricatorApplicationTransactionEditor {
+
+ public function getEditorObjectsDescription() {
+ return pht('Repository Identity');
+ }
+
+ public function getCreateObjectTitle($author, $object) {
+ return pht('%s created this identity.', $author);
+ }
+
+ public function getCreateObjectTitleForFeed($author, $object) {
+ return pht('%s created %s.', $author, $object);
+ }
+
+ protected function supportsSearch() {
+ return true;
+ }
+
+ public function getEditorApplicationClass() {
+ return 'PhabricatorDiffusionApplication';
+ }
+
+}
diff --git a/src/applications/diffusion/query/DiffusionRepositoryIdentitySearchEngine.php b/src/applications/diffusion/query/DiffusionRepositoryIdentitySearchEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/diffusion/query/DiffusionRepositoryIdentitySearchEngine.php
@@ -0,0 +1,108 @@
+<?php
+
+final class DiffusionRepositoryIdentitySearchEngine
+ extends PhabricatorApplicationSearchEngine {
+
+ public function getResultTypeDescription() {
+ return pht('Repository Identities');
+ }
+
+ public function getApplicationClassName() {
+ return 'PhabricatorDiffusionApplication';
+ }
+
+ public function newQuery() {
+ return new PhabricatorRepositoryIdentityQuery();
+ }
+
+ protected function buildCustomSearchFields() {
+ return array(
+ id(new DiffusionIdentityAssigneeSearchField())
+ ->setLabel(pht('Assigned To'))
+ ->setKey('assignee')
+ ->setDescription(pht('Search for identities by assignee.')),
+ id(new PhabricatorSearchTextField())
+ ->setLabel(pht('Identity Contains'))
+ ->setKey('match')
+ ->setDescription(pht('Search for identities by substring.')),
+ id(new PhabricatorSearchThreeStateField())
+ ->setLabel(pht('Is Assigned'))
+ ->setKey('hasEffectivePHID')
+ ->setOptions(
+ pht('(Show All)'),
+ pht('Show Only Assigned Identities'),
+ pht('Show Only Unassigned Identities')),
+ );
+ }
+
+ protected function buildQueryFromParameters(array $map) {
+ $query = $this->newQuery();
+
+ if ($map['hasEffectivePHID'] !== null) {
+ $query->withHasEffectivePHID($map['hasEffectivePHID']);
+ }
+
+ if ($map['match'] !== null) {
+ $query->withIdentityNameLike($map['match']);
+ }
+
+ if ($map['assignee'] !== null) {
+ $query->withAssigneePHIDs($map['assignee']);
+ }
+
+ return $query;
+ }
+
+ protected function getURI($path) {
+ return '/diffusion/identity/'.$path;
+ }
+
+ protected function getBuiltinQueryNames() {
+ $names = array(
+ 'all' => pht('All Identities'),
+ );
+
+ return $names;
+ }
+
+ public function buildSavedQueryFromBuiltin($query_key) {
+
+ $query = $this->newSavedQuery();
+ $query->setQueryKey($query_key);
+
+ switch ($query_key) {
+ case 'all':
+ return $query;
+ }
+
+ return parent::buildSavedQueryFromBuiltin($query_key);
+ }
+
+ protected function renderResultList(
+ array $identities,
+ PhabricatorSavedQuery $query,
+ array $handles) {
+ assert_instances_of($identities, 'PhabricatorRepositoryIdentity');
+
+ $viewer = $this->requireViewer();
+
+ $list = new PHUIObjectItemListView();
+ $list->setUser($viewer);
+ foreach ($identities as $identity) {
+ $item = id(new PHUIObjectItemView())
+ ->setObjectName(pht('Identity %d', $identity->getID()))
+ ->setHeader($identity->getIdentityShortName())
+ ->setHref($identity->getURI())
+ ->setObject($identity);
+
+ $list->addItem($item);
+ }
+
+ $result = new PhabricatorApplicationSearchResultView();
+ $result->setObjectList($list);
+ $result->setNoDataString(pht('No Identities found.'));
+
+ return $result;
+ }
+
+}
diff --git a/src/applications/diffusion/searchfield/DiffusionIdentityAssigneeSearchField.php b/src/applications/diffusion/searchfield/DiffusionIdentityAssigneeSearchField.php
new file mode 100644
--- /dev/null
+++ b/src/applications/diffusion/searchfield/DiffusionIdentityAssigneeSearchField.php
@@ -0,0 +1,22 @@
+<?php
+
+final class DiffusionIdentityAssigneeSearchField
+ extends PhabricatorSearchTokenizerField {
+
+ protected function getDefaultValue() {
+ return array();
+ }
+
+ protected function getValueFromRequest(AphrontRequest $request, $key) {
+ return $this->getUsersFromRequest($request, $key);
+ }
+
+ protected function newDatasource() {
+ return new DiffusionIdentityAssigneeDatasource();
+ }
+
+ protected function newConduitParameterType() {
+ return new ConduitUserListParameterType();
+ }
+
+}
diff --git a/src/applications/diffusion/typeahead/DiffusionIdentityAssigneeDatasource.php b/src/applications/diffusion/typeahead/DiffusionIdentityAssigneeDatasource.php
new file mode 100644
--- /dev/null
+++ b/src/applications/diffusion/typeahead/DiffusionIdentityAssigneeDatasource.php
@@ -0,0 +1,22 @@
+<?php
+
+final class DiffusionIdentityAssigneeDatasource
+ extends PhabricatorTypeaheadCompositeDatasource {
+
+ public function getBrowseTitle() {
+ return pht('Browse Assignee');
+ }
+
+ public function getPlaceholderText() {
+ return pht('Type a username or function...');
+ }
+
+ public function getComponentDatasources() {
+ return array(
+ new PhabricatorViewerDatasource(),
+ new PhabricatorPeopleDatasource(),
+ new DiffusionIdentityUnassignedDatasource(),
+ );
+ }
+
+}
diff --git a/src/applications/diffusion/typeahead/DiffusionIdentityUnassignedDatasource.php b/src/applications/diffusion/typeahead/DiffusionIdentityUnassignedDatasource.php
new file mode 100644
--- /dev/null
+++ b/src/applications/diffusion/typeahead/DiffusionIdentityUnassignedDatasource.php
@@ -0,0 +1,77 @@
+<?php
+
+final class DiffusionIdentityUnassignedDatasource
+ extends PhabricatorTypeaheadDatasource {
+
+ const FUNCTION_TOKEN = 'unassigned()';
+
+ public function getBrowseTitle() {
+ return pht('Browse Explicitly Unassigned');
+ }
+
+ public function getPlaceholderText() {
+ return pht('Type "unassigned"...');
+ }
+
+ public function getDatasourceApplicationClass() {
+ return 'PhabricatorDiffusionApplication';
+ }
+
+ public function getDatasourceFunctions() {
+ return array(
+ 'unassigned' => array(
+ 'name' => pht('Explicitly Unassigned'),
+ 'summary' => pht('Find results which are not assigned.'),
+ 'description' => pht(
+ "This function includes results which have been explicitly ".
+ "unassigned. Use a query like this to find explicitly ".
+ "unassigned results:\n\n%s\n\n".
+ "If you combine this function with other functions, the query will ".
+ "return results which match the other selectors //or// have no ".
+ "assignee. For example, this query will find results which are ".
+ "assigned to `alincoln`, and will also find results which have been ".
+ "unassigned:\n\n%s",
+ '> unassigned()',
+ '> alincoln, unassigned()'),
+ ),
+ );
+ }
+
+ public function loadResults() {
+ $results = array(
+ $this->buildUnassignedResult(),
+ );
+ return $this->filterResultsAgainstTokens($results);
+ }
+
+ protected function evaluateFunction($function, array $argv_list) {
+ $results = array();
+
+ foreach ($argv_list as $argv) {
+ $results[] = self::FUNCTION_TOKEN;
+ }
+
+ return $results;
+ }
+
+ public function renderFunctionTokens($function, array $argv_list) {
+ $results = array();
+ foreach ($argv_list as $argv) {
+ $results[] = PhabricatorTypeaheadTokenView::newFromTypeaheadResult(
+ $this->buildUnassignedResult());
+ }
+ return $results;
+ }
+
+ private function buildUnassignedResult() {
+ $name = pht('Unassigned');
+ return $this->newFunctionResult()
+ ->setName($name.' unassigned')
+ ->setDisplayName($name)
+ ->setIcon('fa-ban')
+ ->setPHID('unassigned()')
+ ->setUnique(true)
+ ->addAttribute(pht('Select results with no owner.'));
+ }
+
+}
diff --git a/src/applications/people/editor/PhabricatorUserEditor.php b/src/applications/people/editor/PhabricatorUserEditor.php
--- a/src/applications/people/editor/PhabricatorUserEditor.php
+++ b/src/applications/people/editor/PhabricatorUserEditor.php
@@ -420,6 +420,11 @@
$user->endWriteLocking();
$user->saveTransaction();
+ // Try and match this new address against unclaimed `RepositoryIdentity`s
+ PhabricatorWorker::scheduleTask(
+ 'PhabricatorRepositoryIdentityChangeWorker',
+ array('userPHID' => $user->getPHID()));
+
return $this;
}
diff --git a/src/applications/repository/engine/PhabricatorRepositoryIdentityEditEngine.php b/src/applications/repository/engine/PhabricatorRepositoryIdentityEditEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/repository/engine/PhabricatorRepositoryIdentityEditEngine.php
@@ -0,0 +1,91 @@
+<?php
+
+final class PhabricatorRepositoryIdentityEditEngine
+ extends PhabricatorEditEngine {
+
+ const ENGINECONST = 'repository.identity';
+
+ public function isEngineConfigurable() {
+ return false;
+ }
+
+ public function getEngineName() {
+ return pht('Repository Identities');
+ }
+
+ public function getSummaryHeader() {
+ return pht('Edit Repository Identity Configurations');
+ }
+
+ public function getSummaryText() {
+ return pht('This engine is used to edit Repository identities.');
+ }
+
+ public function getEngineApplicationClass() {
+ return 'PhabricatorDiffusionApplication';
+ }
+
+ protected function newEditableObject() {
+ return new PhabricatorRepositoryIdentity();
+ }
+
+ protected function newObjectQuery() {
+ return new PhabricatorRepositoryIdentityQuery();
+ }
+
+ protected function getObjectCreateTitleText($object) {
+ return pht('Create Identity');
+ }
+
+ protected function getObjectCreateButtonText($object) {
+ return pht('Create Identity');
+ }
+
+ protected function getObjectEditTitleText($object) {
+ return pht('Edit Identity: %s', $object->getIdentityShortName());
+ }
+
+ protected function getObjectEditShortText($object) {
+ return pht('Edit Identity');
+ }
+
+ protected function getObjectCreateShortText() {
+ return pht('Create Identity');
+ }
+
+ protected function getObjectName() {
+ return pht('Identity');
+ }
+
+ protected function getEditorURI() {
+ return '/diffusion/identity/edit/';
+ }
+
+ protected function getObjectCreateCancelURI($object) {
+ return '/diffusion/identity/';
+ }
+
+ protected function getObjectViewURI($object) {
+ return $object->getURI();
+ }
+
+ protected function getCreateNewObjectPolicy() {
+ return PhabricatorPolicies::POLICY_USER;
+ }
+
+ protected function buildCustomEditFields($object) {
+ return array(
+ id(new DiffusionIdentityAssigneeEditField())
+ ->setKey('manuallySetUserPHID')
+ ->setLabel(pht('Assigned To'))
+ ->setDescription(pht('Override this identity\'s assignment.'))
+ ->setTransactionType(
+ PhabricatorRepositoryIdentityAssignTransaction::TRANSACTIONTYPE)
+ ->setIsCopyable(true)
+ ->setIsNullable(true)
+ ->setSingleValue($object->getManuallySetUserPHID()),
+
+ );
+ }
+
+}
diff --git a/src/applications/repository/phid/PhabricatorRepositoryIdentityPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryIdentityPHIDType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/repository/phid/PhabricatorRepositoryIdentityPHIDType.php
@@ -0,0 +1,33 @@
+<?php
+
+final class PhabricatorRepositoryIdentityPHIDType
+ extends PhabricatorPHIDType {
+
+ const TYPECONST = 'RIDT';
+
+ public function getTypeName() {
+ return pht('Repository Identity');
+ }
+
+ public function newObject() {
+ return new PhabricatorRepositoryIdentity();
+ }
+
+ public function getPHIDTypeApplicationClass() {
+ return 'PhabricatorDiffusionApplication';
+ }
+
+ protected function buildQueryForObjects(
+ PhabricatorObjectQuery $query,
+ array $phids) {
+
+ return id(new PhabricatorRepositoryIdentityQuery())
+ ->withPHIDs($phids);
+ }
+
+ public function loadHandles(
+ PhabricatorHandleQuery $query,
+ array $handles,
+ array $objects) {}
+
+}
diff --git a/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php b/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php
@@ -0,0 +1,132 @@
+<?php
+
+final class PhabricatorRepositoryIdentityQuery
+ extends PhabricatorCursorPagedPolicyAwareQuery {
+
+ private $ids;
+ private $phids;
+ private $identityNames;
+ private $emailAddress;
+ private $assigneePHIDs;
+ private $identityNameLike;
+ private $hasEffectivePHID;
+
+ public function withIDs(array $ids) {
+ $this->ids = $ids;
+ return $this;
+ }
+
+ public function withPHIDs(array $phids) {
+ $this->phids = $phids;
+ return $this;
+ }
+
+ public function withIdentityNames(array $names) {
+ $this->identityNames = $names;
+ return $this;
+ }
+
+ public function withIdentityNameLike($name_like) {
+ $this->identityNameLike = $name_like;
+ return $this;
+ }
+
+ public function withEmailAddress($address) {
+ $this->emailAddress = $address;
+ return $this;
+ }
+
+ public function withAssigneePHIDs(array $assignees) {
+ $this->assigneePHIDs = $assignees;
+ return $this;
+ }
+
+ public function withHasEffectivePHID($has_effective_phid) {
+ $this->hasEffectivePHID = $has_effective_phid;
+ return $this;
+ }
+
+ public function newResultObject() {
+ return new PhabricatorRepositoryIdentity();
+ }
+
+ protected function getPrimaryTableAlias() {
+ return 'repository_identity';
+ }
+
+ protected function loadPage() {
+ return $this->loadStandardPage($this->newResultObject());
+ }
+
+ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
+ $where = parent::buildWhereClauseParts($conn);
+
+ if ($this->ids !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'repository_identity.id IN (%Ld)',
+ $this->ids);
+ }
+
+ if ($this->phids !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'repository_identity.phid IN (%Ls)',
+ $this->phids);
+ }
+
+ if ($this->assigneePHIDs !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'repository_identity.currentEffectiveUserPHID IN (%Ls)',
+ $this->assigneePHIDs);
+ }
+
+ if ($this->hasEffectivePHID !== null) {
+
+ if ($this->hasEffectivePHID) {
+ $where[] = qsprintf(
+ $conn,
+ 'repository_identity.currentEffectiveUserPHID IS NOT NULL');
+ } else {
+ $where[] = qsprintf(
+ $conn,
+ 'repository_identity.currentEffectiveUserPHID IS NULL');
+ }
+ }
+
+ if ($this->identityNames !== null) {
+ $name_hashes = array();
+ foreach ($this->identityNames as $name) {
+ $name_hashes[] = PhabricatorHash::digestForIndex($name);
+ }
+
+ $where[] = qsprintf(
+ $conn,
+ 'repository_identity.identityNameHash IN (%Ls)',
+ $name_hashes);
+ }
+
+ if ($this->emailAddress !== null) {
+ $identity_style = "<{$this->emailAddress}>";
+ $where[] = qsprintf(
+ $conn,
+ 'repository_identity.identityNameRaw LIKE %<',
+ $identity_style);
+ }
+
+ if ($this->identityNameLike != null) {
+ $where[] = qsprintf(
+ $conn,
+ 'repository_identity.identityNameRaw LIKE %~',
+ $this->identityNameLike);
+ }
+
+ return $where;
+ }
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorDiffusionApplication';
+ }
+
+}
diff --git a/src/applications/repository/query/PhabricatorRepositoryIdentityTransactionQuery.php b/src/applications/repository/query/PhabricatorRepositoryIdentityTransactionQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/repository/query/PhabricatorRepositoryIdentityTransactionQuery.php
@@ -0,0 +1,10 @@
+<?php
+
+final class PhabricatorRepositoryIdentityTransactionQuery
+ extends PhabricatorApplicationTransactionQuery {
+
+ public function getTemplateApplicationTransaction() {
+ return new PhabricatorRepositoryIdentityTransaction();
+ }
+
+}
diff --git a/src/applications/repository/search/PhabricatorRepositoryIdentityFerretEngine.php b/src/applications/repository/search/PhabricatorRepositoryIdentityFerretEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/repository/search/PhabricatorRepositoryIdentityFerretEngine.php
@@ -0,0 +1,18 @@
+<?php
+
+final class PhabricatorRepositoryIdentityFerretEngine
+ extends PhabricatorFerretEngine {
+
+ public function getApplicationName() {
+ return 'repository';
+ }
+
+ public function getScopeName() {
+ return 'identity';
+ }
+
+ public function newSearchEngine() {
+ return new DiffusionRepositoryIdentitySearchEngine();
+ }
+
+}
diff --git a/src/applications/repository/storage/PhabricatorRepositoryCommit.php b/src/applications/repository/storage/PhabricatorRepositoryCommit.php
--- a/src/applications/repository/storage/PhabricatorRepositoryCommit.php
+++ b/src/applications/repository/storage/PhabricatorRepositoryCommit.php
@@ -21,6 +21,8 @@
protected $repositoryID;
protected $phid;
+ protected $authorIdentityPHID;
+ protected $committerIdentityPHID;
protected $commitIdentifier;
protected $epoch;
protected $mailKey;
@@ -113,6 +115,8 @@
'commitIdentifier' => 'text40',
'mailKey' => 'bytes20',
'authorPHID' => 'phid?',
+ 'authorIdentityPHID' => 'phid?',
+ 'committerIdentityPHID' => 'phid?',
'auditStatus' => 'uint32',
'summary' => 'text255',
'importStatus' => 'uint32',
diff --git a/src/applications/repository/storage/PhabricatorRepositoryIdentity.php b/src/applications/repository/storage/PhabricatorRepositoryIdentity.php
new file mode 100644
--- /dev/null
+++ b/src/applications/repository/storage/PhabricatorRepositoryIdentity.php
@@ -0,0 +1,119 @@
+<?php
+
+final class PhabricatorRepositoryIdentity
+ extends PhabricatorRepositoryDAO
+ implements
+ PhabricatorPolicyInterface,
+ PhabricatorApplicationTransactionInterface {
+
+ protected $authorPHID;
+ protected $identityNameHash;
+ protected $identityNameRaw;
+ protected $identityNameEncoding;
+ protected $automaticGuessedUserPHID;
+ protected $manuallySetUserPHID;
+ protected $currentEffectiveUserPHID;
+
+ protected function getConfiguration() {
+ return array(
+ self::CONFIG_AUX_PHID => true,
+ self::CONFIG_BINARY => array(
+ 'identityNameRaw' => true,
+ ),
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'identityNameHash' => 'bytes12',
+ 'identityNameEncoding' => 'text16?',
+ 'automaticGuessedUserPHID' => 'phid?',
+ 'manuallySetUserPHID' => 'phid?',
+ 'currentEffectiveUserPHID' => 'phid?',
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ 'key_identity' => array(
+ 'columns' => array('identityNameHash'),
+ 'unique' => true,
+ ),
+ ),
+ ) + parent::getConfiguration();
+ }
+
+ public function getPHIDType() {
+ return PhabricatorRepositoryIdentityPHIDType::TYPECONST;
+ }
+
+ public function setIdentityName($name_raw) {
+ $this->setIdentityNameRaw($name_raw);
+ $this->setIdentityNameHash(PhabricatorHash::digestForIndex($name_raw));
+ $this->setIdentityNameEncoding($this->detectEncodingForStorage($name_raw));
+
+ return $this;
+ }
+
+ public function getIdentityName() {
+ return $this->getUTF8StringFromStorage(
+ $this->getIdentityNameRaw(),
+ $this->getIdentityNameEncoding());
+ }
+
+ public function getIdentityShortName() {
+ // TODO
+ return $this->getIdentityName();
+ }
+
+ public function getURI() {
+ return '/diffusion/identity/view/'.$this->getID().'/';
+ }
+
+ public function save() {
+ if ($this->manuallySetUserPHID) {
+ $this->currentEffectiveUserPHID = $this->manuallySetUserPHID;
+ } else {
+ $this->currentEffectiveUserPHID = $this->automaticGuessedUserPHID;
+ }
+
+ return parent::save();
+ }
+
+
+/* -( PhabricatorPolicyInterface )----------------------------------------- */
+
+
+ public function getCapabilities() {
+ return array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ );
+ }
+
+ public function getPolicy($capability) {
+ return PhabricatorPolicies::getMostOpenPolicy();
+ }
+
+ public function hasAutomaticCapability(
+ $capability, PhabricatorUser $viewer) {
+ return false;
+ }
+
+
+/* -( PhabricatorApplicationTransactionInterface )------------------------- */
+
+
+ public function getApplicationTransactionEditor() {
+ return new DiffusionRepositoryIdentityEditor();
+ }
+
+ public function getApplicationTransactionObject() {
+ return $this;
+ }
+
+ public function getApplicationTransactionTemplate() {
+ return new PhabricatorRepositoryIdentityTransaction();
+ }
+
+ public function willRenderTimeline(
+ PhabricatorApplicationTransactionView $timeline,
+ AphrontRequest $request) {
+
+ return $timeline;
+ }
+
+}
diff --git a/src/applications/repository/storage/PhabricatorRepositoryIdentityTransaction.php b/src/applications/repository/storage/PhabricatorRepositoryIdentityTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/repository/storage/PhabricatorRepositoryIdentityTransaction.php
@@ -0,0 +1,18 @@
+<?php
+
+final class PhabricatorRepositoryIdentityTransaction
+ extends PhabricatorModularTransaction {
+
+ public function getApplicationTransactionType() {
+ return PhabricatorRepositoryIdentityPHIDType::TYPECONST;
+ }
+
+ public function getBaseTransactionClass() {
+ return 'PhabricatorRepositoryIdentityTransactionType';
+ }
+
+ public function getApplicationName() {
+ return 'repository';
+ }
+
+}
diff --git a/src/applications/repository/worker/PhabricatorRepositoryIdentityChangeWorker.php b/src/applications/repository/worker/PhabricatorRepositoryIdentityChangeWorker.php
new file mode 100644
--- /dev/null
+++ b/src/applications/repository/worker/PhabricatorRepositoryIdentityChangeWorker.php
@@ -0,0 +1,34 @@
+<?php
+
+final class PhabricatorRepositoryIdentityChangeWorker
+extends PhabricatorWorker {
+
+ protected function doWork() {
+ $viewer = PhabricatorUser::getOmnipotentUser();
+
+ $task_data = $this->getTaskData();
+ $user_phid = idx($task_data, 'userPHID');
+
+ $user = id(new PhabricatorPeopleQuery())
+ ->withPHIDs(array($user_phid))
+ ->setViewer($viewer)
+ ->executeOne();
+
+ $emails = id(new PhabricatorUserEmail())->loadAllWhere(
+ 'userPHID = %s ORDER BY address',
+ $user->getPHID());
+
+ foreach ($emails as $email) {
+ $identities = id(new PhabricatorRepositoryIdentityQuery())
+ ->setViewer($viewer)
+ ->withEmailAddress($email->getAddress())
+ ->execute();
+
+ foreach ($identities as $identity) {
+ $identity->setAutomaticGuessedUserPHID($user->getPHID())
+ ->save();
+ }
+ }
+ }
+
+}
diff --git a/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php b/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php
--- a/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php
+++ b/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php
@@ -66,6 +66,34 @@
$committer = $ref->getCommitter();
$hashes = $ref->getHashes();
+ $author_identity = id(new PhabricatorRepositoryIdentityQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withIdentityNames(array($author))
+ ->executeOne();
+
+ if (!$author_identity) {
+ $author_identity = id(new PhabricatorRepositoryIdentity())
+ ->setAuthorPHID($commit->getPHID())
+ ->setIdentityName($author)
+ ->setAutomaticGuessedUserPHID(
+ $this->resolveUserPHID($commit, $author))
+ ->save();
+ }
+
+ $committer_identity = id(new PhabricatorRepositoryIdentityQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withIdentityNames(array($committer))
+ ->executeOne();
+
+ if (!$committer_identity) {
+ $committer_identity = id(new PhabricatorRepositoryIdentity())
+ ->setAuthorPHID($commit->getPHID())
+ ->setIdentityName($committer)
+ ->setAutomaticGuessedUserPHID(
+ $this->resolveUserPHID($commit, $committer))
+ ->save();
+ }
+
$data = id(new PhabricatorRepositoryCommitData())->loadOneWhere(
'commitID = %d',
$commit->getID());
@@ -81,6 +109,8 @@
$data->setCommitDetail('authorName', $ref->getAuthorName());
$data->setCommitDetail('authorEmail', $ref->getAuthorEmail());
+ $data->setCommitDetail(
+ 'authorIdentityPHID', $author_identity->getPHID());
$data->setCommitDetail(
'authorPHID',
$this->resolveUserPHID($commit, $author));
@@ -96,6 +126,8 @@
$data->setCommitDetail(
'committerPHID',
$this->resolveUserPHID($commit, $committer));
+ $data->setCommitDetail(
+ 'committerIdentityPHID', $committer_identity->getPHID());
}
$repository = $this->repository;
@@ -133,6 +165,9 @@
$commit->setAuthorPHID($author_phid);
}
+ $commit->setAuthorIdentityPHID($author_identity->getPHID());
+ $commit->setCommitterIdentityPHID($committer_identity->getPHID());
+
$commit->setSummary($data->getSummary());
$commit->save();
diff --git a/src/applications/repository/xaction/PhabricatorRepositoryIdentityAssignTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryIdentityAssignTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/repository/xaction/PhabricatorRepositoryIdentityAssignTransaction.php
@@ -0,0 +1,70 @@
+<?php
+
+final class PhabricatorRepositoryIdentityAssignTransaction
+ extends PhabricatorRepositoryIdentityTransactionType {
+
+ const TRANSACTIONTYPE = 'repository:identity:assign';
+
+ public function generateOldValue($object) {
+ return nonempty($object->getManuallySetUserPHID(), null);
+ }
+
+ public function applyInternalEffects($object, $value) {
+ $object->setManuallySetUserPHID($value);
+ }
+
+ public function getTitle() {
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ if (!$old) {
+ return pht(
+ '%s assigned this identity to %s.',
+ $this->renderAuthor(),
+ $this->renderHandle($new));
+ } else if (!$new) {
+ return pht(
+ '%s removed %s as the assignee of this identity.',
+ $this->renderAuthor(),
+ $this->renderHandle($old));
+ } else {
+ return pht(
+ '%s changed the assigned user for this identity from %s to %s.',
+ $this->renderAuthor(),
+ $this->renderHandle($old),
+ $this->renderHandle($new));
+ }
+ }
+
+ public function validateTransactions($object, array $xactions) {
+ $errors = array();
+
+ foreach ($xactions as $xaction) {
+ $old = $xaction->getOldValue();
+ $new = $xaction->getNewValue();
+ if (!strlen($new)) {
+ continue;
+ }
+
+ if ($new === $old) {
+ continue;
+ }
+
+ $assignee_list = id(new PhabricatorPeopleQuery())
+ ->setViewer($this->getActor())
+ ->withPHIDs(array($new))
+ ->execute();
+
+ // TODO: turn this back on when I figure out how to check for
+ // 'unassigned()'
+
+ // if (!$assignee_list) {
+ // $errors[] = $this->newInvalidError(
+ // pht('User "%s" is not a valid user.',
+ // $new));
+ // }
+ }
+ return $errors;
+ }
+
+}
diff --git a/src/applications/repository/xaction/PhabricatorRepositoryIdentityTransactionType.php b/src/applications/repository/xaction/PhabricatorRepositoryIdentityTransactionType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/repository/xaction/PhabricatorRepositoryIdentityTransactionType.php
@@ -0,0 +1,4 @@
+<?php
+
+abstract class PhabricatorRepositoryIdentityTransactionType
+ extends PhabricatorModularTransactionType {}

File Metadata

Mime Type
text/plain
Expires
Thu, Mar 20, 4:38 AM (2 d, 17 h ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/yo/un/uqqqugfu6gl6byns
Default Alt Text
D19443.id46508.diff (46 KB)

Event Timeline