1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-23 14:00:56 +01:00

Allow comment actions to be grouped; group Differential "Review" and "Revision" actions

Summary:
Ref T11114. Differential has more actions than it once did, and may have further actions in the future.

Make this dropdown a little easier to parse by grouping similar types of actions, like "Accept" and "Reject".

(The action order still needs to be tweaked a bit.)

Test Plan: {F2274526}

Reviewers: chad

Reviewed By: chad

Subscribers: eadler

Maniphest Tasks: T11114

Differential Revision: https://secure.phabricator.com/D17114
This commit is contained in:
epriestley 2016-12-29 13:14:09 -08:00
parent 5a6643f36f
commit 48fcfeadaf
12 changed files with 262 additions and 122 deletions

View file

@ -557,6 +557,7 @@ phutil_register_library_map(array(
'DifferentialRevisionRequiredActionResultBucket' => 'applications/differential/query/DifferentialRevisionRequiredActionResultBucket.php', 'DifferentialRevisionRequiredActionResultBucket' => 'applications/differential/query/DifferentialRevisionRequiredActionResultBucket.php',
'DifferentialRevisionResignTransaction' => 'applications/differential/xaction/DifferentialRevisionResignTransaction.php', 'DifferentialRevisionResignTransaction' => 'applications/differential/xaction/DifferentialRevisionResignTransaction.php',
'DifferentialRevisionResultBucket' => 'applications/differential/query/DifferentialRevisionResultBucket.php', 'DifferentialRevisionResultBucket' => 'applications/differential/query/DifferentialRevisionResultBucket.php',
'DifferentialRevisionReviewTransaction' => 'applications/differential/xaction/DifferentialRevisionReviewTransaction.php',
'DifferentialRevisionReviewersHeraldField' => 'applications/differential/herald/DifferentialRevisionReviewersHeraldField.php', 'DifferentialRevisionReviewersHeraldField' => 'applications/differential/herald/DifferentialRevisionReviewersHeraldField.php',
'DifferentialRevisionReviewersTransaction' => 'applications/differential/xaction/DifferentialRevisionReviewersTransaction.php', 'DifferentialRevisionReviewersTransaction' => 'applications/differential/xaction/DifferentialRevisionReviewersTransaction.php',
'DifferentialRevisionSearchConduitAPIMethod' => 'applications/differential/conduit/DifferentialRevisionSearchConduitAPIMethod.php', 'DifferentialRevisionSearchConduitAPIMethod' => 'applications/differential/conduit/DifferentialRevisionSearchConduitAPIMethod.php',
@ -2554,6 +2555,7 @@ phutil_register_library_map(array(
'PhabricatorEditEngineAPIMethod' => 'applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php', 'PhabricatorEditEngineAPIMethod' => 'applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php',
'PhabricatorEditEngineColumnsCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineColumnsCommentAction.php', 'PhabricatorEditEngineColumnsCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineColumnsCommentAction.php',
'PhabricatorEditEngineCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineCommentAction.php', 'PhabricatorEditEngineCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineCommentAction.php',
'PhabricatorEditEngineCommentActionGroup' => 'applications/transactions/commentaction/PhabricatorEditEngineCommentActionGroup.php',
'PhabricatorEditEngineConfiguration' => 'applications/transactions/storage/PhabricatorEditEngineConfiguration.php', 'PhabricatorEditEngineConfiguration' => 'applications/transactions/storage/PhabricatorEditEngineConfiguration.php',
'PhabricatorEditEngineConfigurationDefaultCreateController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationDefaultCreateController.php', 'PhabricatorEditEngineConfigurationDefaultCreateController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationDefaultCreateController.php',
'PhabricatorEditEngineConfigurationDefaultsController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationDefaultsController.php', 'PhabricatorEditEngineConfigurationDefaultsController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationDefaultsController.php',
@ -5183,7 +5185,7 @@ phutil_register_library_map(array(
'PhabricatorConduitResultInterface', 'PhabricatorConduitResultInterface',
), ),
'DifferentialRevisionAbandonTransaction' => 'DifferentialRevisionActionTransaction', 'DifferentialRevisionAbandonTransaction' => 'DifferentialRevisionActionTransaction',
'DifferentialRevisionAcceptTransaction' => 'DifferentialRevisionActionTransaction', 'DifferentialRevisionAcceptTransaction' => 'DifferentialRevisionReviewTransaction',
'DifferentialRevisionActionTransaction' => 'DifferentialRevisionTransactionType', 'DifferentialRevisionActionTransaction' => 'DifferentialRevisionTransactionType',
'DifferentialRevisionAffectedFilesHeraldField' => 'DifferentialRevisionHeraldField', 'DifferentialRevisionAffectedFilesHeraldField' => 'DifferentialRevisionHeraldField',
'DifferentialRevisionAuthorHeraldField' => 'DifferentialRevisionHeraldField', 'DifferentialRevisionAuthorHeraldField' => 'DifferentialRevisionHeraldField',
@ -5223,7 +5225,7 @@ phutil_register_library_map(array(
'DifferentialRevisionPlanChangesTransaction' => 'DifferentialRevisionActionTransaction', 'DifferentialRevisionPlanChangesTransaction' => 'DifferentialRevisionActionTransaction',
'DifferentialRevisionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'DifferentialRevisionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'DifferentialRevisionReclaimTransaction' => 'DifferentialRevisionActionTransaction', 'DifferentialRevisionReclaimTransaction' => 'DifferentialRevisionActionTransaction',
'DifferentialRevisionRejectTransaction' => 'DifferentialRevisionActionTransaction', 'DifferentialRevisionRejectTransaction' => 'DifferentialRevisionReviewTransaction',
'DifferentialRevisionRelationship' => 'PhabricatorObjectRelationship', 'DifferentialRevisionRelationship' => 'PhabricatorObjectRelationship',
'DifferentialRevisionRelationshipSource' => 'PhabricatorObjectRelationshipSource', 'DifferentialRevisionRelationshipSource' => 'PhabricatorObjectRelationshipSource',
'DifferentialRevisionReopenTransaction' => 'DifferentialRevisionActionTransaction', 'DifferentialRevisionReopenTransaction' => 'DifferentialRevisionActionTransaction',
@ -5232,8 +5234,9 @@ phutil_register_library_map(array(
'DifferentialRevisionRepositoryTransaction' => 'DifferentialRevisionTransactionType', 'DifferentialRevisionRepositoryTransaction' => 'DifferentialRevisionTransactionType',
'DifferentialRevisionRequestReviewTransaction' => 'DifferentialRevisionActionTransaction', 'DifferentialRevisionRequestReviewTransaction' => 'DifferentialRevisionActionTransaction',
'DifferentialRevisionRequiredActionResultBucket' => 'DifferentialRevisionResultBucket', 'DifferentialRevisionRequiredActionResultBucket' => 'DifferentialRevisionResultBucket',
'DifferentialRevisionResignTransaction' => 'DifferentialRevisionActionTransaction', 'DifferentialRevisionResignTransaction' => 'DifferentialRevisionReviewTransaction',
'DifferentialRevisionResultBucket' => 'PhabricatorSearchResultBucket', 'DifferentialRevisionResultBucket' => 'PhabricatorSearchResultBucket',
'DifferentialRevisionReviewTransaction' => 'DifferentialRevisionActionTransaction',
'DifferentialRevisionReviewersHeraldField' => 'DifferentialRevisionHeraldField', 'DifferentialRevisionReviewersHeraldField' => 'DifferentialRevisionHeraldField',
'DifferentialRevisionReviewersTransaction' => 'DifferentialRevisionTransactionType', 'DifferentialRevisionReviewersTransaction' => 'DifferentialRevisionTransactionType',
'DifferentialRevisionSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'DifferentialRevisionSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
@ -7536,6 +7539,7 @@ phutil_register_library_map(array(
'PhabricatorEditEngineAPIMethod' => 'ConduitAPIMethod', 'PhabricatorEditEngineAPIMethod' => 'ConduitAPIMethod',
'PhabricatorEditEngineColumnsCommentAction' => 'PhabricatorEditEngineCommentAction', 'PhabricatorEditEngineColumnsCommentAction' => 'PhabricatorEditEngineCommentAction',
'PhabricatorEditEngineCommentAction' => 'Phobject', 'PhabricatorEditEngineCommentAction' => 'Phobject',
'PhabricatorEditEngineCommentActionGroup' => 'Phobject',
'PhabricatorEditEngineConfiguration' => array( 'PhabricatorEditEngineConfiguration' => array(
'PhabricatorSearchDAO', 'PhabricatorSearchDAO',
'PhabricatorApplicationTransactionInterface', 'PhabricatorApplicationTransactionInterface',

View file

@ -9,6 +9,9 @@ final class DifferentialRevisionEditEngine
const KEY_UPDATE = 'update'; const KEY_UPDATE = 'update';
const ACTIONGROUP_REVIEW = 'review';
const ACTIONGROUP_REVISION = 'revision';
public function getEngineName() { public function getEngineName() {
return pht('Revisions'); return pht('Revisions');
} }
@ -87,6 +90,17 @@ final class DifferentialRevisionEditEngine
return $this->diff; return $this->diff;
} }
protected function newCommentActionGroups() {
return array(
id(new PhabricatorEditEngineCommentActionGroup())
->setKey(self::ACTIONGROUP_REVIEW)
->setLabel(pht('Review Actions')),
id(new PhabricatorEditEngineCommentActionGroup())
->setKey(self::ACTIONGROUP_REVISION)
->setLabel(pht('Revision Actions')),
);
}
protected function buildCustomEditFields($object) { protected function buildCustomEditFields($object) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();

View file

@ -1,7 +1,7 @@
<?php <?php
final class DifferentialRevisionAcceptTransaction final class DifferentialRevisionAcceptTransaction
extends DifferentialRevisionActionTransaction { extends DifferentialRevisionReviewTransaction {
const TRANSACTIONTYPE = 'differential.revision.accept'; const TRANSACTIONTYPE = 'differential.revision.accept';
const ACTIONKEY = 'accept'; const ACTIONKEY = 'accept';

View file

@ -19,6 +19,10 @@ abstract class DifferentialRevisionActionTransaction
abstract protected function validateAction($object, PhabricatorUser $viewer); abstract protected function validateAction($object, PhabricatorUser $viewer);
abstract protected function getRevisionActionLabel(); abstract protected function getRevisionActionLabel();
protected function getRevisionActionGroupKey() {
return DifferentialRevisionEditEngine::ACTIONGROUP_REVISION;
}
protected function getRevisionActionDescription() { protected function getRevisionActionDescription() {
return null; return null;
} }
@ -41,116 +45,6 @@ abstract class DifferentialRevisionActionTransaction
return ($viewer->getPHID() === $revision->getAuthorPHID()); return ($viewer->getPHID() === $revision->getAuthorPHID());
} }
protected function isViewerAnyReviewer(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return ($this->getViewerReviewerStatus($revision, $viewer) !== null);
}
protected function isViewerAcceptingReviewer(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return $this->isViewerReviewerStatusAmong(
$revision,
$viewer,
array(
DifferentialReviewerStatus::STATUS_ACCEPTED,
));
}
protected function isViewerRejectingReviewer(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return $this->isViewerReviewerStatusAmong(
$revision,
$viewer,
array(
DifferentialReviewerStatus::STATUS_REJECTED,
));
}
protected function getViewerReviewerStatus(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
if (!$viewer->getPHID()) {
return null;
}
foreach ($revision->getReviewerStatus() as $reviewer) {
if ($reviewer->getReviewerPHID() != $viewer->getPHID()) {
continue;
}
return $reviewer->getStatus();
}
return null;
}
protected function isViewerReviewerStatusAmong(
DifferentialRevision $revision,
PhabricatorUser $viewer,
array $status_list) {
$status = $this->getViewerReviewerStatus($revision, $viewer);
if ($status === null) {
return false;
}
$status_map = array_fuse($status_list);
return isset($status_map[$status]);
}
protected function applyReviewerEffect(
DifferentialRevision $revision,
PhabricatorUser $viewer,
$value,
$status) {
$map = array();
// When you accept or reject, you may accept or reject on behalf of all
// reviewers you have authority for. When you resign, you only affect
// yourself.
$with_authority = ($status != DifferentialReviewerStatus::STATUS_RESIGNED);
if ($with_authority) {
foreach ($revision->getReviewerStatus() as $reviewer) {
if ($reviewer->hasAuthority($viewer)) {
$map[$reviewer->getReviewerPHID()] = $status;
}
}
}
// In all cases, you affect yourself.
$map[$viewer->getPHID()] = $status;
// Convert reviewer statuses into edge data.
foreach ($map as $reviewer_phid => $reviewer_status) {
$map[$reviewer_phid] = array(
'data' => array(
'status' => $reviewer_status,
),
);
}
$src_phid = $revision->getPHID();
$edge_type = DifferentialRevisionHasReviewerEdgeType::EDGECONST;
$editor = new PhabricatorEdgeEditor();
foreach ($map as $dst_phid => $edge_data) {
if ($status == DifferentialReviewerStatus::STATUS_RESIGNED) {
// TODO: For now, we just remove these reviewers. In the future, we will
// store resignations explicitly.
$editor->removeEdge($src_phid, $edge_type, $dst_phid);
} else {
$editor->addEdge($src_phid, $edge_type, $dst_phid, $edge_data);
}
}
$editor->save();
}
public function newEditField( public function newEditField(
DifferentialRevision $revision, DifferentialRevision $revision,
PhabricatorUser $viewer) { PhabricatorUser $viewer) {
@ -167,6 +61,9 @@ abstract class DifferentialRevisionActionTransaction
$description = $this->getRevisionActionDescription(); $description = $this->getRevisionActionDescription();
$field->setActionDescription($description); $field->setActionDescription($description);
$group_key = $this->getRevisionActionGroupKey();
$field->setCommentActionGroupKey($group_key);
} }
} }

View file

@ -1,7 +1,7 @@
<?php <?php
final class DifferentialRevisionRejectTransaction final class DifferentialRevisionRejectTransaction
extends DifferentialRevisionActionTransaction { extends DifferentialRevisionReviewTransaction {
const TRANSACTIONTYPE = 'differential.revision.reject'; const TRANSACTIONTYPE = 'differential.revision.reject';
const ACTIONKEY = 'reject'; const ACTIONKEY = 'reject';

View file

@ -1,7 +1,7 @@
<?php <?php
final class DifferentialRevisionResignTransaction final class DifferentialRevisionResignTransaction
extends DifferentialRevisionActionTransaction { extends DifferentialRevisionReviewTransaction {
const TRANSACTIONTYPE = 'differential.revision.resign'; const TRANSACTIONTYPE = 'differential.revision.resign';
const ACTIONKEY = 'resign'; const ACTIONKEY = 'resign';

View file

@ -0,0 +1,120 @@
<?php
abstract class DifferentialRevisionReviewTransaction
extends DifferentialRevisionActionTransaction {
protected function getRevisionActionGroupKey() {
return DifferentialRevisionEditEngine::ACTIONGROUP_REVIEW;
}
protected function isViewerAnyReviewer(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return ($this->getViewerReviewerStatus($revision, $viewer) !== null);
}
protected function isViewerAcceptingReviewer(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return $this->isViewerReviewerStatusAmong(
$revision,
$viewer,
array(
DifferentialReviewerStatus::STATUS_ACCEPTED,
));
}
protected function isViewerRejectingReviewer(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return $this->isViewerReviewerStatusAmong(
$revision,
$viewer,
array(
DifferentialReviewerStatus::STATUS_REJECTED,
));
}
protected function getViewerReviewerStatus(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
if (!$viewer->getPHID()) {
return null;
}
foreach ($revision->getReviewerStatus() as $reviewer) {
if ($reviewer->getReviewerPHID() != $viewer->getPHID()) {
continue;
}
return $reviewer->getStatus();
}
return null;
}
protected function isViewerReviewerStatusAmong(
DifferentialRevision $revision,
PhabricatorUser $viewer,
array $status_list) {
$status = $this->getViewerReviewerStatus($revision, $viewer);
if ($status === null) {
return false;
}
$status_map = array_fuse($status_list);
return isset($status_map[$status]);
}
protected function applyReviewerEffect(
DifferentialRevision $revision,
PhabricatorUser $viewer,
$value,
$status) {
$map = array();
// When you accept or reject, you may accept or reject on behalf of all
// reviewers you have authority for. When you resign, you only affect
// yourself.
$with_authority = ($status != DifferentialReviewerStatus::STATUS_RESIGNED);
if ($with_authority) {
foreach ($revision->getReviewerStatus() as $reviewer) {
if ($reviewer->hasAuthority($viewer)) {
$map[$reviewer->getReviewerPHID()] = $status;
}
}
}
// In all cases, you affect yourself.
$map[$viewer->getPHID()] = $status;
// Convert reviewer statuses into edge data.
foreach ($map as $reviewer_phid => $reviewer_status) {
$map[$reviewer_phid] = array(
'data' => array(
'status' => $reviewer_status,
),
);
}
$src_phid = $revision->getPHID();
$edge_type = DifferentialRevisionHasReviewerEdgeType::EDGECONST;
$editor = new PhabricatorEdgeEditor();
foreach ($map as $dst_phid => $edge_data) {
if ($status == DifferentialReviewerStatus::STATUS_RESIGNED) {
// TODO: For now, we just remove these reviewers. In the future, we will
// store resignations explicitly.
$editor->removeEdge($src_phid, $edge_type, $dst_phid);
} else {
$editor->addEdge($src_phid, $edge_type, $dst_phid, $edge_data);
}
}
$editor->save();
}
}

View file

@ -7,6 +7,7 @@ abstract class PhabricatorEditEngineCommentAction extends Phobject {
private $value; private $value;
private $initialValue; private $initialValue;
private $order; private $order;
private $groupKey;
abstract public function getPHUIXControlType(); abstract public function getPHUIXControlType();
abstract public function getPHUIXControlSpecification(); abstract public function getPHUIXControlSpecification();
@ -20,6 +21,15 @@ abstract class PhabricatorEditEngineCommentAction extends Phobject {
return $this->key; return $this->key;
} }
public function setGroupKey($group_key) {
$this->groupKey = $group_key;
return $this;
}
public function getGroupKey() {
return $this->groupKey;
}
public function setLabel($label) { public function setLabel($label) {
$this->label = $label; $this->label = $label;
return $this; return $this;

View file

@ -0,0 +1,27 @@
<?php
final class PhabricatorEditEngineCommentActionGroup
extends Phobject {
private $key;
private $label;
public function setKey($key) {
$this->key = $key;
return $this;
}
public function getKey() {
return $this->key;
}
public function setLabel($label) {
$this->label = $label;
return $this;
}
public function getLabel() {
return $this->label;
}
}

View file

@ -1549,6 +1549,9 @@ abstract class PhabricatorEditEngine
$view->setCommentActions($comment_actions); $view->setCommentActions($comment_actions);
$comment_groups = $this->newCommentActionGroups();
$view->setCommentActionGroups($comment_groups);
return $view; return $view;
} }
@ -2157,6 +2160,10 @@ abstract class PhabricatorEditEngine
return $request->getURIData('editAction'); return $request->getURIData('editAction');
} }
protected function newCommentActionGroups() {
return array();
}
/* -( Form Pages )--------------------------------------------------------- */ /* -( Form Pages )--------------------------------------------------------- */

View file

@ -25,6 +25,7 @@ abstract class PhabricatorEditField extends Phobject {
private $commentActionLabel; private $commentActionLabel;
private $commentActionValue; private $commentActionValue;
private $commentActionGroupKey;
private $commentActionOrder = 1000; private $commentActionOrder = 1000;
private $hasCommentActionValue; private $hasCommentActionValue;
@ -245,6 +246,15 @@ abstract class PhabricatorEditField extends Phobject {
return $this->commentActionLabel; return $this->commentActionLabel;
} }
public function setCommentActionGroupKey($key) {
$this->commentActionGroupKey = $key;
return $this;
}
public function getCommentActionGroupKey() {
return $this->commentActionGroupKey;
}
public function setCommentActionOrder($order) { public function setCommentActionOrder($order) {
$this->commentActionOrder = $order; $this->commentActionOrder = $order;
return $this; return $this;
@ -719,7 +729,8 @@ abstract class PhabricatorEditField extends Phobject {
->setKey($this->getKey()) ->setKey($this->getKey())
->setLabel($label) ->setLabel($label)
->setValue($this->getValueForCommentAction($value)) ->setValue($this->getValueForCommentAction($value))
->setOrder($this->getCommentActionOrder()); ->setOrder($this->getCommentActionOrder())
->setGroupKey($this->getCommentActionGroupKey());
return $action; return $action;
} }

View file

@ -24,6 +24,7 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView {
private $currentVersion; private $currentVersion;
private $versionedDraft; private $versionedDraft;
private $commentActions; private $commentActions;
private $commentActionGroups = array();
private $transactionTimeline; private $transactionTimeline;
public function setObjectPHID($object_phid) { public function setObjectPHID($object_phid) {
@ -118,6 +119,16 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView {
return $this->commentActions; return $this->commentActions;
} }
public function setCommentActionGroups(array $groups) {
assert_instances_of($groups, 'PhabricatorEditEngineCommentActionGroup');
$this->commentActionGroups = $groups;
return $this;
}
public function getCommentActionGroups() {
return $this->commentActionGroups;
}
public function setNoPermission($no_permission) { public function setNoPermission($no_permission) {
$this->noPermission = $no_permission; $this->noPermission = $no_permission;
return $this; return $this;
@ -279,16 +290,13 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView {
'type' => $comment_action->getPHUIXControlType(), 'type' => $comment_action->getPHUIXControlType(),
'spec' => $comment_action->getPHUIXControlSpecification(), 'spec' => $comment_action->getPHUIXControlSpecification(),
'initialValue' => $comment_action->getInitialValue(), 'initialValue' => $comment_action->getInitialValue(),
'groupKey' => $comment_action->getGroupKey(),
); );
$type_map[$key] = $comment_action; $type_map[$key] = $comment_action;
} }
$options = array(); $options = $this->newCommentActionOptions($action_map);
$options['+'] = pht('Add Action...');
foreach ($action_map as $key => $item) {
$options[$key] = $item['label'];
}
$action_id = celerity_generate_unique_node_id(); $action_id = celerity_generate_unique_node_id();
$input_id = celerity_generate_unique_node_id(); $input_id = celerity_generate_unique_node_id();
@ -424,4 +432,46 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView {
return $this->commentID; return $this->commentID;
} }
private function newCommentActionOptions(array $action_map) {
$options = array();
$options['+'] = pht('Add Action...');
// Merge options into groups.
$groups = array();
foreach ($action_map as $key => $item) {
$group_key = $item['groupKey'];
if (!isset($groups[$group_key])) {
$groups[$group_key] = array();
}
$groups[$group_key][$key] = $item;
}
$group_specs = $this->getCommentActionGroups();
$group_labels = mpull($group_specs, 'getLabel', 'getKey');
// Reorder groups to put them in the same order as the recognized
// group definitions.
$groups = array_select_keys($groups, array_keys($group_labels)) + $groups;
// Move options with no group to the end.
$default_group = idx($groups, '');
if ($default_group) {
unset($groups['']);
$groups[''] = $default_group;
}
foreach ($groups as $group_key => $group_items) {
if (strlen($group_key)) {
$group_label = idx($group_labels, $group_key, $group_key);
$options[$group_label] = ipull($group_items, 'label');
} else {
foreach ($group_items as $key => $item) {
$options[$key] = $item['label'];
}
}
}
return $options;
}
} }