mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-10 06:41:04 +01:00
(stable) Promote 2018 Week 49
This commit is contained in:
commit
237a2a1909
25 changed files with 464 additions and 211 deletions
|
@ -2976,6 +2976,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorEditEngineStaticCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineStaticCommentAction.php',
|
'PhabricatorEditEngineStaticCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineStaticCommentAction.php',
|
||||||
'PhabricatorEditEngineSubtype' => 'applications/transactions/editengine/PhabricatorEditEngineSubtype.php',
|
'PhabricatorEditEngineSubtype' => 'applications/transactions/editengine/PhabricatorEditEngineSubtype.php',
|
||||||
'PhabricatorEditEngineSubtypeInterface' => 'applications/transactions/editengine/PhabricatorEditEngineSubtypeInterface.php',
|
'PhabricatorEditEngineSubtypeInterface' => 'applications/transactions/editengine/PhabricatorEditEngineSubtypeInterface.php',
|
||||||
|
'PhabricatorEditEngineSubtypeMap' => 'applications/transactions/editengine/PhabricatorEditEngineSubtypeMap.php',
|
||||||
'PhabricatorEditEngineSubtypeTestCase' => 'applications/transactions/editengine/__tests__/PhabricatorEditEngineSubtypeTestCase.php',
|
'PhabricatorEditEngineSubtypeTestCase' => 'applications/transactions/editengine/__tests__/PhabricatorEditEngineSubtypeTestCase.php',
|
||||||
'PhabricatorEditEngineTokenizerCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineTokenizerCommentAction.php',
|
'PhabricatorEditEngineTokenizerCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineTokenizerCommentAction.php',
|
||||||
'PhabricatorEditField' => 'applications/transactions/editfield/PhabricatorEditField.php',
|
'PhabricatorEditField' => 'applications/transactions/editfield/PhabricatorEditField.php',
|
||||||
|
@ -3760,6 +3761,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPeopleAnyOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php',
|
'PhabricatorPeopleAnyOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php',
|
||||||
'PhabricatorPeopleApplication' => 'applications/people/application/PhabricatorPeopleApplication.php',
|
'PhabricatorPeopleApplication' => 'applications/people/application/PhabricatorPeopleApplication.php',
|
||||||
'PhabricatorPeopleApproveController' => 'applications/people/controller/PhabricatorPeopleApproveController.php',
|
'PhabricatorPeopleApproveController' => 'applications/people/controller/PhabricatorPeopleApproveController.php',
|
||||||
|
'PhabricatorPeopleAvailabilitySearchEngineAttachment' => 'applications/people/engineextension/PhabricatorPeopleAvailabilitySearchEngineAttachment.php',
|
||||||
'PhabricatorPeopleBadgesProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php',
|
'PhabricatorPeopleBadgesProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php',
|
||||||
'PhabricatorPeopleCommitsProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleCommitsProfileMenuItem.php',
|
'PhabricatorPeopleCommitsProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleCommitsProfileMenuItem.php',
|
||||||
'PhabricatorPeopleController' => 'applications/people/controller/PhabricatorPeopleController.php',
|
'PhabricatorPeopleController' => 'applications/people/controller/PhabricatorPeopleController.php',
|
||||||
|
@ -8729,6 +8731,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorEditEngineSettingsPanel' => 'PhabricatorSettingsPanel',
|
'PhabricatorEditEngineSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||||
'PhabricatorEditEngineStaticCommentAction' => 'PhabricatorEditEngineCommentAction',
|
'PhabricatorEditEngineStaticCommentAction' => 'PhabricatorEditEngineCommentAction',
|
||||||
'PhabricatorEditEngineSubtype' => 'Phobject',
|
'PhabricatorEditEngineSubtype' => 'Phobject',
|
||||||
|
'PhabricatorEditEngineSubtypeMap' => 'Phobject',
|
||||||
'PhabricatorEditEngineSubtypeTestCase' => 'PhabricatorTestCase',
|
'PhabricatorEditEngineSubtypeTestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorEditEngineTokenizerCommentAction' => 'PhabricatorEditEngineCommentAction',
|
'PhabricatorEditEngineTokenizerCommentAction' => 'PhabricatorEditEngineCommentAction',
|
||||||
'PhabricatorEditField' => 'Phobject',
|
'PhabricatorEditField' => 'Phobject',
|
||||||
|
@ -9638,6 +9641,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPeopleAnyOwnerDatasource' => 'PhabricatorTypeaheadDatasource',
|
'PhabricatorPeopleAnyOwnerDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||||
'PhabricatorPeopleApplication' => 'PhabricatorApplication',
|
'PhabricatorPeopleApplication' => 'PhabricatorApplication',
|
||||||
'PhabricatorPeopleApproveController' => 'PhabricatorPeopleController',
|
'PhabricatorPeopleApproveController' => 'PhabricatorPeopleController',
|
||||||
|
'PhabricatorPeopleAvailabilitySearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
||||||
'PhabricatorPeopleBadgesProfileMenuItem' => 'PhabricatorProfileMenuItem',
|
'PhabricatorPeopleBadgesProfileMenuItem' => 'PhabricatorProfileMenuItem',
|
||||||
'PhabricatorPeopleCommitsProfileMenuItem' => 'PhabricatorProfileMenuItem',
|
'PhabricatorPeopleCommitsProfileMenuItem' => 'PhabricatorProfileMenuItem',
|
||||||
'PhabricatorPeopleController' => 'PhabricatorController',
|
'PhabricatorPeopleController' => 'PhabricatorController',
|
||||||
|
|
|
@ -59,10 +59,6 @@ final class PhabricatorAuditEditor
|
||||||
|
|
||||||
$this->oldAuditStatus = $object->getAuditStatus();
|
$this->oldAuditStatus = $object->getAuditStatus();
|
||||||
|
|
||||||
$object->loadAndAttachAuditAuthority(
|
|
||||||
$this->getActor(),
|
|
||||||
$this->getActingAsPHID());
|
|
||||||
|
|
||||||
return parent::expandTransactions($object, $xactions);
|
return parent::expandTransactions($object, $xactions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1367,9 +1367,9 @@ final class DifferentialTransactionEditor
|
||||||
foreach (array_chunk($sql, 256) as $chunk) {
|
foreach (array_chunk($sql, 256) as $chunk) {
|
||||||
queryfx(
|
queryfx(
|
||||||
$conn_w,
|
$conn_w,
|
||||||
'INSERT INTO %T (repositoryID, pathID, epoch, revisionID) VALUES %Q',
|
'INSERT INTO %T (repositoryID, pathID, epoch, revisionID) VALUES %LQ',
|
||||||
$table->getTableName(),
|
$table->getTableName(),
|
||||||
implode(', ', $chunk));
|
$chunk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1444,9 +1444,9 @@ final class DifferentialTransactionEditor
|
||||||
if ($sql) {
|
if ($sql) {
|
||||||
queryfx(
|
queryfx(
|
||||||
$conn_w,
|
$conn_w,
|
||||||
'INSERT INTO %T (revisionID, type, hash) VALUES %Q',
|
'INSERT INTO %T (revisionID, type, hash) VALUES %LQ',
|
||||||
ArcanistDifferentialRevisionHash::TABLE_NAME,
|
ArcanistDifferentialRevisionHash::TABLE_NAME,
|
||||||
implode(', ', $sql));
|
$sql);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ final class DiffusionCommitController extends DiffusionController {
|
||||||
->withIdentifiers(array($commit_identifier))
|
->withIdentifiers(array($commit_identifier))
|
||||||
->needCommitData(true)
|
->needCommitData(true)
|
||||||
->needAuditRequests(true)
|
->needAuditRequests(true)
|
||||||
|
->needAuditAuthority(array($viewer))
|
||||||
->setLimit(100)
|
->setLimit(100)
|
||||||
->needIdentities(true)
|
->needIdentities(true)
|
||||||
->execute();
|
->execute();
|
||||||
|
@ -111,7 +112,6 @@ final class DiffusionCommitController extends DiffusionController {
|
||||||
}
|
}
|
||||||
|
|
||||||
$audit_requests = $commit->getAudits();
|
$audit_requests = $commit->getAudits();
|
||||||
$commit->loadAndAttachAuditAuthority($viewer);
|
|
||||||
|
|
||||||
$commit_data = $commit->getCommitData();
|
$commit_data = $commit->getCommitData();
|
||||||
$is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
|
$is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
|
||||||
|
|
|
@ -43,9 +43,12 @@ final class DiffusionCommitEditEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function newObjectQuery() {
|
protected function newObjectQuery() {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
return id(new DiffusionCommitQuery())
|
return id(new DiffusionCommitQuery())
|
||||||
->needCommitData(true)
|
->needCommitData(true)
|
||||||
->needAuditRequests(true);
|
->needAuditRequests(true)
|
||||||
|
->needAuditAuthority(array($viewer));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getEditorURI() {
|
protected function getEditorURI() {
|
||||||
|
|
|
@ -17,6 +17,7 @@ final class DiffusionCommitQuery
|
||||||
private $unreachable;
|
private $unreachable;
|
||||||
|
|
||||||
private $needAuditRequests;
|
private $needAuditRequests;
|
||||||
|
private $needAuditAuthority;
|
||||||
private $auditIDs;
|
private $auditIDs;
|
||||||
private $auditorPHIDs;
|
private $auditorPHIDs;
|
||||||
private $epochMin;
|
private $epochMin;
|
||||||
|
@ -121,6 +122,12 @@ final class DiffusionCommitQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function needAuditAuthority(array $users) {
|
||||||
|
assert_instances_of($users, 'PhabricatorUser');
|
||||||
|
$this->needAuditAuthority = $users;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function withAuditIDs(array $ids) {
|
public function withAuditIDs(array $ids) {
|
||||||
$this->auditIDs = $ids;
|
$this->auditIDs = $ids;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -231,14 +238,27 @@ final class DiffusionCommitQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count($subqueries) > 1) {
|
if (count($subqueries) > 1) {
|
||||||
foreach ($subqueries as $key => $subquery) {
|
$unions = null;
|
||||||
$subqueries[$key] = '('.$subquery.')';
|
foreach ($subqueries as $subquery) {
|
||||||
|
if (!$unions) {
|
||||||
|
$unions = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'(%Q)',
|
||||||
|
$subquery);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$unions = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'%Q UNION DISTINCT (%Q)',
|
||||||
|
$unions,
|
||||||
|
$subquery);
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = qsprintf(
|
$query = qsprintf(
|
||||||
$conn,
|
$conn,
|
||||||
'%Q %Q %Q',
|
'%Q %Q %Q',
|
||||||
implode(' UNION DISTINCT ', $subqueries),
|
$unions,
|
||||||
$this->buildOrderClause($conn, true),
|
$this->buildOrderClause($conn, true),
|
||||||
$this->buildLimitClause($conn));
|
$this->buildLimitClause($conn));
|
||||||
} else {
|
} else {
|
||||||
|
@ -423,6 +443,72 @@ final class DiffusionCommitQuery
|
||||||
$commits);
|
$commits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->needAuditAuthority) {
|
||||||
|
$authority_users = $this->needAuditAuthority;
|
||||||
|
|
||||||
|
// NOTE: This isn't very efficient since we're running two queries per
|
||||||
|
// user, but there's currently no way to figure out authority for
|
||||||
|
// multiple users in one query. Today, we only ever request authority for
|
||||||
|
// a single user and single commit, so this has no practical impact.
|
||||||
|
|
||||||
|
// NOTE: We're querying with the viewership of query viewer, not the
|
||||||
|
// actual users. If the viewer can't see a project or package, they
|
||||||
|
// won't be able to see who has authority on it. This is safer than
|
||||||
|
// showing them true authority, and should never matter today, but it
|
||||||
|
// also doesn't seem like a significant disclosure and might be
|
||||||
|
// reasonable to adjust later if it causes something weird or confusing
|
||||||
|
// to happen.
|
||||||
|
|
||||||
|
$authority_map = array();
|
||||||
|
foreach ($authority_users as $authority_user) {
|
||||||
|
$authority_phid = $authority_user->getPHID();
|
||||||
|
if (!$authority_phid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result_phids = array();
|
||||||
|
|
||||||
|
// Users have authority over themselves.
|
||||||
|
$result_phids[] = $authority_phid;
|
||||||
|
|
||||||
|
// Users have authority over packages they own.
|
||||||
|
$owned_packages = id(new PhabricatorOwnersPackageQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withAuthorityPHIDs(array($authority_phid))
|
||||||
|
->execute();
|
||||||
|
foreach ($owned_packages as $package) {
|
||||||
|
$result_phids[] = $package->getPHID();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Users have authority over projects they're members of.
|
||||||
|
$projects = id(new PhabricatorProjectQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withMemberPHIDs(array($authority_phid))
|
||||||
|
->execute();
|
||||||
|
foreach ($projects as $project) {
|
||||||
|
$result_phids[] = $project->getPHID();
|
||||||
|
}
|
||||||
|
|
||||||
|
$result_phids = array_fuse($result_phids);
|
||||||
|
|
||||||
|
foreach ($commits as $commit) {
|
||||||
|
$attach_phids = $result_phids;
|
||||||
|
|
||||||
|
// NOTE: When modifying your own commits, you act only on behalf of
|
||||||
|
// yourself, not your packages or projects. The idea here is that you
|
||||||
|
// can't accept your own commits. In the future, this might change or
|
||||||
|
// depend on configuration.
|
||||||
|
$author_phid = $commit->getAuthorPHID();
|
||||||
|
if ($author_phid == $authority_phid) {
|
||||||
|
$attach_phids = array($author_phid);
|
||||||
|
$attach_phids = array_fuse($attach_phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
$commit->attachAuditAuthority($authority_user, $attach_phids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $commits;
|
return $commits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -649,7 +649,7 @@ final class ManiphestTransactionEditor
|
||||||
$old_value = $object->getOwnerPHID();
|
$old_value = $object->getOwnerPHID();
|
||||||
$new_value = $xaction->getNewValue();
|
$new_value = $xaction->getNewValue();
|
||||||
if ($old_value === $new_value) {
|
if ($old_value === $new_value) {
|
||||||
continue;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When a task is reassigned, move the old owner to the subscriber
|
// When a task is reassigned, move the old owner to the subscriber
|
||||||
|
|
|
@ -47,7 +47,7 @@ final class ManiphestTaskSearchEngine
|
||||||
// Hide the "Subtypes" constraint from the web UI if the install only
|
// Hide the "Subtypes" constraint from the web UI if the install only
|
||||||
// defines one task subtype, since it isn't of any use in this case.
|
// defines one task subtype, since it isn't of any use in this case.
|
||||||
$subtype_map = id(new ManiphestTask())->newEditEngineSubtypeMap();
|
$subtype_map = id(new ManiphestTask())->newEditEngineSubtypeMap();
|
||||||
$hide_subtypes = (count($subtype_map) == 1);
|
$hide_subtypes = ($subtype_map->getCount() == 1);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
id(new PhabricatorOwnersSearchField())
|
id(new PhabricatorOwnersSearchField())
|
||||||
|
|
|
@ -573,7 +573,7 @@ final class ManiphestTask extends ManiphestDAO
|
||||||
public function newSubtypeObject() {
|
public function newSubtypeObject() {
|
||||||
$subtype_key = $this->getEditEngineSubtype();
|
$subtype_key = $this->getEditEngineSubtype();
|
||||||
$subtype_map = $this->newEditEngineSubtypeMap();
|
$subtype_map = $this->newEditEngineSubtypeMap();
|
||||||
return idx($subtype_map, $subtype_key);
|
return $subtype_map->getSubtype($subtype_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -( PhabricatorFulltextInterface )--------------------------------------- */
|
/* -( PhabricatorFulltextInterface )--------------------------------------- */
|
||||||
|
|
|
@ -214,11 +214,12 @@ final class ManiphestTransaction
|
||||||
public function renderSubtypeName($value) {
|
public function renderSubtypeName($value) {
|
||||||
$object = $this->getObject();
|
$object = $this->getObject();
|
||||||
$map = $object->newEditEngineSubtypeMap();
|
$map = $object->newEditEngineSubtypeMap();
|
||||||
if (!isset($map[$value])) {
|
|
||||||
|
if (!$map->isValidSubtype($value)) {
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $map[$value]->getName();
|
return $map->getSubtype($value)->getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ final class ManiphestTaskSubtypeDatasource
|
||||||
$results = array();
|
$results = array();
|
||||||
|
|
||||||
$subtype_map = id(new ManiphestTask())->newEditEngineSubtypeMap();
|
$subtype_map = id(new ManiphestTask())->newEditEngineSubtypeMap();
|
||||||
foreach ($subtype_map as $key => $subtype) {
|
foreach ($subtype_map->getSubtypes() as $key => $subtype) {
|
||||||
|
|
||||||
$result = id(new PhabricatorTypeaheadResult())
|
$result = id(new PhabricatorTypeaheadResult())
|
||||||
->setIcon($subtype->getIcon())
|
->setIcon($subtype->getIcon())
|
||||||
|
|
|
@ -56,9 +56,6 @@ final class ManiphestTaskListView extends ManiphestView {
|
||||||
Javelin::initBehavior('maniphest-list-editor');
|
Javelin::initBehavior('maniphest-list-editor');
|
||||||
}
|
}
|
||||||
|
|
||||||
$subtype_map = id(new ManiphestTask())
|
|
||||||
->newEditEngineSubtypeMap();
|
|
||||||
|
|
||||||
foreach ($this->tasks as $task) {
|
foreach ($this->tasks as $task) {
|
||||||
$item = id(new PHUIObjectItemView())
|
$item = id(new PHUIObjectItemView())
|
||||||
->setUser($this->getUser())
|
->setUser($this->getUser())
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorPeopleAvailabilitySearchEngineAttachment
|
||||||
|
extends PhabricatorSearchEngineAttachment {
|
||||||
|
|
||||||
|
public function getAttachmentName() {
|
||||||
|
return pht('User Availability');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAttachmentDescription() {
|
||||||
|
return pht('Get availability information for users.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function willLoadAttachmentData($query, $spec) {
|
||||||
|
$query->needAvailability(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAttachmentForObject($object, $data, $spec) {
|
||||||
|
|
||||||
|
$until = $object->getAwayUntil();
|
||||||
|
if ($until) {
|
||||||
|
$until = (int)$until;
|
||||||
|
} else {
|
||||||
|
$until = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $object->getDisplayAvailability();
|
||||||
|
if ($value === null) {
|
||||||
|
$value = PhabricatorCalendarEventInvitee::AVAILABILITY_AVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = PhabricatorCalendarEventInvitee::getAvailabilityName($value);
|
||||||
|
$color = PhabricatorCalendarEventInvitee::getAvailabilityColor($value);
|
||||||
|
|
||||||
|
$event_phid = $object->getAvailabilityEventPHID();
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'value' => $value,
|
||||||
|
'until' => $until,
|
||||||
|
'name' => $name,
|
||||||
|
'color' => $color,
|
||||||
|
'eventPHID' => $event_phid,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1466,7 +1466,10 @@ final class PhabricatorUser
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getConduitSearchAttachments() {
|
public function getConduitSearchAttachments() {
|
||||||
return array();
|
return array(
|
||||||
|
id(new PhabricatorPeopleAvailabilitySearchEngineAttachment())
|
||||||
|
->setAttachmentKey('availability'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,10 +15,11 @@ final class PhabricatorRepositoryManagementThawWorkflow
|
||||||
array(
|
array(
|
||||||
array(
|
array(
|
||||||
'name' => 'demote',
|
'name' => 'demote',
|
||||||
'param' => 'device',
|
'param' => 'device/service',
|
||||||
'help' => pht(
|
'help' => pht(
|
||||||
'Demote a device, discarding local changes. Clears stuck '.
|
'Demote a device (or all devices in a service) discarding '.
|
||||||
'write locks and recovers from lost leaders.'),
|
'local changes. Clears stuck write locks and recovers from '.
|
||||||
|
'lost leaders.'),
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'name' => 'promote',
|
'name' => 'promote',
|
||||||
|
@ -32,6 +33,12 @@ final class PhabricatorRepositoryManagementThawWorkflow
|
||||||
'name' => 'force',
|
'name' => 'force',
|
||||||
'help' => pht('Run operations without asking for confirmation.'),
|
'help' => pht('Run operations without asking for confirmation.'),
|
||||||
),
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'all-repositories',
|
||||||
|
'help' => pht(
|
||||||
|
'Apply the promotion or demotion to all repositories hosted '.
|
||||||
|
'on the device.'),
|
||||||
|
),
|
||||||
array(
|
array(
|
||||||
'name' => 'repositories',
|
'name' => 'repositories',
|
||||||
'wildcard' => true,
|
'wildcard' => true,
|
||||||
|
@ -42,12 +49,6 @@ final class PhabricatorRepositoryManagementThawWorkflow
|
||||||
public function execute(PhutilArgumentParser $args) {
|
public function execute(PhutilArgumentParser $args) {
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
$repositories = $this->loadRepositories($args, 'repositories');
|
|
||||||
if (!$repositories) {
|
|
||||||
throw new PhutilArgumentUsageException(
|
|
||||||
pht('Specify one or more repositories to thaw.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$promote = $args->getArg('promote');
|
$promote = $args->getArg('promote');
|
||||||
$demote = $args->getArg('demote');
|
$demote = $args->getArg('demote');
|
||||||
|
|
||||||
|
@ -61,17 +62,109 @@ final class PhabricatorRepositoryManagementThawWorkflow
|
||||||
pht('Specify either --promote or --demote, but not both.'));
|
pht('Specify either --promote or --demote, but not both.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$device_name = nonempty($promote, $demote);
|
$target_name = nonempty($promote, $demote);
|
||||||
|
|
||||||
$device = id(new AlmanacDeviceQuery())
|
$devices = id(new AlmanacDeviceQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withNames(array($device_name))
|
->withNames(array($target_name))
|
||||||
->executeOne();
|
->execute();
|
||||||
if (!$device) {
|
if (!$devices) {
|
||||||
throw new PhutilArgumentUsageException(
|
$service = id(new AlmanacServiceQuery())
|
||||||
pht('No device "%s" exists.', $device_name));
|
->setViewer($viewer)
|
||||||
|
->withNames(array($target_name))
|
||||||
|
->executeOne();
|
||||||
|
|
||||||
|
if (!$service) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht('No device or service named "%s" exists.', $target_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($promote) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'You can not "--promote" an entire service ("%s"). Only a single '.
|
||||||
|
'device may be promoted.',
|
||||||
|
$target_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
$bindings = id(new AlmanacBindingQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withServicePHIDs(array($service->getPHID()))
|
||||||
|
->execute();
|
||||||
|
if (!$bindings) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Service "%s" is not bound to any devices.',
|
||||||
|
$target_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
$interfaces = id(new AlmanacInterfaceQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs(mpull($bindings, 'getInterfacePHID'))
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$device_phids = mpull($interfaces, 'getDevicePHID');
|
||||||
|
|
||||||
|
$devices = id(new AlmanacDeviceQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs($device_phids)
|
||||||
|
->execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$repository_names = $args->getArg('repositories');
|
||||||
|
$all_repositories = $args->getArg('all-repositories');
|
||||||
|
if ($repository_names && $all_repositories) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Specify a list of repositories or "--all-repositories", '.
|
||||||
|
'but not both.'));
|
||||||
|
} else if (!$repository_names && !$all_repositories) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Select repositories to affect by providing a list of repositories '.
|
||||||
|
'or using the "--all-repositories" flag.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($repository_names) {
|
||||||
|
$repositories = $this->loadRepositories($args, 'repositories');
|
||||||
|
if (!$repositories) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht('Specify one or more repositories to thaw.'));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$repositories = array();
|
||||||
|
|
||||||
|
$services = id(new AlmanacServiceQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withDevicePHIDs(mpull($devices, 'getPHID'))
|
||||||
|
->execute();
|
||||||
|
if ($services) {
|
||||||
|
$repositories = id(new PhabricatorRepositoryQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withAlmanacServicePHIDs(mpull($services, 'getPHID'))
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$repositories) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht('There are no repositories on the selected device or service.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$display_list = new PhutilConsoleList();
|
||||||
|
foreach ($repositories as $repository) {
|
||||||
|
$display_list->addItem(
|
||||||
|
pht(
|
||||||
|
'%s %s',
|
||||||
|
$repository->getMonogram(),
|
||||||
|
$repository->getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n\n%B\n",
|
||||||
|
pht('These repositories will be thawed:'),
|
||||||
|
$display_list->drawConsoleString());
|
||||||
|
|
||||||
if ($promote) {
|
if ($promote) {
|
||||||
$risk_message = pht(
|
$risk_message = pht(
|
||||||
'Promoting a device can cause the loss of any repository data which '.
|
'Promoting a device can cause the loss of any repository data which '.
|
||||||
|
@ -96,126 +189,128 @@ final class PhabricatorRepositoryManagementThawWorkflow
|
||||||
pht('User aborted the workflow.'));
|
pht('User aborted the workflow.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($repositories as $repository) {
|
foreach ($devices as $device) {
|
||||||
$repository_phid = $repository->getPHID();
|
foreach ($repositories as $repository) {
|
||||||
|
$repository_phid = $repository->getPHID();
|
||||||
|
|
||||||
$write_lock = PhabricatorRepositoryWorkingCopyVersion::getWriteLock(
|
$write_lock = PhabricatorRepositoryWorkingCopyVersion::getWriteLock(
|
||||||
$repository_phid);
|
$repository_phid);
|
||||||
|
|
||||||
echo tsprintf(
|
echo tsprintf(
|
||||||
"%s\n",
|
"%s\n",
|
||||||
pht(
|
pht(
|
||||||
'Waiting to acquire write lock for "%s"...',
|
'Waiting to acquire write lock for "%s"...',
|
||||||
$repository->getDisplayName()));
|
$repository->getDisplayName()));
|
||||||
|
|
||||||
$write_lock->lock(phutil_units('5 minutes in seconds'));
|
$write_lock->lock(phutil_units('5 minutes in seconds'));
|
||||||
try {
|
try {
|
||||||
|
|
||||||
$service = $repository->loadAlmanacService();
|
$service = $repository->loadAlmanacService();
|
||||||
if (!$service) {
|
if (!$service) {
|
||||||
throw new PhutilArgumentUsageException(
|
|
||||||
pht(
|
|
||||||
'Repository "%s" is not a cluster repository: it is not '.
|
|
||||||
'bound to an Almanac service.',
|
|
||||||
$repository->getDisplayName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($promote) {
|
|
||||||
// You can only promote active devices. (You may demote active or
|
|
||||||
// inactive devices.)
|
|
||||||
$bindings = $service->getActiveBindings();
|
|
||||||
$bindings = mpull($bindings, null, 'getDevicePHID');
|
|
||||||
if (empty($bindings[$device->getPHID()])) {
|
|
||||||
throw new PhutilArgumentUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
pht(
|
pht(
|
||||||
'Repository "%s" has no active binding to device "%s". Only '.
|
'Repository "%s" is not a cluster repository: it is not '.
|
||||||
'actively bound devices can be promoted.',
|
'bound to an Almanac service.',
|
||||||
$repository->getDisplayName(),
|
$repository->getDisplayName()));
|
||||||
$device->getName()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$versions = PhabricatorRepositoryWorkingCopyVersion::loadVersions(
|
if ($promote) {
|
||||||
$repository->getPHID());
|
// You can only promote active devices. (You may demote active or
|
||||||
$versions = mpull($versions, null, 'getDevicePHID');
|
// inactive devices.)
|
||||||
|
$bindings = $service->getActiveBindings();
|
||||||
// Before we promote, make sure there are no outstanding versions on
|
$bindings = mpull($bindings, null, 'getDevicePHID');
|
||||||
// devices with inactive bindings. If there are, you need to demote
|
if (empty($bindings[$device->getPHID()])) {
|
||||||
// these first.
|
throw new PhutilArgumentUsageException(
|
||||||
$inactive = array();
|
pht(
|
||||||
foreach ($versions as $device_phid => $version) {
|
'Repository "%s" has no active binding to device "%s". '.
|
||||||
if (isset($bindings[$device_phid])) {
|
'Only actively bound devices can be promoted.',
|
||||||
continue;
|
$repository->getDisplayName(),
|
||||||
|
$device->getName()));
|
||||||
}
|
}
|
||||||
$inactive[$device_phid] = $version;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($inactive) {
|
$versions = PhabricatorRepositoryWorkingCopyVersion::loadVersions(
|
||||||
$handles = $viewer->loadHandles(array_keys($inactive));
|
$repository->getPHID());
|
||||||
|
$versions = mpull($versions, null, 'getDevicePHID');
|
||||||
|
|
||||||
$handle_list = iterator_to_array($handles);
|
// Before we promote, make sure there are no outstanding versions
|
||||||
$handle_list = mpull($handle_list, 'getName');
|
// on devices with inactive bindings. If there are, you need to
|
||||||
$handle_list = implode(', ', $handle_list);
|
// demote these first.
|
||||||
|
$inactive = array();
|
||||||
|
foreach ($versions as $device_phid => $version) {
|
||||||
|
if (isset($bindings[$device_phid])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$inactive[$device_phid] = $version;
|
||||||
|
}
|
||||||
|
|
||||||
throw new PhutilArgumentUsageException(
|
if ($inactive) {
|
||||||
|
$handles = $viewer->loadHandles(array_keys($inactive));
|
||||||
|
|
||||||
|
$handle_list = iterator_to_array($handles);
|
||||||
|
$handle_list = mpull($handle_list, 'getName');
|
||||||
|
$handle_list = implode(', ', $handle_list);
|
||||||
|
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Repository "%s" has versions on inactive devices. Demote '.
|
||||||
|
'(or reactivate) these devices before promoting a new '.
|
||||||
|
'leader: %s.',
|
||||||
|
$repository->getDisplayName(),
|
||||||
|
$handle_list));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, make sure there are no outstanding versions on devices with
|
||||||
|
// active bindings. These also need to be demoted (or promoting is
|
||||||
|
// a mistake or already happened).
|
||||||
|
$active = array_select_keys($versions, array_keys($bindings));
|
||||||
|
if ($active) {
|
||||||
|
$handles = $viewer->loadHandles(array_keys($active));
|
||||||
|
|
||||||
|
$handle_list = iterator_to_array($handles);
|
||||||
|
$handle_list = mpull($handle_list, 'getName');
|
||||||
|
$handle_list = implode(', ', $handle_list);
|
||||||
|
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Unable to promote "%s" for repository "%s" because this '.
|
||||||
|
'cluster already has one or more unambiguous leaders: %s.',
|
||||||
|
$device->getName(),
|
||||||
|
$repository->getDisplayName(),
|
||||||
|
$handle_list));
|
||||||
|
}
|
||||||
|
|
||||||
|
PhabricatorRepositoryWorkingCopyVersion::updateVersion(
|
||||||
|
$repository->getPHID(),
|
||||||
|
$device->getPHID(),
|
||||||
|
0);
|
||||||
|
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
pht(
|
pht(
|
||||||
'Repository "%s" has versions on inactive devices. Demote '.
|
'Promoted "%s" to become a leader for "%s".',
|
||||||
'(or reactivate) these devices before promoting a new '.
|
|
||||||
'leader: %s.',
|
|
||||||
$repository->getDisplayName(),
|
|
||||||
$handle_list));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now, make sure there are no outstanding versions on devices with
|
|
||||||
// active bindings. These also need to be demoted (or promoting is a
|
|
||||||
// mistake or already happened).
|
|
||||||
$active = array_select_keys($versions, array_keys($bindings));
|
|
||||||
if ($active) {
|
|
||||||
$handles = $viewer->loadHandles(array_keys($active));
|
|
||||||
|
|
||||||
$handle_list = iterator_to_array($handles);
|
|
||||||
$handle_list = mpull($handle_list, 'getName');
|
|
||||||
$handle_list = implode(', ', $handle_list);
|
|
||||||
|
|
||||||
throw new PhutilArgumentUsageException(
|
|
||||||
pht(
|
|
||||||
'Unable to promote "%s" for repository "%s" because this '.
|
|
||||||
'cluster already has one or more unambiguous leaders: %s.',
|
|
||||||
$device->getName(),
|
$device->getName(),
|
||||||
$repository->getDisplayName(),
|
$repository->getDisplayName()));
|
||||||
$handle_list));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PhabricatorRepositoryWorkingCopyVersion::updateVersion(
|
if ($demote) {
|
||||||
$repository->getPHID(),
|
PhabricatorRepositoryWorkingCopyVersion::demoteDevice(
|
||||||
$device->getPHID(),
|
$repository->getPHID(),
|
||||||
0);
|
$device->getPHID());
|
||||||
|
|
||||||
echo tsprintf(
|
echo tsprintf(
|
||||||
"%s\n",
|
"%s\n",
|
||||||
pht(
|
pht(
|
||||||
'Promoted "%s" to become a leader for "%s".',
|
'Demoted "%s" from leadership of repository "%s".',
|
||||||
$device->getName(),
|
$device->getName(),
|
||||||
$repository->getDisplayName()));
|
$repository->getDisplayName()));
|
||||||
|
}
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$write_lock->unlock();
|
||||||
|
throw $ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($demote) {
|
|
||||||
PhabricatorRepositoryWorkingCopyVersion::demoteDevice(
|
|
||||||
$repository->getPHID(),
|
|
||||||
$device->getPHID());
|
|
||||||
|
|
||||||
echo tsprintf(
|
|
||||||
"%s\n",
|
|
||||||
pht(
|
|
||||||
'Demoted "%s" from leadership of repository "%s".',
|
|
||||||
$device->getName(),
|
|
||||||
$repository->getDisplayName()));
|
|
||||||
}
|
|
||||||
} catch (Exception $ex) {
|
|
||||||
$write_lock->unlock();
|
$write_lock->unlock();
|
||||||
throw $ex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$write_lock->unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -44,6 +44,15 @@ final class PhabricatorRepositorySearchEngine
|
||||||
->setKey('uris')
|
->setKey('uris')
|
||||||
->setDescription(
|
->setDescription(
|
||||||
pht('Search for repositories by clone/checkout URI.')),
|
pht('Search for repositories by clone/checkout URI.')),
|
||||||
|
id(new PhabricatorPHIDsSearchField())
|
||||||
|
->setLabel(pht('Services'))
|
||||||
|
->setKey('almanacServicePHIDs')
|
||||||
|
->setAliases(
|
||||||
|
array(
|
||||||
|
'almanacServicePHID',
|
||||||
|
'almanacService',
|
||||||
|
'almanacServices',
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,6 +89,10 @@ final class PhabricatorRepositorySearchEngine
|
||||||
$query->withURIs($map['uris']);
|
$query->withURIs($map['uris']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($map['almanacServicePHIDs']) {
|
||||||
|
$query->withAlmanacServicePHIDs($map['almanacServicePHIDs']);
|
||||||
|
}
|
||||||
|
|
||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2773,6 +2773,13 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
->setDescription(
|
->setDescription(
|
||||||
pht(
|
pht(
|
||||||
'True if the repository is importing initial commits.')),
|
'True if the repository is importing initial commits.')),
|
||||||
|
id(new PhabricatorConduitSearchFieldSpecification())
|
||||||
|
->setKey('almanacServicePHID')
|
||||||
|
->setType('phid?')
|
||||||
|
->setDescription(
|
||||||
|
pht(
|
||||||
|
'The Almanac Service that hosts this repository, if the '.
|
||||||
|
'repository is clustered.')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2784,6 +2791,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
'shortName' => $this->getRepositorySlug(),
|
'shortName' => $this->getRepositorySlug(),
|
||||||
'status' => $this->getStatus(),
|
'status' => $this->getStatus(),
|
||||||
'isImporting' => (bool)$this->isImporting(),
|
'isImporting' => (bool)$this->isImporting(),
|
||||||
|
'almanacServicePHID' => $this->getAlmanacServicePHID(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -210,84 +210,32 @@ final class PhabricatorRepositoryCommit
|
||||||
return $this->assertAttached($this->committerIdentity);
|
return $this->assertAttached($this->committerIdentity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadAndAttachAuditAuthority(
|
public function attachAuditAuthority(
|
||||||
PhabricatorUser $viewer,
|
PhabricatorUser $user,
|
||||||
$actor_phid = null) {
|
array $authority) {
|
||||||
|
|
||||||
if ($actor_phid === null) {
|
$user_phid = $user->getPHID();
|
||||||
$actor_phid = $viewer->getPHID();
|
if (!$user->getPHID()) {
|
||||||
|
throw new Exception(
|
||||||
|
pht('You can not attach audit authority for a user with no PHID.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This method is a little weird and sketchy, but worlds better than
|
$this->auditAuthorityPHIDs[$user_phid] = $authority;
|
||||||
// what came before it. Eventually, this should probably live in a Query
|
|
||||||
// class.
|
|
||||||
|
|
||||||
// Figure out which requests the actor has authority over: these are user
|
|
||||||
// requests where they are the auditor, and packages and projects they are
|
|
||||||
// a member of.
|
|
||||||
|
|
||||||
if (!$actor_phid) {
|
|
||||||
$attach_key = $viewer->getCacheFragment();
|
|
||||||
$phids = array();
|
|
||||||
} else {
|
|
||||||
$attach_key = $actor_phid;
|
|
||||||
// At least currently, when modifying your own commits, you act only on
|
|
||||||
// behalf of yourself, not your packages/projects -- the idea being that
|
|
||||||
// you can't accept your own commits. This may change or depend on
|
|
||||||
// config.
|
|
||||||
$actor_is_author = ($actor_phid == $this->getAuthorPHID());
|
|
||||||
if ($actor_is_author) {
|
|
||||||
$phids = array($actor_phid);
|
|
||||||
} else {
|
|
||||||
$phids = array();
|
|
||||||
$phids[$actor_phid] = true;
|
|
||||||
|
|
||||||
$owned_packages = id(new PhabricatorOwnersPackageQuery())
|
|
||||||
->setViewer($viewer)
|
|
||||||
->withAuthorityPHIDs(array($actor_phid))
|
|
||||||
->execute();
|
|
||||||
foreach ($owned_packages as $package) {
|
|
||||||
$phids[$package->getPHID()] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$projects = id(new PhabricatorProjectQuery())
|
|
||||||
->setViewer($viewer)
|
|
||||||
->withMemberPHIDs(array($actor_phid))
|
|
||||||
->execute();
|
|
||||||
foreach ($projects as $project) {
|
|
||||||
$phids[$project->getPHID()] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$phids = array_keys($phids);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->auditAuthorityPHIDs[$attach_key] = array_fuse($phids);
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasAuditAuthority(
|
public function hasAuditAuthority(
|
||||||
PhabricatorUser $viewer,
|
PhabricatorUser $user,
|
||||||
PhabricatorRepositoryAuditRequest $audit,
|
PhabricatorRepositoryAuditRequest $audit) {
|
||||||
$actor_phid = null) {
|
|
||||||
|
|
||||||
if ($actor_phid === null) {
|
$user_phid = $user->getPHID();
|
||||||
$actor_phid = $viewer->getPHID();
|
if (!$user_phid) {
|
||||||
}
|
|
||||||
|
|
||||||
if (!$actor_phid) {
|
|
||||||
$attach_key = $viewer->getCacheFragment();
|
|
||||||
} else {
|
|
||||||
$attach_key = $actor_phid;
|
|
||||||
}
|
|
||||||
|
|
||||||
$map = $this->assertAttachedKey($this->auditAuthorityPHIDs, $attach_key);
|
|
||||||
|
|
||||||
if (!$actor_phid) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$map = $this->assertAttachedKey($this->auditAuthorityPHIDs, $user_phid);
|
||||||
|
|
||||||
return isset($map[$audit->getAuditorPHID()]);
|
return isset($map[$audit->getAuditorPHID()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,8 +61,7 @@ Choose the object **subtype** that this form should create and edit.
|
||||||
EOTEXT
|
EOTEXT
|
||||||
);
|
);
|
||||||
|
|
||||||
$map = $engine->newSubtypeMap();
|
$map = $engine->newSubtypeMap()->getDisplayMap();
|
||||||
$map = mpull($map, 'getName');
|
|
||||||
|
|
||||||
$form = id(new AphrontFormView())
|
$form = id(new AphrontFormView())
|
||||||
->setUser($viewer)
|
->setUser($viewer)
|
||||||
|
|
|
@ -182,7 +182,7 @@ final class PhabricatorEditEngineSubtype
|
||||||
$map[$key] = $subtype;
|
$map[$key] = $subtype;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $map;
|
return new PhabricatorEditEngineSubtypeMap($map);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
final class PhabricatorEditEngineSubtypeMap
|
||||||
|
extends Phobject {
|
||||||
|
|
||||||
|
private $subtypes;
|
||||||
|
|
||||||
|
public function __construct(array $subtypes) {
|
||||||
|
assert_instances_of($subtypes, 'PhabricatorEditEngineSubtype');
|
||||||
|
|
||||||
|
$this->subtypes = $subtypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplayMap() {
|
||||||
|
return mpull($this->subtypes, 'getName');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCount() {
|
||||||
|
return count($this->subtypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isValidSubtype($subtype_key) {
|
||||||
|
return isset($this->subtypes[$subtype_key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubtypes() {
|
||||||
|
return $this->subtypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubtype($subtype_key) {
|
||||||
|
if (!$this->isValidSubtype($subtype_key)) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Subtype key "%s" does not identify a valid subtype.',
|
||||||
|
$subtype_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->subtypes[$subtype_key];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2541,7 +2541,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($map[$new])) {
|
if (!$map->isValidSubtype($new)) {
|
||||||
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
||||||
$transaction_type,
|
$transaction_type,
|
||||||
pht('Invalid'),
|
pht('Invalid'),
|
||||||
|
|
|
@ -64,7 +64,7 @@ final class PhabricatorEditEngineConfigurationEditor
|
||||||
foreach ($xactions as $xaction) {
|
foreach ($xactions as $xaction) {
|
||||||
$new = $xaction->getNewValue();
|
$new = $xaction->getNewValue();
|
||||||
|
|
||||||
if (isset($map[$new])) {
|
if ($map->isValidSubtype($new)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ final class PhabricatorSubtypeEditEngineExtension
|
||||||
$subtype_type = PhabricatorTransactions::TYPE_SUBTYPE;
|
$subtype_type = PhabricatorTransactions::TYPE_SUBTYPE;
|
||||||
|
|
||||||
$map = $object->newEditEngineSubtypeMap();
|
$map = $object->newEditEngineSubtypeMap();
|
||||||
$options = mpull($map, 'getName');
|
$options = $map->getDisplayMap();
|
||||||
|
|
||||||
$subtype_field = id(new PhabricatorSelectEditField())
|
$subtype_field = id(new PhabricatorSelectEditField())
|
||||||
->setKey(self::EDITKEY)
|
->setKey(self::EDITKEY)
|
||||||
|
@ -45,7 +45,7 @@ final class PhabricatorSubtypeEditEngineExtension
|
||||||
|
|
||||||
// If subtypes are configured, enable changing them from the bulk editor
|
// If subtypes are configured, enable changing them from the bulk editor
|
||||||
// and comment action stack.
|
// and comment action stack.
|
||||||
if (count($map) > 1) {
|
if ($map->getCount() > 1) {
|
||||||
$subtype_field
|
$subtype_field
|
||||||
->setBulkEditLabel(pht('Change subtype to'))
|
->setBulkEditLabel(pht('Change subtype to'))
|
||||||
->setCommentActionLabel(pht('Change Subtype'))
|
->setCommentActionLabel(pht('Change Subtype'))
|
||||||
|
|
|
@ -433,6 +433,18 @@ If you do this, **you will lose unreplicated data**. You will discard any
|
||||||
changes on the affected leaders which have not replicated to other devices
|
changes on the affected leaders which have not replicated to other devices
|
||||||
in the cluster.
|
in the cluster.
|
||||||
|
|
||||||
|
If you have lost an entire cluster and replaced it with new devices that you
|
||||||
|
have restored from backups, you can aggressively wipe all memory of the old
|
||||||
|
devices by using `--demote <service>` and `--all-repositories`. **This is
|
||||||
|
dangerous and discards all unreplicated data in any repository on any device.**
|
||||||
|
|
||||||
|
```
|
||||||
|
phabricator/ $ ./bin/repository thaw --demote repo.corp.net --all-repositories
|
||||||
|
```
|
||||||
|
|
||||||
|
After you do this, continue below to promote a leader and restore the cluster
|
||||||
|
to service.
|
||||||
|
|
||||||
|
|
||||||
Ambiguous Leaders
|
Ambiguous Leaders
|
||||||
=================
|
=================
|
||||||
|
|
Loading…
Reference in a new issue