1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 16:52:41 +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:
epriestley 2012-11-21 17:38:57 -08:00
parent 1f873572a4
commit 029cfcfc19
6 changed files with 168 additions and 10 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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