2013-01-25 02:23:05 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
|
|
|
|
2013-05-31 01:37:51 +02:00
|
|
|
const ERROR_EMPTY_PARTICIPANTS = 'error-empty-participants';
|
|
|
|
const ERROR_EMPTY_MESSAGE = 'error-empty-message';
|
|
|
|
|
2014-08-12 21:28:41 +02:00
|
|
|
public function getEditorApplicationClass() {
|
|
|
|
return 'PhabricatorConpherenceApplication';
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getEditorObjectsDescription() {
|
|
|
|
return pht('Conpherence Threads');
|
|
|
|
}
|
|
|
|
|
2015-03-26 20:24:29 +01:00
|
|
|
public static function createThread(
|
2013-05-31 01:37:51 +02:00
|
|
|
PhabricatorUser $creator,
|
|
|
|
array $participant_phids,
|
|
|
|
$title,
|
|
|
|
$message,
|
|
|
|
PhabricatorContentSource $source) {
|
|
|
|
|
2015-03-18 01:04:44 +01:00
|
|
|
$conpherence = ConpherenceThread::initializeNewThread($creator);
|
2013-05-31 01:37:51 +02:00
|
|
|
$files = array();
|
|
|
|
$errors = array();
|
|
|
|
if (empty($participant_phids)) {
|
|
|
|
$errors[] = self::ERROR_EMPTY_PARTICIPANTS;
|
|
|
|
} else {
|
|
|
|
$participant_phids[] = $creator->getPHID();
|
|
|
|
$participant_phids = array_unique($participant_phids);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (empty($message)) {
|
|
|
|
$errors[] = self::ERROR_EMPTY_MESSAGE;
|
|
|
|
}
|
|
|
|
|
2014-04-24 01:30:38 +02:00
|
|
|
$file_phids = PhabricatorMarkupEngine::extractFilePHIDsFromEmbeddedFiles(
|
|
|
|
$creator,
|
|
|
|
array($message));
|
2013-05-31 01:37:51 +02:00
|
|
|
if ($file_phids) {
|
|
|
|
$files = id(new PhabricatorFileQuery())
|
|
|
|
->setViewer($creator)
|
|
|
|
->withPHIDs($file_phids)
|
|
|
|
->execute();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$errors) {
|
|
|
|
$xactions = array();
|
|
|
|
$xactions[] = id(new ConpherenceTransaction())
|
|
|
|
->setTransactionType(ConpherenceTransactionType::TYPE_PARTICIPANTS)
|
|
|
|
->setNewValue(array('+' => $participant_phids));
|
|
|
|
if ($files) {
|
|
|
|
$xactions[] = id(new ConpherenceTransaction())
|
|
|
|
->setTransactionType(ConpherenceTransactionType::TYPE_FILES)
|
|
|
|
->setNewValue(array('+' => mpull($files, 'getPHID')));
|
|
|
|
}
|
|
|
|
if ($title) {
|
|
|
|
$xactions[] = id(new ConpherenceTransaction())
|
|
|
|
->setTransactionType(ConpherenceTransactionType::TYPE_TITLE)
|
|
|
|
->setNewValue($title);
|
|
|
|
}
|
2015-03-24 21:04:33 +01:00
|
|
|
|
2013-05-31 01:37:51 +02:00
|
|
|
$xactions[] = id(new ConpherenceTransaction())
|
|
|
|
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
|
|
|
|
->attachComment(
|
|
|
|
id(new ConpherenceTransactionComment())
|
|
|
|
->setContent($message)
|
|
|
|
->setConpherencePHID($conpherence->getPHID()));
|
|
|
|
|
|
|
|
id(new ConpherenceEditor())
|
2015-03-26 20:24:29 +01:00
|
|
|
->setActor($creator)
|
2013-05-31 01:37:51 +02:00
|
|
|
->setContentSource($source)
|
|
|
|
->setContinueOnNoEffect(true)
|
|
|
|
->applyTransactions($conpherence, $xactions);
|
|
|
|
}
|
|
|
|
|
|
|
|
return array($errors, $conpherence);
|
|
|
|
}
|
|
|
|
|
2013-01-27 04:56:39 +01:00
|
|
|
public function generateTransactionsFromText(
|
2014-04-24 01:30:38 +02:00
|
|
|
PhabricatorUser $viewer,
|
2013-01-27 04:56:39 +01:00
|
|
|
ConpherenceThread $conpherence,
|
|
|
|
$text) {
|
|
|
|
|
|
|
|
$files = array();
|
2014-04-24 01:30:38 +02:00
|
|
|
$file_phids = PhabricatorMarkupEngine::extractFilePHIDsFromEmbeddedFiles(
|
|
|
|
$viewer,
|
|
|
|
array($text));
|
2013-01-27 04:56:39 +01:00
|
|
|
// Since these are extracted from text, we might be re-including the
|
|
|
|
// same file -- e.g. a mock under discussion. Filter files we
|
|
|
|
// already have.
|
|
|
|
$existing_file_phids = $conpherence->getFilePHIDs();
|
|
|
|
$file_phids = array_diff($file_phids, $existing_file_phids);
|
|
|
|
if ($file_phids) {
|
|
|
|
$files = id(new PhabricatorFileQuery())
|
|
|
|
->setViewer($this->getActor())
|
|
|
|
->withPHIDs($file_phids)
|
|
|
|
->execute();
|
|
|
|
}
|
|
|
|
$xactions = array();
|
|
|
|
if ($files) {
|
|
|
|
$xactions[] = id(new ConpherenceTransaction())
|
|
|
|
->setTransactionType(ConpherenceTransactionType::TYPE_FILES)
|
|
|
|
->setNewValue(array('+' => mpull($files, 'getPHID')));
|
|
|
|
}
|
|
|
|
$xactions[] = id(new ConpherenceTransaction())
|
|
|
|
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
|
|
|
|
->attachComment(
|
|
|
|
id(new ConpherenceTransactionComment())
|
|
|
|
->setContent($text)
|
2013-02-19 22:33:10 +01:00
|
|
|
->setConpherencePHID($conpherence->getPHID()));
|
2013-01-27 04:56:39 +01:00
|
|
|
return $xactions;
|
|
|
|
}
|
|
|
|
|
2013-01-25 02:23:05 +01:00
|
|
|
public function getTransactionTypes() {
|
|
|
|
$types = parent::getTransactionTypes();
|
|
|
|
|
|
|
|
$types[] = PhabricatorTransactions::TYPE_COMMENT;
|
|
|
|
|
|
|
|
$types[] = ConpherenceTransactionType::TYPE_TITLE;
|
|
|
|
$types[] = ConpherenceTransactionType::TYPE_PARTICIPANTS;
|
|
|
|
$types[] = ConpherenceTransactionType::TYPE_FILES;
|
2015-05-04 22:52:22 +02:00
|
|
|
$types[] = ConpherenceTransactionType::TYPE_PICTURE;
|
|
|
|
$types[] = ConpherenceTransactionType::TYPE_PICTURE_CROP;
|
2015-03-24 21:04:33 +01:00
|
|
|
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
|
|
|
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
|
|
|
$types[] = PhabricatorTransactions::TYPE_JOIN_POLICY;
|
2013-01-25 02:23:05 +01:00
|
|
|
|
|
|
|
return $types;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getCustomTransactionOldValue(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
PhabricatorApplicationTransaction $xaction) {
|
|
|
|
|
|
|
|
switch ($xaction->getTransactionType()) {
|
|
|
|
case ConpherenceTransactionType::TYPE_TITLE:
|
|
|
|
return $object->getTitle();
|
2015-05-04 22:52:22 +02:00
|
|
|
case ConpherenceTransactionType::TYPE_PICTURE:
|
|
|
|
return $object->getImagePHID(ConpherenceImageData::SIZE_ORIG);
|
|
|
|
case ConpherenceTransactionType::TYPE_PICTURE_CROP:
|
|
|
|
return $object->getImagePHID(ConpherenceImageData::SIZE_CROP);
|
2013-01-25 02:23:05 +01:00
|
|
|
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
2015-03-27 00:37:32 +01:00
|
|
|
if ($this->getIsNewObject()) {
|
|
|
|
return array();
|
|
|
|
}
|
2013-01-25 02:23:05 +01:00
|
|
|
return $object->getParticipantPHIDs();
|
|
|
|
case ConpherenceTransactionType::TYPE_FILES:
|
|
|
|
return $object->getFilePHIDs();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getCustomTransactionNewValue(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
PhabricatorApplicationTransaction $xaction) {
|
|
|
|
|
|
|
|
switch ($xaction->getTransactionType()) {
|
|
|
|
case ConpherenceTransactionType::TYPE_TITLE:
|
2015-05-04 22:52:22 +02:00
|
|
|
case ConpherenceTransactionType::TYPE_PICTURE_CROP:
|
2013-01-25 02:23:05 +01:00
|
|
|
return $xaction->getNewValue();
|
2015-05-04 22:52:22 +02:00
|
|
|
case ConpherenceTransactionType::TYPE_PICTURE:
|
|
|
|
$file = $xaction->getNewValue();
|
|
|
|
return $file->getPHID();
|
2013-01-25 02:23:05 +01:00
|
|
|
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
|
|
|
case ConpherenceTransactionType::TYPE_FILES:
|
|
|
|
return $this->getPHIDTransactionNewValue($xaction);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-05 01:57:38 +02:00
|
|
|
/**
|
|
|
|
* We really only need a read lock if we have a comment. In that case, we
|
|
|
|
* must update the messagesCount field on the conpherence and
|
|
|
|
* seenMessagesCount(s) for the participant(s).
|
|
|
|
*/
|
|
|
|
protected function shouldReadLock(
|
2013-01-25 02:23:05 +01:00
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
PhabricatorApplicationTransaction $xaction) {
|
|
|
|
|
2013-04-05 01:57:38 +02:00
|
|
|
$lock = false;
|
2013-01-25 02:23:05 +01:00
|
|
|
switch ($xaction->getTransactionType()) {
|
2013-04-05 01:57:38 +02:00
|
|
|
case PhabricatorTransactions::TYPE_COMMENT:
|
|
|
|
$lock = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $lock;
|
|
|
|
}
|
|
|
|
|
2013-08-15 02:32:16 +02:00
|
|
|
/**
|
|
|
|
* We need to apply initial effects IFF the conpherence is new. We must
|
2015-03-26 20:24:29 +01:00
|
|
|
* save the conpherence first thing to make sure we have an id and a phid, as
|
|
|
|
* well as create the initial set of participants so that we pass policy
|
|
|
|
* checks.
|
2013-08-15 02:32:16 +02:00
|
|
|
*/
|
|
|
|
protected function shouldApplyInitialEffects(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
array $xactions) {
|
|
|
|
|
2015-03-26 20:24:29 +01:00
|
|
|
return $this->getIsNewObject();
|
2013-08-15 02:32:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function applyInitialEffects(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
array $xactions) {
|
|
|
|
|
|
|
|
$object->save();
|
|
|
|
|
2015-03-26 20:24:29 +01:00
|
|
|
foreach ($xactions as $xaction) {
|
|
|
|
switch ($xaction->getTransactionType()) {
|
|
|
|
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
|
|
|
// Since this is a new ConpherenceThread, we have to create the
|
|
|
|
// participation data asap to pass policy checks. For existing
|
|
|
|
// ConpherenceThreads, the existing participation is correct
|
|
|
|
// at this stage. Note that later in applyCustomExternalTransaction
|
|
|
|
// this participation data will be updated, particularly the
|
|
|
|
// behindTransactionPHID which is just a generated dummy for now.
|
2014-12-04 20:54:17 +01:00
|
|
|
$participants = array();
|
2015-03-26 20:24:29 +01:00
|
|
|
$phids = $this->getPHIDTransactionNewValue($xaction, array());
|
|
|
|
foreach ($phids as $phid) {
|
2014-12-04 20:54:17 +01:00
|
|
|
if ($phid == $this->getActor()->getPHID()) {
|
|
|
|
$status = ConpherenceParticipationStatus::UP_TO_DATE;
|
|
|
|
$message_count = 1;
|
|
|
|
} else {
|
|
|
|
$status = ConpherenceParticipationStatus::BEHIND;
|
|
|
|
$message_count = 0;
|
|
|
|
}
|
|
|
|
$participants[$phid] =
|
|
|
|
id(new ConpherenceParticipant())
|
|
|
|
->setConpherencePHID($object->getPHID())
|
|
|
|
->setParticipantPHID($phid)
|
|
|
|
->setParticipationStatus($status)
|
|
|
|
->setDateTouched(time())
|
|
|
|
->setBehindTransactionPHID($xaction->generatePHID())
|
|
|
|
->setSeenMessageCount($message_count)
|
|
|
|
->save();
|
|
|
|
$object->attachParticipants($participants);
|
2015-03-26 20:24:29 +01:00
|
|
|
$object->setRecentParticipantPHIDs(array_keys($participants));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function applyCustomInternalTransaction(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
PhabricatorApplicationTransaction $xaction) {
|
|
|
|
|
2015-04-09 23:02:35 +02:00
|
|
|
$make_author_recent_participant = true;
|
2015-03-26 20:24:29 +01:00
|
|
|
switch ($xaction->getTransactionType()) {
|
|
|
|
case ConpherenceTransactionType::TYPE_TITLE:
|
|
|
|
$object->setTitle($xaction->getNewValue());
|
|
|
|
break;
|
2015-05-04 22:52:22 +02:00
|
|
|
case ConpherenceTransactionType::TYPE_PICTURE:
|
|
|
|
$object->setImagePHID(
|
|
|
|
$xaction->getNewValue(),
|
|
|
|
ConpherenceImageData::SIZE_ORIG);
|
|
|
|
break;
|
|
|
|
case ConpherenceTransactionType::TYPE_PICTURE_CROP:
|
|
|
|
$object->setImagePHID(
|
|
|
|
$xaction->getNewValue(),
|
|
|
|
ConpherenceImageData::SIZE_CROP);
|
|
|
|
break;
|
2015-03-26 20:24:29 +01:00
|
|
|
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
|
|
|
if (!$this->getIsNewObject()) {
|
|
|
|
$old_map = array_fuse($xaction->getOldValue());
|
|
|
|
$new_map = array_fuse($xaction->getNewValue());
|
2015-04-09 23:02:35 +02:00
|
|
|
// if we added people, add them to the end of "recent" participants
|
2015-03-26 20:24:29 +01:00
|
|
|
$add = array_keys(array_diff_key($new_map, $old_map));
|
2015-04-09 23:02:35 +02:00
|
|
|
// if we remove people, then definintely remove them from "recent"
|
|
|
|
// participants
|
|
|
|
$del = array_keys(array_diff_key($old_map, $new_map));
|
|
|
|
if ($add || $del) {
|
2015-03-26 20:24:29 +01:00
|
|
|
$participants = $object->getRecentParticipantPHIDs();
|
2015-04-09 23:02:35 +02:00
|
|
|
if ($add) {
|
|
|
|
$participants = array_merge($participants, $add);
|
|
|
|
}
|
|
|
|
if ($del) {
|
|
|
|
$participants = array_diff($participants, $del);
|
|
|
|
$actor = $this->requireActor();
|
|
|
|
if (in_array($actor->getPHID(), $del)) {
|
|
|
|
$make_author_recent_participant = false;
|
|
|
|
}
|
|
|
|
}
|
2015-03-26 20:24:29 +01:00
|
|
|
$participants = array_slice(array_unique($participants), 0, 10);
|
|
|
|
$object->setRecentParticipantPHIDs($participants);
|
2014-12-04 20:54:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2013-01-25 02:23:05 +01:00
|
|
|
}
|
2015-04-09 23:02:35 +02:00
|
|
|
|
|
|
|
if ($make_author_recent_participant) {
|
|
|
|
$this->makeAuthorMostRecentParticipant($object, $xaction);
|
|
|
|
}
|
2013-04-05 01:57:38 +02:00
|
|
|
}
|
|
|
|
|
2015-05-19 21:33:55 +02:00
|
|
|
protected function applyBuiltinInternalTransaction(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
PhabricatorApplicationTransaction $xaction) {
|
|
|
|
|
|
|
|
switch ($xaction->getTransactionType()) {
|
|
|
|
case PhabricatorTransactions::TYPE_COMMENT:
|
|
|
|
$object->setMessageCount((int)$object->getMessageCount() + 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return parent::applyBuiltinInternalTransaction($object, $xaction);
|
|
|
|
}
|
|
|
|
|
2015-04-09 23:02:35 +02:00
|
|
|
private function makeAuthorMostRecentParticipant(
|
2013-04-05 01:57:38 +02:00
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
PhabricatorApplicationTransaction $xaction) {
|
|
|
|
|
|
|
|
$participants = $object->getRecentParticipantPHIDs();
|
|
|
|
array_unshift($participants, $xaction->getAuthorPHID());
|
|
|
|
$participants = array_slice(array_unique($participants), 0, 10);
|
|
|
|
|
|
|
|
$object->setRecentParticipantPHIDs($participants);
|
2013-01-25 02:23:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function applyCustomExternalTransaction(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
PhabricatorApplicationTransaction $xaction) {
|
|
|
|
|
|
|
|
switch ($xaction->getTransactionType()) {
|
|
|
|
case ConpherenceTransactionType::TYPE_FILES:
|
2014-07-18 00:41:42 +02:00
|
|
|
$editor = new PhabricatorEdgeEditor();
|
2015-01-03 00:33:25 +01:00
|
|
|
$edge_type = PhabricatorObjectHasFileEdgeType::EDGECONST;
|
2013-03-08 19:40:06 +01:00
|
|
|
$old = array_fill_keys($xaction->getOldValue(), true);
|
|
|
|
$new = array_fill_keys($xaction->getNewValue(), true);
|
|
|
|
$add_edges = array_keys(array_diff_key($new, $old));
|
|
|
|
$remove_edges = array_keys(array_diff_key($old, $new));
|
|
|
|
foreach ($add_edges as $file_phid) {
|
2013-01-25 02:23:05 +01:00
|
|
|
$editor->addEdge(
|
|
|
|
$object->getPHID(),
|
|
|
|
$edge_type,
|
2013-02-19 22:33:10 +01:00
|
|
|
$file_phid);
|
2013-01-25 02:23:05 +01:00
|
|
|
}
|
2013-03-08 19:40:06 +01:00
|
|
|
foreach ($remove_edges as $file_phid) {
|
|
|
|
$editor->removeEdge(
|
|
|
|
$object->getPHID(),
|
|
|
|
$edge_type,
|
|
|
|
$file_phid);
|
|
|
|
}
|
2013-01-25 02:23:05 +01:00
|
|
|
$editor->save();
|
|
|
|
break;
|
|
|
|
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
2015-03-26 20:24:29 +01:00
|
|
|
if ($this->getIsNewObject()) {
|
|
|
|
continue;
|
|
|
|
}
|
2013-04-02 18:32:40 +02:00
|
|
|
$participants = $object->getParticipants();
|
|
|
|
|
|
|
|
$old_map = array_fuse($xaction->getOldValue());
|
|
|
|
$new_map = array_fuse($xaction->getNewValue());
|
|
|
|
|
|
|
|
$remove = array_keys(array_diff_key($old_map, $new_map));
|
|
|
|
foreach ($remove as $phid) {
|
|
|
|
$remove_participant = $participants[$phid];
|
|
|
|
$remove_participant->delete();
|
|
|
|
unset($participants[$phid]);
|
|
|
|
}
|
|
|
|
|
|
|
|
$add = array_keys(array_diff_key($new_map, $old_map));
|
|
|
|
foreach ($add as $phid) {
|
2015-03-26 20:24:29 +01:00
|
|
|
if ($phid == $this->getActor()->getPHID()) {
|
|
|
|
$status = ConpherenceParticipationStatus::UP_TO_DATE;
|
|
|
|
$message_count = $object->getMessageCount();
|
2013-01-25 02:23:05 +01:00
|
|
|
} else {
|
2015-03-26 20:24:29 +01:00
|
|
|
$status = ConpherenceParticipationStatus::BEHIND;
|
|
|
|
$message_count = 0;
|
2013-01-25 02:23:05 +01:00
|
|
|
}
|
2015-03-26 20:24:29 +01:00
|
|
|
$participants[$phid] =
|
|
|
|
id(new ConpherenceParticipant())
|
|
|
|
->setConpherencePHID($object->getPHID())
|
|
|
|
->setParticipantPHID($phid)
|
|
|
|
->setParticipationStatus($status)
|
|
|
|
->setDateTouched(time())
|
|
|
|
->setBehindTransactionPHID($xaction->getPHID())
|
|
|
|
->setSeenMessageCount($message_count)
|
|
|
|
->save();
|
2013-01-25 02:23:05 +01:00
|
|
|
}
|
2013-03-26 21:30:35 +01:00
|
|
|
$object->attachParticipants($participants);
|
2013-01-25 02:23:05 +01:00
|
|
|
break;
|
2013-04-26 19:30:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function applyFinalEffects(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
array $xactions) {
|
|
|
|
|
2015-03-25 02:38:16 +01:00
|
|
|
$message_count = 0;
|
|
|
|
foreach ($xactions as $xaction) {
|
|
|
|
switch ($xaction->getTransactionType()) {
|
|
|
|
case PhabricatorTransactions::TYPE_COMMENT:
|
|
|
|
$message_count++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-26 19:30:41 +02:00
|
|
|
// update everyone's participation status on the last xaction -only-
|
|
|
|
$xaction = end($xactions);
|
|
|
|
$xaction_phid = $xaction->getPHID();
|
|
|
|
$behind = ConpherenceParticipationStatus::BEHIND;
|
|
|
|
$up_to_date = ConpherenceParticipationStatus::UP_TO_DATE;
|
|
|
|
$participants = $object->getParticipants();
|
|
|
|
$user = $this->getActor();
|
|
|
|
$time = time();
|
|
|
|
foreach ($participants as $phid => $participant) {
|
|
|
|
if ($phid != $user->getPHID()) {
|
|
|
|
if ($participant->getParticipationStatus() != $behind) {
|
|
|
|
$participant->setBehindTransactionPHID($xaction_phid);
|
2015-03-25 02:38:16 +01:00
|
|
|
$participant->setSeenMessageCount(
|
|
|
|
$object->getMessageCount() - $message_count);
|
2013-04-26 19:30:41 +02:00
|
|
|
}
|
|
|
|
$participant->setParticipationStatus($behind);
|
|
|
|
$participant->setDateTouched($time);
|
|
|
|
} else {
|
|
|
|
$participant->setSeenMessageCount($object->getMessageCount());
|
2015-03-26 20:24:29 +01:00
|
|
|
$participant->setBehindTransactionPHID($xaction_phid);
|
2013-04-26 19:30:41 +02:00
|
|
|
$participant->setParticipationStatus($up_to_date);
|
|
|
|
$participant->setDateTouched($time);
|
|
|
|
}
|
|
|
|
$participant->save();
|
|
|
|
}
|
Update overall revision status after reviewers change
Summary:
Ref T2222. This doesn't feel super clean, but doesn't feel too bad either.
Basically, Differential transactions can have secondary state-based effects (changing the overall revision status) when reviewers resign, are removed, accept, or reject revisions.
To deal with this in ApplicationTransactions, I did this:
- `applyFinalEffects()` can now alter the transaction set (notably, add new ones). This mostly matters for email, notifications and feed.
- In Differential, check for an overall revision state transition in `applyFinalEffects()` (e.g., your reject moving the revision to a rejected state).
- I'm only writing the transaction if the transition is implied and indirect.
- For example, if you "Plan Changes", that action changes the state on its own so there's no implicit state change transaction added.
The transactions themselves are kind of fluff, but it seems useful to keep a record of when state changes occurred in the transaction log. If people complain we can hide/remove them.
Test Plan: {F118143}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2222
Differential Revision: https://secure.phabricator.com/D8339
2014-02-25 21:36:49 +01:00
|
|
|
|
2014-06-11 22:52:15 +02:00
|
|
|
if ($xactions) {
|
|
|
|
$data = array(
|
|
|
|
'type' => 'message',
|
|
|
|
'threadPHID' => $object->getPHID(),
|
|
|
|
'messageID' => last($xactions)->getID(),
|
|
|
|
'subscribers' => array($object->getPHID()),
|
|
|
|
);
|
|
|
|
|
|
|
|
PhabricatorNotificationClient::tryToPostMessage($data);
|
|
|
|
}
|
|
|
|
|
Update overall revision status after reviewers change
Summary:
Ref T2222. This doesn't feel super clean, but doesn't feel too bad either.
Basically, Differential transactions can have secondary state-based effects (changing the overall revision status) when reviewers resign, are removed, accept, or reject revisions.
To deal with this in ApplicationTransactions, I did this:
- `applyFinalEffects()` can now alter the transaction set (notably, add new ones). This mostly matters for email, notifications and feed.
- In Differential, check for an overall revision state transition in `applyFinalEffects()` (e.g., your reject moving the revision to a rejected state).
- I'm only writing the transaction if the transition is implied and indirect.
- For example, if you "Plan Changes", that action changes the state on its own so there's no implicit state change transaction added.
The transactions themselves are kind of fluff, but it seems useful to keep a record of when state changes occurred in the transaction log. If people complain we can hide/remove them.
Test Plan: {F118143}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2222
Differential Revision: https://secure.phabricator.com/D8339
2014-02-25 21:36:49 +01:00
|
|
|
return $xactions;
|
2013-01-25 02:23:05 +01:00
|
|
|
}
|
|
|
|
|
2015-03-25 19:48:22 +01:00
|
|
|
protected function requireCapabilities(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
PhabricatorApplicationTransaction $xaction) {
|
|
|
|
|
|
|
|
parent::requireCapabilities($object, $xaction);
|
|
|
|
|
|
|
|
switch ($xaction->getTransactionType()) {
|
|
|
|
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
|
|
|
$old_map = array_fuse($xaction->getOldValue());
|
|
|
|
$new_map = array_fuse($xaction->getNewValue());
|
|
|
|
|
|
|
|
$add = array_keys(array_diff_key($new_map, $old_map));
|
|
|
|
$rem = array_keys(array_diff_key($old_map, $new_map));
|
|
|
|
|
|
|
|
$actor_phid = $this->requireActor()->getPHID();
|
|
|
|
|
|
|
|
$is_join = (($add === array($actor_phid)) && !$rem);
|
|
|
|
$is_leave = (($rem === array($actor_phid)) && !$add);
|
|
|
|
|
|
|
|
if ($is_join) {
|
|
|
|
// You need CAN_JOIN to join a thread / room.
|
|
|
|
PhabricatorPolicyFilter::requireCapability(
|
|
|
|
$this->requireActor(),
|
|
|
|
$object,
|
|
|
|
PhabricatorPolicyCapability::CAN_JOIN);
|
|
|
|
} else if ($is_leave) {
|
|
|
|
// You don't need any capabilities to leave a conpherence thread.
|
|
|
|
} else {
|
|
|
|
// You need CAN_EDIT to change participants other than yourself.
|
|
|
|
PhabricatorPolicyFilter::requireCapability(
|
|
|
|
$this->requireActor(),
|
|
|
|
$object,
|
|
|
|
PhabricatorPolicyCapability::CAN_EDIT);
|
|
|
|
}
|
|
|
|
break;
|
2015-04-17 01:15:36 +02:00
|
|
|
// This is similar to PhabricatorTransactions::TYPE_COMMENT so
|
|
|
|
// use CAN_VIEW
|
2015-03-25 19:48:22 +01:00
|
|
|
case ConpherenceTransactionType::TYPE_FILES:
|
2015-04-17 01:15:36 +02:00
|
|
|
PhabricatorPolicyFilter::requireCapability(
|
|
|
|
$this->requireActor(),
|
|
|
|
$object,
|
|
|
|
PhabricatorPolicyCapability::CAN_VIEW);
|
|
|
|
break;
|
2015-03-25 19:48:22 +01:00
|
|
|
case ConpherenceTransactionType::TYPE_TITLE:
|
|
|
|
PhabricatorPolicyFilter::requireCapability(
|
|
|
|
$this->requireActor(),
|
|
|
|
$object,
|
|
|
|
PhabricatorPolicyCapability::CAN_EDIT);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-25 02:23:05 +01:00
|
|
|
protected function mergeTransactions(
|
|
|
|
PhabricatorApplicationTransaction $u,
|
|
|
|
PhabricatorApplicationTransaction $v) {
|
|
|
|
|
|
|
|
$type = $u->getTransactionType();
|
|
|
|
switch ($type) {
|
|
|
|
case ConpherenceTransactionType::TYPE_TITLE:
|
|
|
|
return $v;
|
|
|
|
case ConpherenceTransactionType::TYPE_FILES:
|
|
|
|
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
2013-03-28 16:34:34 +01:00
|
|
|
return $this->mergePHIDOrEdgeTransactions($u, $v);
|
2013-01-25 02:23:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return parent::mergeTransactions($u, $v);
|
|
|
|
}
|
|
|
|
|
2013-09-14 00:08:17 +02:00
|
|
|
protected function shouldSendMail(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
array $xactions) {
|
2013-01-26 01:03:54 +01:00
|
|
|
return true;
|
2013-01-25 02:23:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
|
|
|
|
return id(new ConpherenceReplyHandler())
|
2013-01-26 01:03:54 +01:00
|
|
|
->setActor($this->getActor())
|
2013-01-25 02:23:05 +01:00
|
|
|
->setMailReceiver($object);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
|
|
|
|
$id = $object->getID();
|
|
|
|
$title = $object->getTitle();
|
2013-01-26 01:03:54 +01:00
|
|
|
if (!$title) {
|
|
|
|
$title = pht(
|
|
|
|
'%s sent you a message.',
|
2013-02-19 22:33:10 +01:00
|
|
|
$this->getActor()->getUserName());
|
2013-01-26 01:03:54 +01:00
|
|
|
}
|
2013-01-25 02:23:05 +01:00
|
|
|
$phid = $object->getPHID();
|
|
|
|
|
|
|
|
return id(new PhabricatorMetaMTAMail())
|
2015-04-29 23:03:36 +02:00
|
|
|
->setSubject("Z{$id}: {$title}")
|
|
|
|
->addHeader('Thread-Topic', "Z{$id}: {$phid}");
|
2013-01-25 02:23:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function getMailTo(PhabricatorLiskDAO $object) {
|
2013-04-02 18:32:40 +02:00
|
|
|
$to_phids = array();
|
2013-01-25 02:23:05 +01:00
|
|
|
$participants = $object->getParticipants();
|
2013-04-02 18:32:40 +02:00
|
|
|
if (empty($participants)) {
|
|
|
|
return $to_phids;
|
|
|
|
}
|
2013-03-26 21:30:35 +01:00
|
|
|
$preferences = id(new PhabricatorUserPreferences())
|
|
|
|
->loadAllWhere('userPHID in (%Ls)', array_keys($participants));
|
|
|
|
$preferences = mpull($preferences, null, 'getUserPHID');
|
|
|
|
foreach ($participants as $phid => $participant) {
|
|
|
|
$default = ConpherenceSettings::EMAIL_ALWAYS;
|
|
|
|
$preference = idx($preferences, $phid);
|
|
|
|
if ($preference) {
|
|
|
|
$default = $preference->getPreference(
|
|
|
|
PhabricatorUserPreferences::PREFERENCE_CONPH_NOTIFICATIONS,
|
|
|
|
ConpherenceSettings::EMAIL_ALWAYS);
|
|
|
|
}
|
|
|
|
$settings = $participant->getSettings();
|
|
|
|
$notifications = idx(
|
|
|
|
$settings,
|
|
|
|
'notifications',
|
|
|
|
$default);
|
|
|
|
if ($notifications == ConpherenceSettings::EMAIL_ALWAYS) {
|
|
|
|
$to_phids[] = $phid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $to_phids;
|
2013-01-25 02:23:05 +01:00
|
|
|
}
|
|
|
|
|
2013-01-26 01:03:54 +01:00
|
|
|
protected function getMailCC(PhabricatorLiskDAO $object) {
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
2013-01-25 02:23:05 +01:00
|
|
|
protected function buildMailBody(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
array $xactions) {
|
|
|
|
|
|
|
|
$body = parent::buildMailBody($object, $xactions);
|
2014-10-30 23:24:10 +01:00
|
|
|
$body->addLinkSection(
|
2013-01-25 02:23:05 +01:00
|
|
|
pht('CONPHERENCE DETAIL'),
|
2015-05-06 00:49:43 +02:00
|
|
|
PhabricatorEnv::getProductionURI('/'.$object->getMonogram()));
|
2013-01-25 02:23:05 +01:00
|
|
|
|
|
|
|
return $body;
|
|
|
|
}
|
|
|
|
|
2015-05-29 00:30:33 +02:00
|
|
|
protected function addEmailPreferenceSectionToMailBody(
|
|
|
|
PhabricatorMetaMTAMailBody $body,
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
array $xactions) {
|
|
|
|
|
|
|
|
$href = PhabricatorEnv::getProductionURI(
|
|
|
|
'/'.$object->getMonogram().'?settings');
|
|
|
|
if ($object->getIsRoom()) {
|
|
|
|
$label = pht('EMAIL PREFERENCES FOR THIS ROOM');
|
|
|
|
} else {
|
|
|
|
$label = pht('EMAIL PREFERENCES FOR THIS MESSAGE');
|
|
|
|
}
|
|
|
|
$body->addLinkSection($label, $href);
|
|
|
|
}
|
|
|
|
|
2013-01-25 02:23:05 +01:00
|
|
|
protected function getMailSubjectPrefix() {
|
|
|
|
return PhabricatorEnv::getEnvConfig('metamta.conpherence.subject-prefix');
|
|
|
|
}
|
|
|
|
|
2014-03-05 02:01:33 +01:00
|
|
|
protected function shouldPublishFeedStory(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
array $xactions) {
|
2013-01-25 02:23:05 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function supportsSearch() {
|
2015-01-06 19:24:30 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getSearchContextParameter(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
array $xactions) {
|
|
|
|
|
|
|
|
$comment_phids = array();
|
|
|
|
foreach ($xactions as $xaction) {
|
|
|
|
if ($xaction->hasComment()) {
|
|
|
|
$comment_phids[] = $xaction->getPHID();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return array(
|
|
|
|
'commentPHIDs' => $comment_phids,
|
|
|
|
);
|
2013-01-25 02:23:05 +01:00
|
|
|
}
|
2014-07-10 00:12:48 +02:00
|
|
|
|
2015-05-04 22:52:22 +02:00
|
|
|
protected function extractFilePHIDsFromCustomTransaction(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
PhabricatorApplicationTransaction $xaction) {
|
|
|
|
|
|
|
|
switch ($xaction->getTransactionType()) {
|
|
|
|
case ConpherenceTransactionType::TYPE_PICTURE:
|
|
|
|
return array($xaction->getNewValue()->getPHID());
|
|
|
|
case ConpherenceTransactionType::TYPE_PICTURE_CROP:
|
|
|
|
return array($xaction->getNewValue());
|
|
|
|
}
|
|
|
|
|
|
|
|
return parent::extractFilePHIDsFromCustomTransaction($object, $xaction);
|
|
|
|
}
|
|
|
|
|
2015-03-03 19:40:00 +01:00
|
|
|
protected function validateTransaction(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
$type,
|
|
|
|
array $xactions) {
|
|
|
|
|
|
|
|
$errors = parent::validateTransaction($object, $type, $xactions);
|
|
|
|
|
|
|
|
switch ($type) {
|
2015-03-24 21:04:33 +01:00
|
|
|
case ConpherenceTransactionType::TYPE_TITLE:
|
2015-03-25 19:48:22 +01:00
|
|
|
if (!$object->getIsRoom()) {
|
2015-03-24 21:04:33 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$missing = $this->validateIsEmptyTextField(
|
|
|
|
$object->getTitle(),
|
|
|
|
$xactions);
|
|
|
|
|
|
|
|
if ($missing) {
|
|
|
|
if ($object->getIsRoom()) {
|
|
|
|
$detail = pht('Room title is required.');
|
|
|
|
} else {
|
|
|
|
$detail = pht('Thread title can not be blank.');
|
|
|
|
}
|
|
|
|
$error = new PhabricatorApplicationTransactionValidationError(
|
|
|
|
$type,
|
|
|
|
pht('Required'),
|
|
|
|
$detail,
|
|
|
|
last($xactions));
|
|
|
|
|
|
|
|
$error->setIsMissingFieldError(true);
|
|
|
|
$errors[] = $error;
|
|
|
|
}
|
|
|
|
break;
|
2015-05-04 22:52:22 +02:00
|
|
|
case ConpherenceTransactionType::TYPE_PICTURE:
|
|
|
|
foreach ($xactions as $xaction) {
|
|
|
|
$file = $xaction->getNewValue();
|
|
|
|
if (!$file->isTransformableImage()) {
|
|
|
|
$detail = pht('This server only supports these image formats: %s.',
|
|
|
|
implode(', ', PhabricatorFile::getTransformableImageFormats()));
|
|
|
|
$error = new PhabricatorApplicationTransactionValidationError(
|
|
|
|
$type,
|
|
|
|
pht('Invalid'),
|
|
|
|
$detail,
|
|
|
|
last($xactions));
|
|
|
|
$errors[] = $error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2015-03-03 19:40:00 +01:00
|
|
|
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
|
|
|
foreach ($xactions as $xaction) {
|
2015-04-06 20:45:43 +02:00
|
|
|
$new_phids = $this->getPHIDTransactionNewValue($xaction, array());
|
|
|
|
$old_phids = nonempty($object->getParticipantPHIDs(), array());
|
|
|
|
$phids = array_diff($new_phids, $old_phids);
|
2015-03-03 19:40:00 +01:00
|
|
|
|
|
|
|
if (!$phids) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$users = id(new PhabricatorPeopleQuery())
|
|
|
|
->setViewer($this->requireActor())
|
|
|
|
->withPHIDs($phids)
|
|
|
|
->execute();
|
|
|
|
$users = mpull($users, null, 'getPHID');
|
|
|
|
foreach ($phids as $phid) {
|
|
|
|
if (isset($users[$phid])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
|
|
|
$type,
|
|
|
|
pht('Invalid'),
|
|
|
|
pht('New thread member "%s" is not a valid user.', $phid),
|
|
|
|
$xaction);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $errors;
|
|
|
|
}
|
2013-01-25 02:23:05 +01:00
|
|
|
}
|