mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-30 02:32:42 +01:00
Add subscriber/mention support to Pholio
Summary: - Show subscribers. - When a user is mentioned in the description or a comment, subscribe them explicitly. Test Plan: {F22370} Reviewers: btrahan, chad Reviewed By: btrahan CC: aran Maniphest Tasks: T2097 Differential Revision: https://secure.phabricator.com/D3838
This commit is contained in:
parent
1f873572a4
commit
029cfcfc19
6 changed files with 168 additions and 10 deletions
|
@ -22,5 +22,6 @@ final class PholioTransactionType extends PholioConstants {
|
||||||
const TYPE_NAME = 'name';
|
const TYPE_NAME = 'name';
|
||||||
const TYPE_DESCRIPTION = 'description';
|
const TYPE_DESCRIPTION = 'description';
|
||||||
const TYPE_VIEW_POLICY = 'viewPolicy';
|
const TYPE_VIEW_POLICY = 'viewPolicy';
|
||||||
|
const TYPE_SUBSCRIBERS = 'subscribers';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,11 +44,19 @@ final class PholioMockViewController extends PholioController {
|
||||||
->withMockIDs(array($mock->getID()))
|
->withMockIDs(array($mock->getID()))
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
|
$subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID(
|
||||||
|
$mock->getPHID());
|
||||||
|
|
||||||
$phids = array();
|
$phids = array();
|
||||||
$phids[] = $mock->getAuthorPHID();
|
$phids[] = $mock->getAuthorPHID();
|
||||||
foreach ($xactions as $xaction) {
|
foreach ($xactions as $xaction) {
|
||||||
$phids[] = $xaction->getAuthorPHID();
|
$phids[] = $xaction->getAuthorPHID();
|
||||||
|
foreach ($xaction->getRequiredHandlePHIDs() as $hphid) {
|
||||||
|
$phids[] = $hphid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($subscribers as $subscriber) {
|
||||||
|
$phids[] = $subscriber;
|
||||||
}
|
}
|
||||||
$this->loadHandles($phids);
|
$this->loadHandles($phids);
|
||||||
|
|
||||||
|
@ -67,15 +75,11 @@ final class PholioMockViewController extends PholioController {
|
||||||
->setHeader($title);
|
->setHeader($title);
|
||||||
|
|
||||||
$actions = $this->buildActionView($mock);
|
$actions = $this->buildActionView($mock);
|
||||||
$properties = $this->buildPropertyView($mock, $engine);
|
$properties = $this->buildPropertyView($mock, $engine, $subscribers);
|
||||||
|
|
||||||
$carousel =
|
$carousel =
|
||||||
'<h1 style="margin: 2em; padding: 1em; border: 1px dashed grey;">'.
|
'<h1 style="margin: 2em; padding: 1em; border: 1px dashed grey;">'.
|
||||||
'Carousel Goes Here</h1>';
|
'Carousel Goes Here</h1>';
|
||||||
$comments =
|
|
||||||
'<h1 style="margin: 2em; padding: 1em; border: 1px dashed grey;">'.
|
|
||||||
'Comments/Transactions Go Here</h1>';
|
|
||||||
|
|
||||||
|
|
||||||
$xaction_view = $this->buildTransactionView($xactions, $engine);
|
$xaction_view = $this->buildTransactionView($xactions, $engine);
|
||||||
|
|
||||||
|
@ -123,7 +127,8 @@ final class PholioMockViewController extends PholioController {
|
||||||
|
|
||||||
private function buildPropertyView(
|
private function buildPropertyView(
|
||||||
PholioMock $mock,
|
PholioMock $mock,
|
||||||
PhabricatorMarkupEngine $engine) {
|
PhabricatorMarkupEngine $engine,
|
||||||
|
array $subscribers) {
|
||||||
|
|
||||||
$user = $this->getRequest()->getUser();
|
$user = $this->getRequest()->getUser();
|
||||||
|
|
||||||
|
@ -145,6 +150,20 @@ final class PholioMockViewController extends PholioController {
|
||||||
pht('Visible To'),
|
pht('Visible To'),
|
||||||
$descriptions[PhabricatorPolicyCapability::CAN_VIEW]);
|
$descriptions[PhabricatorPolicyCapability::CAN_VIEW]);
|
||||||
|
|
||||||
|
if ($subscribers) {
|
||||||
|
$sub_view = array();
|
||||||
|
foreach ($subscribers as $subscriber) {
|
||||||
|
$sub_view[] = $this->getHandle($subscriber)->renderLink();
|
||||||
|
}
|
||||||
|
$sub_view = implode(', ', $sub_view);
|
||||||
|
} else {
|
||||||
|
$sub_view = '<em>'.pht('None').'</em>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$properties->addProperty(
|
||||||
|
pht('Subscribers'),
|
||||||
|
$sub_view);
|
||||||
|
|
||||||
$properties->addTextContent(
|
$properties->addTextContent(
|
||||||
$engine->getOutput($mock, PholioMock::MARKUP_FIELD_DESCRIPTION));
|
$engine->getOutput($mock, PholioMock::MARKUP_FIELD_DESCRIPTION));
|
||||||
|
|
||||||
|
@ -201,8 +220,9 @@ final class PholioMockViewController extends PholioController {
|
||||||
|
|
||||||
$xaction_visible = true;
|
$xaction_visible = true;
|
||||||
$title = null;
|
$title = null;
|
||||||
|
$type = $xaction->getTransactionType();
|
||||||
|
|
||||||
switch ($xaction->getTransactionType()) {
|
switch ($type) {
|
||||||
case PholioTransactionType::TYPE_NONE:
|
case PholioTransactionType::TYPE_NONE:
|
||||||
$title = pht(
|
$title = pht(
|
||||||
'%s added a comment.',
|
'%s added a comment.',
|
||||||
|
@ -243,6 +263,44 @@ final class PholioMockViewController extends PholioController {
|
||||||
phutil_escape_html($old),
|
phutil_escape_html($old),
|
||||||
phutil_escape_html($new));
|
phutil_escape_html($new));
|
||||||
break;
|
break;
|
||||||
|
case PholioTransactionType::TYPE_SUBSCRIBERS:
|
||||||
|
$rem = array_diff($old, $new);
|
||||||
|
$add = array_diff($new, $old);
|
||||||
|
|
||||||
|
$add_l = array();
|
||||||
|
foreach ($add as $phid) {
|
||||||
|
$add_l[] = $this->getHandle($phid)->renderLink();
|
||||||
|
}
|
||||||
|
$add_l = implode(', ', $add_l);
|
||||||
|
|
||||||
|
$rem_l = array();
|
||||||
|
foreach ($rem as $phid) {
|
||||||
|
$rem_l[] = $this->getHandle($phid)->renderLink();
|
||||||
|
}
|
||||||
|
$rem_l = implode(', ', $rem_l);
|
||||||
|
|
||||||
|
if ($add && $rem) {
|
||||||
|
$title = pht(
|
||||||
|
'%s edited subscriber(s), added %d: %s; removed %d: %s.',
|
||||||
|
$author->renderLink(),
|
||||||
|
$add_l,
|
||||||
|
count($add),
|
||||||
|
$rem_l,
|
||||||
|
count($rem));
|
||||||
|
} else if ($add) {
|
||||||
|
$title = pht(
|
||||||
|
'%s added %d subscriber(s): %s.',
|
||||||
|
$author->renderLink(),
|
||||||
|
count($add),
|
||||||
|
$add_l);
|
||||||
|
} else if ($rem) {
|
||||||
|
$title = pht(
|
||||||
|
'%s removed %d subscribers: %s.',
|
||||||
|
$author->renderLink(),
|
||||||
|
count($rem),
|
||||||
|
$rem_l);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception("Unknown transaction type '{$type}'!");
|
throw new Exception("Unknown transaction type '{$type}'!");
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,36 @@ final class PholioMockEditor extends PhabricatorEditor {
|
||||||
"Call setContentSource() before applyTransactions()!");
|
"Call setContentSource() before applyTransactions()!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$comments = array();
|
||||||
|
foreach ($xactions as $xaction) {
|
||||||
|
if (strlen($xaction->getComment())) {
|
||||||
|
$comments[] = $xaction->getComment();
|
||||||
|
}
|
||||||
|
$type = $xaction->getTransactionType();
|
||||||
|
if ($type == PholioTransactionType::TYPE_DESCRIPTION) {
|
||||||
|
$comments[] = $xaction->getNewValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$mentioned_phids = PhabricatorMarkupEngine::extractPHIDsFromMentions(
|
||||||
|
$comments);
|
||||||
|
|
||||||
|
if ($mentioned_phids) {
|
||||||
|
if ($mock->getID()) {
|
||||||
|
$old_subs = PhabricatorSubscribersQuery::loadSubscribersForPHID(
|
||||||
|
$mock->getPHID());
|
||||||
|
} else {
|
||||||
|
$old_subs = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$new_subs = array_merge($old_subs, $mentioned_phids);
|
||||||
|
$xaction = id(new PholioTransaction())
|
||||||
|
->setTransactionType(PholioTransactionType::TYPE_SUBSCRIBERS)
|
||||||
|
->setOldValue($old_subs)
|
||||||
|
->setNewValue($new_subs);
|
||||||
|
array_unshift($xactions, $xaction);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($xactions as $xaction) {
|
foreach ($xactions as $xaction) {
|
||||||
$xaction->setContentSource($this->contentSource);
|
$xaction->setContentSource($this->contentSource);
|
||||||
$xaction->setAuthorPHID($actor->getPHID());
|
$xaction->setAuthorPHID($actor->getPHID());
|
||||||
|
@ -64,6 +94,21 @@ final class PholioMockEditor extends PhabricatorEditor {
|
||||||
$xaction->setMockID($mock->getID());
|
$xaction->setMockID($mock->getID());
|
||||||
$xaction->save();
|
$xaction->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply ID/PHID-dependent transactions.
|
||||||
|
foreach ($xactions as $xaction) {
|
||||||
|
$type = $xaction->getTransactionType();
|
||||||
|
switch ($type) {
|
||||||
|
case PholioTransactionType::TYPE_SUBSCRIBERS:
|
||||||
|
$subeditor = id(new PhabricatorSubscriptionsEditor())
|
||||||
|
->setObject($mock)
|
||||||
|
->setActor($this->requireActor())
|
||||||
|
->subscribeExplicit($xaction->getNewValue())
|
||||||
|
->save();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$mock->saveTransaction();
|
$mock->saveTransaction();
|
||||||
|
|
||||||
PholioIndexer::indexMock($mock);
|
PholioIndexer::indexMock($mock);
|
||||||
|
@ -91,13 +136,17 @@ final class PholioMockEditor extends PhabricatorEditor {
|
||||||
case PholioTransactionType::TYPE_VIEW_POLICY:
|
case PholioTransactionType::TYPE_VIEW_POLICY:
|
||||||
$old = $mock->getViewPolicy();
|
$old = $mock->getViewPolicy();
|
||||||
break;
|
break;
|
||||||
|
case PholioTransactionType::TYPE_SUBSCRIBERS:
|
||||||
|
$old = PhabricatorSubscribersQuery::loadSubscribersForPHID(
|
||||||
|
$mock->getPHID());
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception("Unknown transaction type '{$type}'!");
|
throw new Exception("Unknown transaction type '{$type}'!");
|
||||||
}
|
}
|
||||||
|
|
||||||
$xaction->setOldValue($old);
|
$xaction->setOldValue($old);
|
||||||
|
|
||||||
if (!$this->transactionHasEffect($xaction)) {
|
if (!$this->transactionHasEffect($mock, $xaction)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +162,9 @@ final class PholioMockEditor extends PhabricatorEditor {
|
||||||
case PholioTransactionType::TYPE_VIEW_POLICY:
|
case PholioTransactionType::TYPE_VIEW_POLICY:
|
||||||
$mock->setViewPolicy($xaction->getNewValue());
|
$mock->setViewPolicy($xaction->getNewValue());
|
||||||
break;
|
break;
|
||||||
|
case PholioTransactionType::TYPE_SUBSCRIBERS:
|
||||||
|
// This applies later.
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception("Unknown transaction type '{$type}'!");
|
throw new Exception("Unknown transaction type '{$type}'!");
|
||||||
}
|
}
|
||||||
|
@ -120,16 +172,47 @@ final class PholioMockEditor extends PhabricatorEditor {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function transactionHasEffect(PholioTransaction $xaction) {
|
private function transactionHasEffect(
|
||||||
|
PholioMock $mock,
|
||||||
|
PholioTransaction $xaction) {
|
||||||
|
|
||||||
$effect = false;
|
$effect = false;
|
||||||
|
|
||||||
|
$old = $xaction->getOldValue();
|
||||||
|
$new = $xaction->getNewValue();
|
||||||
|
|
||||||
$type = $xaction->getTransactionType();
|
$type = $xaction->getTransactionType();
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case PholioTransactionType::TYPE_NONE:
|
case PholioTransactionType::TYPE_NONE:
|
||||||
case PholioTransactionType::TYPE_NAME:
|
case PholioTransactionType::TYPE_NAME:
|
||||||
case PholioTransactionType::TYPE_DESCRIPTION:
|
case PholioTransactionType::TYPE_DESCRIPTION:
|
||||||
case PholioTransactionType::TYPE_VIEW_POLICY:
|
case PholioTransactionType::TYPE_VIEW_POLICY:
|
||||||
$effect = ($xaction->getOldValue() !== $xaction->getNewValue());
|
$effect = ($old !== $new);
|
||||||
|
break;
|
||||||
|
case PholioTransactionType::TYPE_SUBSCRIBERS:
|
||||||
|
$old = nonempty($old, array());
|
||||||
|
$old_map = array_fill_keys($old, true);
|
||||||
|
$filtered = $old;
|
||||||
|
|
||||||
|
foreach ($new as $phid) {
|
||||||
|
if ($mock->getAuthorPHID() == $phid) {
|
||||||
|
// The author may not be explicitly subscribed.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (isset($old_map[$phid])) {
|
||||||
|
// This PHID was already subscribed.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$filtered[] = $phid;
|
||||||
|
}
|
||||||
|
|
||||||
|
$old = array_keys($old_map);
|
||||||
|
$new = array_values($filtered);
|
||||||
|
|
||||||
|
$xaction->setOldValue($old);
|
||||||
|
$xaction->setNewValue($new);
|
||||||
|
|
||||||
|
$effect = ($old !== $new);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception("Unknown transaction type '{$type}'!");
|
throw new Exception("Unknown transaction type '{$type}'!");
|
||||||
|
|
|
@ -52,6 +52,17 @@ final class PholioTransaction extends PholioDAO
|
||||||
return PhabricatorContentSource::newFromSerialized($this->contentSource);
|
return PhabricatorContentSource::newFromSerialized($this->contentSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRequiredHandlePHIDs() {
|
||||||
|
switch ($this->getTransactionType()) {
|
||||||
|
case PholioTransactionType::TYPE_SUBSCRIBERS:
|
||||||
|
return array_merge(
|
||||||
|
$this->getOldValue(),
|
||||||
|
$this->getNewValue());
|
||||||
|
default:
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorSubscribableInterface Implementation )-------------------- */
|
/* -( PhabricatorSubscribableInterface Implementation )-------------------- */
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ final class PhabricatorSubscriptionsEditController
|
||||||
}
|
}
|
||||||
|
|
||||||
$objects = id(new PhabricatorObjectHandleData(array($phid)))
|
$objects = id(new PhabricatorObjectHandleData(array($phid)))
|
||||||
|
->setViewer($user)
|
||||||
->loadObjects();
|
->loadObjects();
|
||||||
$object = idx($objects, $phid);
|
$object = idx($objects, $phid);
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,10 @@ final class PhabricatorSubscribersQuery extends PhabricatorQuery {
|
||||||
private $subscriberPHIDs;
|
private $subscriberPHIDs;
|
||||||
|
|
||||||
public static function loadSubscribersForPHID($phid) {
|
public static function loadSubscribersForPHID($phid) {
|
||||||
|
if (!$phid) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
$subscribers = id(new PhabricatorSubscribersQuery())
|
$subscribers = id(new PhabricatorSubscribersQuery())
|
||||||
->withObjectPHIDs(array($phid))
|
->withObjectPHIDs(array($phid))
|
||||||
->execute();
|
->execute();
|
||||||
|
|
Loading…
Reference in a new issue