mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-29 10:12:41 +01:00
Support edge transactions in ApplicationTransactions
Summary: Fixes T2655. Adds generic support for edge edits (e.g., membership or attached objects). Test Plan: See next diff. Reviewers: chad, btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T2655 Differential Revision: https://secure.phabricator.com/D5434
This commit is contained in:
parent
1ee7bbe305
commit
696498934c
6 changed files with 234 additions and 4 deletions
24
resources/sql/patches/20130310.xactionmeta.sql
Normal file
24
resources/sql/patches/20130310.xactionmeta.sql
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
ALTER TABLE `{$NAMESPACE}_file`.`macro_transaction`
|
||||||
|
ADD metadata LONGTEXT NOT NULL COLLATE utf8_bin;
|
||||||
|
UPDATE `{$NAMESPACE}_file`.macro_transaction SET metadata = '{}'
|
||||||
|
WHERE metadata = '';
|
||||||
|
|
||||||
|
ALTER TABLE `{$NAMESPACE}_pholio`.`pholio_transaction`
|
||||||
|
ADD metadata LONGTEXT NOT NULL COLLATE utf8_bin;
|
||||||
|
UPDATE `{$NAMESPACE}_pholio`.pholio_transaction SET metadata = '{}'
|
||||||
|
WHERE metadata = '';
|
||||||
|
|
||||||
|
ALTER TABLE `{$NAMESPACE}_config`.`config_transaction`
|
||||||
|
ADD metadata LONGTEXT NOT NULL COLLATE utf8_bin;
|
||||||
|
UPDATE `{$NAMESPACE}_config`.config_transaction SET metadata = '{}'
|
||||||
|
WHERE metadata = '';
|
||||||
|
|
||||||
|
ALTER TABLE `{$NAMESPACE}_conpherence`.`conpherence_transaction`
|
||||||
|
ADD metadata LONGTEXT NOT NULL COLLATE utf8_bin;
|
||||||
|
UPDATE `{$NAMESPACE}_conpherence`.conpherence_transaction SET metadata = '{}'
|
||||||
|
WHERE metadata = '';
|
||||||
|
|
||||||
|
ALTER TABLE `{$NAMESPACE}_phlux`.`phlux_transaction`
|
||||||
|
ADD metadata LONGTEXT NOT NULL COLLATE utf8_bin;
|
||||||
|
UPDATE `{$NAMESPACE}_phlux`.phlux_transaction SET metadata = '{}'
|
||||||
|
WHERE metadata = '';
|
|
@ -192,7 +192,7 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
||||||
return $v;
|
return $v;
|
||||||
case ConpherenceTransactionType::TYPE_FILES:
|
case ConpherenceTransactionType::TYPE_FILES:
|
||||||
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
||||||
return $this->mergePHIDTransactions($u, $v);
|
return $this->mergePHIDOrEdgeTransactions($u, $v);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::mergeTransactions($u, $v);
|
return parent::mergeTransactions($u, $v);
|
||||||
|
|
|
@ -6,6 +6,7 @@ final class PhabricatorTransactions {
|
||||||
const TYPE_SUBSCRIBERS = 'core:subscribers';
|
const TYPE_SUBSCRIBERS = 'core:subscribers';
|
||||||
const TYPE_VIEW_POLICY = 'core:view-policy';
|
const TYPE_VIEW_POLICY = 'core:view-policy';
|
||||||
const TYPE_EDIT_POLICY = 'core:edit-policy';
|
const TYPE_EDIT_POLICY = 'core:edit-policy';
|
||||||
|
const TYPE_EDGE = 'core:edge';
|
||||||
|
|
||||||
const COLOR_RED = 'red';
|
const COLOR_RED = 'red';
|
||||||
const COLOR_ORANGE = 'orange';
|
const COLOR_ORANGE = 'orange';
|
||||||
|
|
|
@ -101,6 +101,26 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
return $object->getViewPolicy();
|
return $object->getViewPolicy();
|
||||||
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
||||||
return $object->getEditPolicy();
|
return $object->getEditPolicy();
|
||||||
|
case PhabricatorTransactions::TYPE_EDGE:
|
||||||
|
$edge_type = $xaction->getMetadataValue('edge:type');
|
||||||
|
if (!$edge_type) {
|
||||||
|
throw new Exception("Edge transaction has no 'edge:type'!");
|
||||||
|
}
|
||||||
|
|
||||||
|
$old_edges = array();
|
||||||
|
if ($object->getPHID()) {
|
||||||
|
$edge_src = $object->getPHID();
|
||||||
|
|
||||||
|
$old_edges = id(new PhabricatorEdgeQuery())
|
||||||
|
->setViewer($this->getActor())
|
||||||
|
->withSourcePHIDs(array($edge_src))
|
||||||
|
->withEdgeTypes(array($edge_type))
|
||||||
|
->needEdgeData(true)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$old_edges = $old_edges[$edge_src][$edge_type];
|
||||||
|
}
|
||||||
|
return $old_edges;
|
||||||
default:
|
default:
|
||||||
return $this->getCustomTransactionOldValue($object, $xaction);
|
return $this->getCustomTransactionOldValue($object, $xaction);
|
||||||
}
|
}
|
||||||
|
@ -115,6 +135,8 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
case PhabricatorTransactions::TYPE_VIEW_POLICY:
|
case PhabricatorTransactions::TYPE_VIEW_POLICY:
|
||||||
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
||||||
return $xaction->getNewValue();
|
return $xaction->getNewValue();
|
||||||
|
case PhabricatorTransactions::TYPE_EDGE:
|
||||||
|
return $this->getEdgeTransactionNewValue($xaction);
|
||||||
default:
|
default:
|
||||||
return $this->getCustomTransactionNewValue($object, $xaction);
|
return $this->getCustomTransactionNewValue($object, $xaction);
|
||||||
}
|
}
|
||||||
|
@ -180,7 +202,46 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
|
|
||||||
$subeditor->save();
|
$subeditor->save();
|
||||||
break;
|
break;
|
||||||
|
case PhabricatorTransactions::TYPE_EDGE:
|
||||||
|
$old = $xaction->getOldValue();
|
||||||
|
$new = $xaction->getNewValue();
|
||||||
|
$src = $object->getPHID();
|
||||||
|
$type = $xaction->getMetadataValue('edge:type');
|
||||||
|
|
||||||
|
foreach ($new as $dst_phid => $edge) {
|
||||||
|
$new[$dst_phid]['src'] = $src;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$editor = id(new PhabricatorEdgeEditor())
|
||||||
|
->setActor($this->getActor());
|
||||||
|
|
||||||
|
foreach ($old as $dst_phid => $edge) {
|
||||||
|
if (!empty($new[$dst_phid])) {
|
||||||
|
if ($old[$dst_phid]['data'] === $new[$dst_phid]['data']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$editor->removeEdge($src, $type, $dst_phid);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($new as $dst_phid => $edge) {
|
||||||
|
if (!empty($old[$dst_phid])) {
|
||||||
|
if ($old[$dst_phid]['data'] === $new[$dst_phid]['data']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'data' => $edge['data'],
|
||||||
|
);
|
||||||
|
|
||||||
|
$editor->addEdge($src, $type, $dst_phid, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$editor->save();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return $this->applyCustomExternalTransaction($object, $xaction);
|
return $this->applyCustomExternalTransaction($object, $xaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,7 +521,14 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
|
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
|
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
|
||||||
return $this->mergePHIDTransactions($u, $v);
|
return $this->mergePHIDOrEdgeTransactions($u, $v);
|
||||||
|
case PhabricatorTransactions::TYPE_EDGE:
|
||||||
|
$u_type = $u->getMetadataValue('edge:type');
|
||||||
|
$v_type = $v->getMetadataValue('edge:type');
|
||||||
|
if ($u_type == $v_type) {
|
||||||
|
return $this->mergePHIDOrEdgeTransactions($u, $v);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// By default, do not merge the transactions.
|
// By default, do not merge the transactions.
|
||||||
|
@ -516,7 +584,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
return array_values($result);
|
return array_values($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function mergePHIDTransactions(
|
protected function mergePHIDOrEdgeTransactions(
|
||||||
PhabricatorApplicationTransaction $u,
|
PhabricatorApplicationTransaction $u,
|
||||||
PhabricatorApplicationTransaction $v) {
|
PhabricatorApplicationTransaction $v) {
|
||||||
|
|
||||||
|
@ -529,7 +597,6 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
return $u;
|
return $u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected function getPHIDTransactionNewValue(
|
protected function getPHIDTransactionNewValue(
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
|
||||||
|
@ -578,6 +645,110 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
return array_values($result);
|
return array_values($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getEdgeTransactionNewValue(
|
||||||
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
|
||||||
|
$new = $xaction->getNewValue();
|
||||||
|
$new_add = idx($new, '+', array());
|
||||||
|
unset($new['+']);
|
||||||
|
$new_rem = idx($new, '-', array());
|
||||||
|
unset($new['-']);
|
||||||
|
$new_set = idx($new, '=', null);
|
||||||
|
unset($new['=']);
|
||||||
|
|
||||||
|
if ($new) {
|
||||||
|
throw new Exception(
|
||||||
|
"Invalid 'new' value for Edge transaction. Value should contain only ".
|
||||||
|
"keys '+' (add edges), '-' (remove edges) and '=' (set edges).");
|
||||||
|
}
|
||||||
|
|
||||||
|
$old = $xaction->getOldValue();
|
||||||
|
|
||||||
|
$lists = array($new_set, $new_add, $new_rem);
|
||||||
|
foreach ($lists as $list) {
|
||||||
|
$this->checkEdgeList($list);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array();
|
||||||
|
foreach ($old as $dst_phid => $edge) {
|
||||||
|
if ($new_set !== null && empty($new_set[$dst_phid])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$result[$dst_phid] = $this->normalizeEdgeTransactionValue(
|
||||||
|
$xaction,
|
||||||
|
$edge);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($new_set !== null) {
|
||||||
|
foreach ($new_set as $dst_phid => $edge) {
|
||||||
|
$result[$dst_phid] = $this->normalizeEdgeTransactionValue(
|
||||||
|
$xaction,
|
||||||
|
$edge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($new_add as $dst_phid => $edge) {
|
||||||
|
$result[$dst_phid] = $this->normalizeEdgeTransactionValue(
|
||||||
|
$xaction,
|
||||||
|
$edge);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($new_rem as $dst_phid => $edge) {
|
||||||
|
unset($result[$dst_phid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkEdgeList($list) {
|
||||||
|
if (!$list) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach ($list as $key => $item) {
|
||||||
|
if (phid_get_type($key) === PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN) {
|
||||||
|
throw new Exception(
|
||||||
|
"Edge transactions must have destination PHIDs as in edge ".
|
||||||
|
"lists (found key '{$key}').");
|
||||||
|
}
|
||||||
|
if (!is_array($item) && $item !== $key) {
|
||||||
|
throw new Exception(
|
||||||
|
"Edge transactions must have PHIDs or edge specs as values ".
|
||||||
|
"(found value '{$item}').");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function normalizeEdgeTransactionValue(
|
||||||
|
PhabricatorApplicationTransaction $xaction,
|
||||||
|
$edge) {
|
||||||
|
|
||||||
|
if (!is_array($edge)) {
|
||||||
|
$edge = array(
|
||||||
|
'dst' => $edge,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$edge_type = $xaction->getMetadataValue('edge:type');
|
||||||
|
|
||||||
|
if (empty($edge['type'])) {
|
||||||
|
$edge['type'] = $edge_type;
|
||||||
|
} else {
|
||||||
|
if ($edge['type'] != $edge_type) {
|
||||||
|
$this_type = $edge['type'];
|
||||||
|
throw new Exception(
|
||||||
|
"Edge transaction includes edge of type '{$this_type}', but ".
|
||||||
|
"transaction is of type '{$edge_type}'. Each edge transaction must ".
|
||||||
|
"alter edges of only one type.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($edge['data'])) {
|
||||||
|
$edge['data'] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $edge;
|
||||||
|
}
|
||||||
|
|
||||||
protected function sortTransactions(array $xactions) {
|
protected function sortTransactions(array $xactions) {
|
||||||
$head = array();
|
$head = array();
|
||||||
$tail = array();
|
$tail = array();
|
||||||
|
|
|
@ -18,6 +18,7 @@ abstract class PhabricatorApplicationTransaction
|
||||||
protected $transactionType;
|
protected $transactionType;
|
||||||
protected $oldValue;
|
protected $oldValue;
|
||||||
protected $newValue;
|
protected $newValue;
|
||||||
|
protected $metadata = array();
|
||||||
|
|
||||||
protected $contentSource;
|
protected $contentSource;
|
||||||
|
|
||||||
|
@ -36,6 +37,15 @@ abstract class PhabricatorApplicationTransaction
|
||||||
return new PhabricatorApplicationTransactionView();
|
return new PhabricatorApplicationTransactionView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetadataValue($key, $default = null) {
|
||||||
|
return idx($this->metadata, $key, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMetadataValue($key, $value) {
|
||||||
|
$this->metadata[$key] = $value;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function generatePHID() {
|
public function generatePHID() {
|
||||||
$type = PhabricatorPHIDConstants::PHID_TYPE_XACT;
|
$type = PhabricatorPHIDConstants::PHID_TYPE_XACT;
|
||||||
$subtype = $this->getApplicationTransactionType();
|
$subtype = $this->getApplicationTransactionType();
|
||||||
|
@ -49,6 +59,7 @@ abstract class PhabricatorApplicationTransaction
|
||||||
self::CONFIG_SERIALIZATION => array(
|
self::CONFIG_SERIALIZATION => array(
|
||||||
'oldValue' => self::SERIALIZATION_JSON,
|
'oldValue' => self::SERIALIZATION_JSON,
|
||||||
'newValue' => self::SERIALIZATION_JSON,
|
'newValue' => self::SERIALIZATION_JSON,
|
||||||
|
'metadata' => self::SERIALIZATION_JSON,
|
||||||
),
|
),
|
||||||
) + parent::getConfiguration();
|
) + parent::getConfiguration();
|
||||||
}
|
}
|
||||||
|
@ -108,6 +119,10 @@ abstract class PhabricatorApplicationTransaction
|
||||||
$phids[] = $old;
|
$phids[] = $old;
|
||||||
$phids[] = $new;
|
$phids[] = $new;
|
||||||
break;
|
break;
|
||||||
|
case PhabricatorTransactions::TYPE_EDGE:
|
||||||
|
$phids[] = ipull($old, 'dst');
|
||||||
|
$phids[] = ipull($new, 'dst');
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return array_mergev($phids);
|
return array_mergev($phids);
|
||||||
|
@ -160,6 +175,8 @@ abstract class PhabricatorApplicationTransaction
|
||||||
case PhabricatorTransactions::TYPE_VIEW_POLICY:
|
case PhabricatorTransactions::TYPE_VIEW_POLICY:
|
||||||
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
||||||
return 'lock';
|
return 'lock';
|
||||||
|
case PhabricatorTransactions::TYPE_EDGE:
|
||||||
|
return 'link';
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -201,6 +218,8 @@ abstract class PhabricatorApplicationTransaction
|
||||||
return pht(
|
return pht(
|
||||||
'All users are already subscribed to this %s.',
|
'All users are already subscribed to this %s.',
|
||||||
$this->getApplicationObjectTypeName());
|
$this->getApplicationObjectTypeName());
|
||||||
|
case PhabricatorTransactions::TYPE_EDGE:
|
||||||
|
return pht('Edges already exist; transaction has no effect.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return pht('Transaction has no effect.');
|
return pht('Transaction has no effect.');
|
||||||
|
@ -259,6 +278,12 @@ abstract class PhabricatorApplicationTransaction
|
||||||
$this->renderHandleList($rem));
|
$this->renderHandleList($rem));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case PhabricatorTransactions::TYPE_EDGE:
|
||||||
|
$type = $this->getMetadata('edge:type');
|
||||||
|
return pht(
|
||||||
|
'%s edited edges of type %s.',
|
||||||
|
$this->renderHandleLink($author_phid),
|
||||||
|
$type);
|
||||||
default:
|
default:
|
||||||
return pht(
|
return pht(
|
||||||
'%s edited this %s.',
|
'%s edited this %s.',
|
||||||
|
@ -295,6 +320,11 @@ abstract class PhabricatorApplicationTransaction
|
||||||
'%s updated subscribers of %s.',
|
'%s updated subscribers of %s.',
|
||||||
$this->renderHandleLink($author_phid),
|
$this->renderHandleLink($author_phid),
|
||||||
$this->renderHandleLink($object_phid));
|
$this->renderHandleLink($object_phid));
|
||||||
|
case PhabricatorTransactions::TYPE_EDGE:
|
||||||
|
return pht(
|
||||||
|
'%s updated edges of %s.',
|
||||||
|
$this->renderHandleLink($author_phid),
|
||||||
|
$this->renderHandleLink($object_phid));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->getTitle();
|
return $this->getTitle();
|
||||||
|
|
|
@ -1194,6 +1194,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
|
||||||
'type' => 'sql',
|
'type' => 'sql',
|
||||||
'name' => $this->getPatchPath('20130317.phrictionedge.sql'),
|
'name' => $this->getPatchPath('20130317.phrictionedge.sql'),
|
||||||
),
|
),
|
||||||
|
'20130310.xactionmeta.sql' => array(
|
||||||
|
'type' => 'sql',
|
||||||
|
'name' => $this->getPatchPath('20130310.xactionmeta.sql'),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue