1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-01-09 22:31:03 +01:00

(stable) Promote 2018 Week 49

This commit is contained in:
epriestley 2018-12-09 16:54:34 -08:00
commit 237a2a1909
25 changed files with 464 additions and 211 deletions

View file

@ -2976,6 +2976,7 @@ phutil_register_library_map(array(
'PhabricatorEditEngineStaticCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineStaticCommentAction.php',
'PhabricatorEditEngineSubtype' => 'applications/transactions/editengine/PhabricatorEditEngineSubtype.php',
'PhabricatorEditEngineSubtypeInterface' => 'applications/transactions/editengine/PhabricatorEditEngineSubtypeInterface.php',
'PhabricatorEditEngineSubtypeMap' => 'applications/transactions/editengine/PhabricatorEditEngineSubtypeMap.php',
'PhabricatorEditEngineSubtypeTestCase' => 'applications/transactions/editengine/__tests__/PhabricatorEditEngineSubtypeTestCase.php',
'PhabricatorEditEngineTokenizerCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineTokenizerCommentAction.php',
'PhabricatorEditField' => 'applications/transactions/editfield/PhabricatorEditField.php',
@ -3760,6 +3761,7 @@ phutil_register_library_map(array(
'PhabricatorPeopleAnyOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php',
'PhabricatorPeopleApplication' => 'applications/people/application/PhabricatorPeopleApplication.php',
'PhabricatorPeopleApproveController' => 'applications/people/controller/PhabricatorPeopleApproveController.php',
'PhabricatorPeopleAvailabilitySearchEngineAttachment' => 'applications/people/engineextension/PhabricatorPeopleAvailabilitySearchEngineAttachment.php',
'PhabricatorPeopleBadgesProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php',
'PhabricatorPeopleCommitsProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleCommitsProfileMenuItem.php',
'PhabricatorPeopleController' => 'applications/people/controller/PhabricatorPeopleController.php',
@ -8729,6 +8731,7 @@ phutil_register_library_map(array(
'PhabricatorEditEngineSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorEditEngineStaticCommentAction' => 'PhabricatorEditEngineCommentAction',
'PhabricatorEditEngineSubtype' => 'Phobject',
'PhabricatorEditEngineSubtypeMap' => 'Phobject',
'PhabricatorEditEngineSubtypeTestCase' => 'PhabricatorTestCase',
'PhabricatorEditEngineTokenizerCommentAction' => 'PhabricatorEditEngineCommentAction',
'PhabricatorEditField' => 'Phobject',
@ -9638,6 +9641,7 @@ phutil_register_library_map(array(
'PhabricatorPeopleAnyOwnerDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorPeopleApplication' => 'PhabricatorApplication',
'PhabricatorPeopleApproveController' => 'PhabricatorPeopleController',
'PhabricatorPeopleAvailabilitySearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
'PhabricatorPeopleBadgesProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorPeopleCommitsProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorPeopleController' => 'PhabricatorController',

View file

@ -59,10 +59,6 @@ final class PhabricatorAuditEditor
$this->oldAuditStatus = $object->getAuditStatus();
$object->loadAndAttachAuditAuthority(
$this->getActor(),
$this->getActingAsPHID());
return parent::expandTransactions($object, $xactions);
}

View file

@ -1367,9 +1367,9 @@ final class DifferentialTransactionEditor
foreach (array_chunk($sql, 256) as $chunk) {
queryfx(
$conn_w,
'INSERT INTO %T (repositoryID, pathID, epoch, revisionID) VALUES %Q',
'INSERT INTO %T (repositoryID, pathID, epoch, revisionID) VALUES %LQ',
$table->getTableName(),
implode(', ', $chunk));
$chunk);
}
}
@ -1444,9 +1444,9 @@ final class DifferentialTransactionEditor
if ($sql) {
queryfx(
$conn_w,
'INSERT INTO %T (revisionID, type, hash) VALUES %Q',
'INSERT INTO %T (revisionID, type, hash) VALUES %LQ',
ArcanistDifferentialRevisionHash::TABLE_NAME,
implode(', ', $sql));
$sql);
}
}

View file

@ -45,6 +45,7 @@ final class DiffusionCommitController extends DiffusionController {
->withIdentifiers(array($commit_identifier))
->needCommitData(true)
->needAuditRequests(true)
->needAuditAuthority(array($viewer))
->setLimit(100)
->needIdentities(true)
->execute();
@ -111,7 +112,6 @@ final class DiffusionCommitController extends DiffusionController {
}
$audit_requests = $commit->getAudits();
$commit->loadAndAttachAuditAuthority($viewer);
$commit_data = $commit->getCommitData();
$is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');

View file

@ -43,9 +43,12 @@ final class DiffusionCommitEditEngine
}
protected function newObjectQuery() {
$viewer = $this->getViewer();
return id(new DiffusionCommitQuery())
->needCommitData(true)
->needAuditRequests(true);
->needAuditRequests(true)
->needAuditAuthority(array($viewer));
}
protected function getEditorURI() {

View file

@ -17,6 +17,7 @@ final class DiffusionCommitQuery
private $unreachable;
private $needAuditRequests;
private $needAuditAuthority;
private $auditIDs;
private $auditorPHIDs;
private $epochMin;
@ -121,6 +122,12 @@ final class DiffusionCommitQuery
return $this;
}
public function needAuditAuthority(array $users) {
assert_instances_of($users, 'PhabricatorUser');
$this->needAuditAuthority = $users;
return $this;
}
public function withAuditIDs(array $ids) {
$this->auditIDs = $ids;
return $this;
@ -231,14 +238,27 @@ final class DiffusionCommitQuery
}
if (count($subqueries) > 1) {
foreach ($subqueries as $key => $subquery) {
$subqueries[$key] = '('.$subquery.')';
$unions = null;
foreach ($subqueries as $subquery) {
if (!$unions) {
$unions = qsprintf(
$conn,
'(%Q)',
$subquery);
continue;
}
$unions = qsprintf(
$conn,
'%Q UNION DISTINCT (%Q)',
$unions,
$subquery);
}
$query = qsprintf(
$conn,
'%Q %Q %Q',
implode(' UNION DISTINCT ', $subqueries),
$unions,
$this->buildOrderClause($conn, true),
$this->buildLimitClause($conn));
} else {
@ -423,6 +443,72 @@ final class DiffusionCommitQuery
$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;
}

View file

@ -649,7 +649,7 @@ final class ManiphestTransactionEditor
$old_value = $object->getOwnerPHID();
$new_value = $xaction->getNewValue();
if ($old_value === $new_value) {
continue;
break;
}
// When a task is reassigned, move the old owner to the subscriber

View file

@ -47,7 +47,7 @@ final class ManiphestTaskSearchEngine
// 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.
$subtype_map = id(new ManiphestTask())->newEditEngineSubtypeMap();
$hide_subtypes = (count($subtype_map) == 1);
$hide_subtypes = ($subtype_map->getCount() == 1);
return array(
id(new PhabricatorOwnersSearchField())

View file

@ -573,7 +573,7 @@ final class ManiphestTask extends ManiphestDAO
public function newSubtypeObject() {
$subtype_key = $this->getEditEngineSubtype();
$subtype_map = $this->newEditEngineSubtypeMap();
return idx($subtype_map, $subtype_key);
return $subtype_map->getSubtype($subtype_key);
}
/* -( PhabricatorFulltextInterface )--------------------------------------- */

View file

@ -214,11 +214,12 @@ final class ManiphestTransaction
public function renderSubtypeName($value) {
$object = $this->getObject();
$map = $object->newEditEngineSubtypeMap();
if (!isset($map[$value])) {
if (!$map->isValidSubtype($value)) {
return $value;
}
return $map[$value]->getName();
return $map->getSubtype($value)->getName();
}
}

View file

@ -28,7 +28,7 @@ final class ManiphestTaskSubtypeDatasource
$results = array();
$subtype_map = id(new ManiphestTask())->newEditEngineSubtypeMap();
foreach ($subtype_map as $key => $subtype) {
foreach ($subtype_map->getSubtypes() as $key => $subtype) {
$result = id(new PhabricatorTypeaheadResult())
->setIcon($subtype->getIcon())

View file

@ -56,9 +56,6 @@ final class ManiphestTaskListView extends ManiphestView {
Javelin::initBehavior('maniphest-list-editor');
}
$subtype_map = id(new ManiphestTask())
->newEditEngineSubtypeMap();
foreach ($this->tasks as $task) {
$item = id(new PHUIObjectItemView())
->setUser($this->getUser())

View file

@ -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,
);
}
}

View file

@ -1466,7 +1466,10 @@ final class PhabricatorUser
}
public function getConduitSearchAttachments() {
return array();
return array(
id(new PhabricatorPeopleAvailabilitySearchEngineAttachment())
->setAttachmentKey('availability'),
);
}

View file

@ -15,10 +15,11 @@ final class PhabricatorRepositoryManagementThawWorkflow
array(
array(
'name' => 'demote',
'param' => 'device',
'param' => 'device/service',
'help' => pht(
'Demote a device, discarding local changes. Clears stuck '.
'write locks and recovers from lost leaders.'),
'Demote a device (or all devices in a service) discarding '.
'local changes. Clears stuck write locks and recovers from '.
'lost leaders.'),
),
array(
'name' => 'promote',
@ -32,6 +33,12 @@ final class PhabricatorRepositoryManagementThawWorkflow
'name' => 'force',
'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(
'name' => 'repositories',
'wildcard' => true,
@ -42,12 +49,6 @@ final class PhabricatorRepositoryManagementThawWorkflow
public function execute(PhutilArgumentParser $args) {
$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');
$demote = $args->getArg('demote');
@ -61,17 +62,109 @@ final class PhabricatorRepositoryManagementThawWorkflow
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)
->withNames(array($device_name))
->executeOne();
if (!$device) {
throw new PhutilArgumentUsageException(
pht('No device "%s" exists.', $device_name));
->withNames(array($target_name))
->execute();
if (!$devices) {
$service = id(new AlmanacServiceQuery())
->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) {
$risk_message = pht(
'Promoting a device can cause the loss of any repository data which '.
@ -96,126 +189,128 @@ final class PhabricatorRepositoryManagementThawWorkflow
pht('User aborted the workflow.'));
}
foreach ($repositories as $repository) {
$repository_phid = $repository->getPHID();
foreach ($devices as $device) {
foreach ($repositories as $repository) {
$repository_phid = $repository->getPHID();
$write_lock = PhabricatorRepositoryWorkingCopyVersion::getWriteLock(
$repository_phid);
$write_lock = PhabricatorRepositoryWorkingCopyVersion::getWriteLock(
$repository_phid);
echo tsprintf(
"%s\n",
pht(
'Waiting to acquire write lock for "%s"...',
$repository->getDisplayName()));
echo tsprintf(
"%s\n",
pht(
'Waiting to acquire write lock for "%s"...',
$repository->getDisplayName()));
$write_lock->lock(phutil_units('5 minutes in seconds'));
try {
$write_lock->lock(phutil_units('5 minutes in seconds'));
try {
$service = $repository->loadAlmanacService();
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()])) {
$service = $repository->loadAlmanacService();
if (!$service) {
throw new PhutilArgumentUsageException(
pht(
'Repository "%s" has no active binding to device "%s". Only '.
'actively bound devices can be promoted.',
$repository->getDisplayName(),
$device->getName()));
'Repository "%s" is not a cluster repository: it is not '.
'bound to an Almanac service.',
$repository->getDisplayName()));
}
$versions = PhabricatorRepositoryWorkingCopyVersion::loadVersions(
$repository->getPHID());
$versions = mpull($versions, null, 'getDevicePHID');
// Before we promote, make sure there are no outstanding versions on
// devices with inactive bindings. If there are, you need to demote
// these first.
$inactive = array();
foreach ($versions as $device_phid => $version) {
if (isset($bindings[$device_phid])) {
continue;
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(
pht(
'Repository "%s" has no active binding to device "%s". '.
'Only actively bound devices can be promoted.',
$repository->getDisplayName(),
$device->getName()));
}
$inactive[$device_phid] = $version;
}
if ($inactive) {
$handles = $viewer->loadHandles(array_keys($inactive));
$versions = PhabricatorRepositoryWorkingCopyVersion::loadVersions(
$repository->getPHID());
$versions = mpull($versions, null, 'getDevicePHID');
$handle_list = iterator_to_array($handles);
$handle_list = mpull($handle_list, 'getName');
$handle_list = implode(', ', $handle_list);
// Before we promote, make sure there are no outstanding versions
// on devices with inactive bindings. If there are, you need to
// 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(
'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.',
'Promoted "%s" to become a leader for "%s".',
$device->getName(),
$repository->getDisplayName(),
$handle_list));
$repository->getDisplayName()));
}
PhabricatorRepositoryWorkingCopyVersion::updateVersion(
$repository->getPHID(),
$device->getPHID(),
0);
if ($demote) {
PhabricatorRepositoryWorkingCopyVersion::demoteDevice(
$repository->getPHID(),
$device->getPHID());
echo tsprintf(
"%s\n",
pht(
'Promoted "%s" to become a leader for "%s".',
$device->getName(),
$repository->getDisplayName()));
echo tsprintf(
"%s\n",
pht(
'Demoted "%s" from leadership of repository "%s".',
$device->getName(),
$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();
throw $ex;
}
$write_lock->unlock();
}
return 0;

View file

@ -44,6 +44,15 @@ final class PhabricatorRepositorySearchEngine
->setKey('uris')
->setDescription(
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']);
}
if ($map['almanacServicePHIDs']) {
$query->withAlmanacServicePHIDs($map['almanacServicePHIDs']);
}
return $query;
}

View file

@ -2773,6 +2773,13 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
->setDescription(
pht(
'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(),
'status' => $this->getStatus(),
'isImporting' => (bool)$this->isImporting(),
'almanacServicePHID' => $this->getAlmanacServicePHID(),
);
}

View file

@ -210,84 +210,32 @@ final class PhabricatorRepositoryCommit
return $this->assertAttached($this->committerIdentity);
}
public function loadAndAttachAuditAuthority(
PhabricatorUser $viewer,
$actor_phid = null) {
public function attachAuditAuthority(
PhabricatorUser $user,
array $authority) {
if ($actor_phid === null) {
$actor_phid = $viewer->getPHID();
$user_phid = $user->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
// 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);
$this->auditAuthorityPHIDs[$user_phid] = $authority;
return $this;
}
public function hasAuditAuthority(
PhabricatorUser $viewer,
PhabricatorRepositoryAuditRequest $audit,
$actor_phid = null) {
PhabricatorUser $user,
PhabricatorRepositoryAuditRequest $audit) {
if ($actor_phid === null) {
$actor_phid = $viewer->getPHID();
}
if (!$actor_phid) {
$attach_key = $viewer->getCacheFragment();
} else {
$attach_key = $actor_phid;
}
$map = $this->assertAttachedKey($this->auditAuthorityPHIDs, $attach_key);
if (!$actor_phid) {
$user_phid = $user->getPHID();
if (!$user_phid) {
return false;
}
$map = $this->assertAttachedKey($this->auditAuthorityPHIDs, $user_phid);
return isset($map[$audit->getAuditorPHID()]);
}

View file

@ -61,8 +61,7 @@ Choose the object **subtype** that this form should create and edit.
EOTEXT
);
$map = $engine->newSubtypeMap();
$map = mpull($map, 'getName');
$map = $engine->newSubtypeMap()->getDisplayMap();
$form = id(new AphrontFormView())
->setUser($viewer)

View file

@ -182,7 +182,7 @@ final class PhabricatorEditEngineSubtype
$map[$key] = $subtype;
}
return $map;
return new PhabricatorEditEngineSubtypeMap($map);
}
}

View file

@ -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];
}
}

View file

@ -2541,7 +2541,7 @@ abstract class PhabricatorApplicationTransactionEditor
continue;
}
if (!isset($map[$new])) {
if (!$map->isValidSubtype($new)) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$transaction_type,
pht('Invalid'),

View file

@ -64,7 +64,7 @@ final class PhabricatorEditEngineConfigurationEditor
foreach ($xactions as $xaction) {
$new = $xaction->getNewValue();
if (isset($map[$new])) {
if ($map->isValidSubtype($new)) {
continue;
}

View file

@ -31,7 +31,7 @@ final class PhabricatorSubtypeEditEngineExtension
$subtype_type = PhabricatorTransactions::TYPE_SUBTYPE;
$map = $object->newEditEngineSubtypeMap();
$options = mpull($map, 'getName');
$options = $map->getDisplayMap();
$subtype_field = id(new PhabricatorSelectEditField())
->setKey(self::EDITKEY)
@ -45,7 +45,7 @@ final class PhabricatorSubtypeEditEngineExtension
// If subtypes are configured, enable changing them from the bulk editor
// and comment action stack.
if (count($map) > 1) {
if ($map->getCount() > 1) {
$subtype_field
->setBulkEditLabel(pht('Change subtype to'))
->setCommentActionLabel(pht('Change Subtype'))

View file

@ -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
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
=================