1
0
Fork 0
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:
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', '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',

View file

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

View file

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

View file

@ -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');

View file

@ -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() {

View file

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

View file

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

View file

@ -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())

View file

@ -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 )--------------------------------------- */

View file

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

View file

@ -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())

View file

@ -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())

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() { public function getConduitSearchAttachments() {
return array(); return array(
id(new PhabricatorPeopleAvailabilitySearchEngineAttachment())
->setAttachmentKey('availability'),
);
} }

View file

@ -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))
->execute();
if (!$devices) {
$service = id(new AlmanacServiceQuery())
->setViewer($viewer)
->withNames(array($target_name))
->executeOne(); ->executeOne();
if (!$device) {
if (!$service) {
throw new PhutilArgumentUsageException( throw new PhutilArgumentUsageException(
pht('No device "%s" exists.', $device_name)); 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,6 +189,7 @@ final class PhabricatorRepositoryManagementThawWorkflow
pht('User aborted the workflow.')); pht('User aborted the workflow.'));
} }
foreach ($devices as $device) {
foreach ($repositories as $repository) { foreach ($repositories as $repository) {
$repository_phid = $repository->getPHID(); $repository_phid = $repository->getPHID();
@ -128,8 +222,8 @@ final class PhabricatorRepositoryManagementThawWorkflow
if (empty($bindings[$device->getPHID()])) { 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" has no active binding to device "%s". '.
'actively bound devices can be promoted.', 'Only actively bound devices can be promoted.',
$repository->getDisplayName(), $repository->getDisplayName(),
$device->getName())); $device->getName()));
} }
@ -138,9 +232,9 @@ final class PhabricatorRepositoryManagementThawWorkflow
$repository->getPHID()); $repository->getPHID());
$versions = mpull($versions, null, 'getDevicePHID'); $versions = mpull($versions, null, 'getDevicePHID');
// Before we promote, make sure there are no outstanding versions on // Before we promote, make sure there are no outstanding versions
// devices with inactive bindings. If there are, you need to demote // on devices with inactive bindings. If there are, you need to
// these first. // demote these first.
$inactive = array(); $inactive = array();
foreach ($versions as $device_phid => $version) { foreach ($versions as $device_phid => $version) {
if (isset($bindings[$device_phid])) { if (isset($bindings[$device_phid])) {
@ -166,8 +260,8 @@ final class PhabricatorRepositoryManagementThawWorkflow
} }
// Now, make sure there are no outstanding versions on devices with // Now, make sure there are no outstanding versions on devices with
// active bindings. These also need to be demoted (or promoting is a // active bindings. These also need to be demoted (or promoting is
// mistake or already happened). // a mistake or already happened).
$active = array_select_keys($versions, array_keys($bindings)); $active = array_select_keys($versions, array_keys($bindings));
if ($active) { if ($active) {
$handles = $viewer->loadHandles(array_keys($active)); $handles = $viewer->loadHandles(array_keys($active));
@ -217,6 +311,7 @@ final class PhabricatorRepositoryManagementThawWorkflow
$write_lock->unlock(); $write_lock->unlock();
} }
}
return 0; return 0;
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -182,7 +182,7 @@ final class PhabricatorEditEngineSubtype
$map[$key] = $subtype; $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; continue;
} }
if (!isset($map[$new])) { if (!$map->isValidSubtype($new)) {
$errors[] = new PhabricatorApplicationTransactionValidationError( $errors[] = new PhabricatorApplicationTransactionValidationError(
$transaction_type, $transaction_type,
pht('Invalid'), pht('Invalid'),

View file

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

View file

@ -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'))

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