Page MenuHomestyx hydra

No OneTemporary

diff --git a/resources/sql/autopatches/20170419.thread.03.touched.sql b/resources/sql/autopatches/20170419.thread.03.touched.sql
new file mode 100644
index 0000000000..f6fee00272
--- /dev/null
+++ b/resources/sql/autopatches/20170419.thread.03.touched.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_conpherence.conpherence_participant
+ DROP dateTouched;
diff --git a/src/applications/conpherence/editor/ConpherenceEditor.php b/src/applications/conpherence/editor/ConpherenceEditor.php
index 6668b3324f..59d854cdc3 100644
--- a/src/applications/conpherence/editor/ConpherenceEditor.php
+++ b/src/applications/conpherence/editor/ConpherenceEditor.php
@@ -1,512 +1,508 @@
<?php
final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
const ERROR_EMPTY_PARTICIPANTS = 'error-empty-participants';
const ERROR_EMPTY_MESSAGE = 'error-empty-message';
public function getEditorApplicationClass() {
return 'PhabricatorConpherenceApplication';
}
public function getEditorObjectsDescription() {
return pht('Conpherence Rooms');
}
public static function createThread(
PhabricatorUser $creator,
array $participant_phids,
$title,
$message,
PhabricatorContentSource $source,
$topic) {
$conpherence = ConpherenceThread::initializeNewRoom($creator);
$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;
}
if (!$errors) {
$xactions = array();
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS)
->setNewValue(array('+' => $participant_phids));
if ($title) {
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(
ConpherenceThreadTitleTransaction::TRANSACTIONTYPE)
->setNewValue($title);
}
if (strlen($topic)) {
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(
ConpherenceThreadTopicTransaction::TRANSACTIONTYPE)
->setNewValue($topic);
}
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
->attachComment(
id(new ConpherenceTransactionComment())
->setContent($message)
->setConpherencePHID($conpherence->getPHID()));
id(new ConpherenceEditor())
->setActor($creator)
->setContentSource($source)
->setContinueOnNoEffect(true)
->applyTransactions($conpherence, $xactions);
}
return array($errors, $conpherence);
}
public function generateTransactionsFromText(
PhabricatorUser $viewer,
ConpherenceThread $conpherence,
$text) {
$xactions = array();
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
->attachComment(
id(new ConpherenceTransactionComment())
->setContent($text)
->setConpherencePHID($conpherence->getPHID()));
return $xactions;
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = ConpherenceTransaction::TYPE_PARTICIPANTS;
$types[] = PhabricatorTransactions::TYPE_COMMENT;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
return $types;
}
public function getCreateObjectTitle($author, $object) {
return pht('%s created this room.', $author);
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case ConpherenceTransaction::TYPE_PARTICIPANTS:
if ($this->getIsNewObject()) {
return array();
}
return $object->getParticipantPHIDs();
}
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case ConpherenceTransaction::TYPE_PARTICIPANTS:
return $this->getPHIDTransactionNewValue($xaction);
}
}
/**
* 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(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
$lock = false;
switch ($xaction->getTransactionType()) {
case PhabricatorTransactions::TYPE_COMMENT:
$lock = true;
break;
}
return $lock;
}
/**
* We need to apply initial effects IFF the conpherence is new. We must
* 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.
*/
protected function shouldApplyInitialEffects(
PhabricatorLiskDAO $object,
array $xactions) {
return $this->getIsNewObject();
}
protected function applyInitialEffects(
PhabricatorLiskDAO $object,
array $xactions) {
$object->save();
foreach ($xactions as $xaction) {
switch ($xaction->getTransactionType()) {
case ConpherenceTransaction::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.
$participants = array();
$phids = $this->getPHIDTransactionNewValue($xaction, array());
foreach ($phids as $phid) {
if ($phid == $this->getActor()->getPHID()) {
$message_count = 1;
} else {
$message_count = 0;
}
$participants[$phid] =
id(new ConpherenceParticipant())
->setConpherencePHID($object->getPHID())
->setParticipantPHID($phid)
- ->setDateTouched(time())
->setSeenMessageCount($message_count)
->save();
$object->attachParticipants($participants);
}
break;
}
}
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case ConpherenceTransaction::TYPE_PARTICIPANTS:
if (!$this->getIsNewObject()) {}
break;
}
}
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);
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case ConpherenceTransaction::TYPE_PARTICIPANTS:
if ($this->getIsNewObject()) {
continue;
}
$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) {
if ($phid == $this->getActor()->getPHID()) {
$message_count = $object->getMessageCount();
} else {
$message_count = 0;
}
$participants[$phid] =
id(new ConpherenceParticipant())
->setConpherencePHID($object->getPHID())
->setParticipantPHID($phid)
- ->setDateTouched(time())
->setSeenMessageCount($message_count)
->save();
}
$object->attachParticipants($participants);
break;
}
}
protected function applyFinalEffects(
PhabricatorLiskDAO $object,
array $xactions) {
if (!$xactions) {
return $xactions;
}
$message_count = 0;
foreach ($xactions as $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorTransactions::TYPE_COMMENT:
$message_count++;
// update everyone's participation status on a message -only-
$xaction_phid = $xaction->getPHID();
$participants = $object->getParticipants();
$user = $this->getActor();
$time = time();
foreach ($participants as $phid => $participant) {
if ($phid != $user->getPHID()) {
if ($participant->isUpToDate($object)) {
$participant->setSeenMessageCount(
$object->getMessageCount() - $message_count);
}
- $participant->setDateTouched($time);
} else {
$participant->setSeenMessageCount($object->getMessageCount());
- $participant->setDateTouched($time);
}
$participant->save();
}
PhabricatorUserCache::clearCaches(
PhabricatorUserMessageCountCacheType::KEY_COUNT,
array_keys($participants));
break;
}
}
if ($xactions) {
$data = array(
'type' => 'message',
'threadPHID' => $object->getPHID(),
'messageID' => last($xactions)->getID(),
'subscribers' => array($object->getPHID()),
);
PhabricatorNotificationClient::tryToPostMessage($data);
}
return $xactions;
}
protected function requireCapabilities(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
parent::requireCapabilities($object, $xaction);
switch ($xaction->getTransactionType()) {
case ConpherenceTransaction::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->getActingAsPHID();
$is_join = (($add === array($actor_phid)) && !$rem);
$is_leave = (($rem === array($actor_phid)) && !$add);
if ($is_join) {
// Anyone can join a thread they can see.
} else if ($is_leave) {
// Anyone can leave a thread.
} else {
// You need CAN_EDIT to add or remove participants. For additional
// discussion, see D17696 and T4411.
PhabricatorPolicyFilter::requireCapability(
$this->requireActor(),
$object,
PhabricatorPolicyCapability::CAN_EDIT);
}
break;
case ConpherenceThreadTitleTransaction::TRANSACTIONTYPE:
case ConpherenceThreadTopicTransaction::TRANSACTIONTYPE:
PhabricatorPolicyFilter::requireCapability(
$this->requireActor(),
$object,
PhabricatorPolicyCapability::CAN_EDIT);
break;
}
}
protected function mergeTransactions(
PhabricatorApplicationTransaction $u,
PhabricatorApplicationTransaction $v) {
$type = $u->getTransactionType();
switch ($type) {
case ConpherenceTransaction::TYPE_PARTICIPANTS:
return $this->mergePHIDOrEdgeTransactions($u, $v);
}
return parent::mergeTransactions($u, $v);
}
protected function shouldSendMail(
PhabricatorLiskDAO $object,
array $xactions) {
return true;
}
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
return id(new ConpherenceReplyHandler())
->setActor($this->getActor())
->setMailReceiver($object);
}
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
$id = $object->getID();
$title = $object->getTitle();
if (!$title) {
$title = pht(
'%s sent you a message.',
$this->getActor()->getUserName());
}
$phid = $object->getPHID();
return id(new PhabricatorMetaMTAMail())
->setSubject("Z{$id}: {$title}")
->addHeader('Thread-Topic', "Z{$id}: {$phid}");
}
protected function getMailTo(PhabricatorLiskDAO $object) {
$to_phids = array();
$participants = $object->getParticipants();
if (!$participants) {
return $to_phids;
}
$participant_phids = mpull($participants, 'getParticipantPHID');
$users = id(new PhabricatorPeopleQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs($participant_phids)
->needUserSettings(true)
->execute();
$users = mpull($users, null, 'getPHID');
$notification_key = PhabricatorConpherenceNotificationsSetting::SETTINGKEY;
$notification_email =
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL;
foreach ($participants as $phid => $participant) {
$user = idx($users, $phid);
if ($user) {
$default = $user->getUserSetting($notification_key);
} else {
$default = $notification_email;
}
$settings = $participant->getSettings();
$notifications = idx($settings, 'notifications', $default);
if ($notifications == $notification_email) {
$to_phids[] = $phid;
}
}
return $to_phids;
}
protected function getMailCC(PhabricatorLiskDAO $object) {
return array();
}
protected function buildMailBody(
PhabricatorLiskDAO $object,
array $xactions) {
$body = parent::buildMailBody($object, $xactions);
$body->addLinkSection(
pht('CONPHERENCE DETAIL'),
PhabricatorEnv::getProductionURI('/'.$object->getMonogram()));
return $body;
}
protected function addEmailPreferenceSectionToMailBody(
PhabricatorMetaMTAMailBody $body,
PhabricatorLiskDAO $object,
array $xactions) {
$href = PhabricatorEnv::getProductionURI(
'/'.$object->getMonogram().'?settings');
$label = pht('EMAIL PREFERENCES FOR THIS ROOM');
$body->addLinkSection($label, $href);
}
protected function getMailSubjectPrefix() {
return PhabricatorEnv::getEnvConfig('metamta.conpherence.subject-prefix');
}
protected function supportsSearch() {
return true;
}
protected function validateTransaction(
PhabricatorLiskDAO $object,
$type,
array $xactions) {
$errors = parent::validateTransaction($object, $type, $xactions);
switch ($type) {
case ConpherenceTransaction::TYPE_PARTICIPANTS:
foreach ($xactions as $xaction) {
$new_phids = $this->getPHIDTransactionNewValue($xaction, array());
$old_phids = nonempty($object->getParticipantPHIDs(), array());
$phids = array_diff($new_phids, $old_phids);
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 room participant "%s" is not a valid user.', $phid),
$xaction);
}
}
break;
}
return $errors;
}
}
diff --git a/src/applications/conpherence/query/ConpherenceParticipantQuery.php b/src/applications/conpherence/query/ConpherenceParticipantQuery.php
index bb879be4e4..e538ded2c2 100644
--- a/src/applications/conpherence/query/ConpherenceParticipantQuery.php
+++ b/src/applications/conpherence/query/ConpherenceParticipantQuery.php
@@ -1,128 +1,65 @@
<?php
-/**
- * Query class that answers these questions:
- *
- * - Q: What are the conpherences to show when I land on /conpherence/ ?
- * - A:
- *
- * id(new ConpherenceParticipantQuery())
- * ->withParticipantPHIDs(array($my_phid))
- * ->execute();
- *
- * - Q: What are the next set of conpherences as I scroll up (more recent) or
- * down (less recent) this list of conpherences?
- * - A:
- *
- * id(new ConpherenceParticipantQuery())
- * ->withParticipantPHIDs(array($my_phid))
- * ->withParticipantCursor($top_participant)
- * ->setOrder(ConpherenceParticipantQuery::ORDER_NEWER)
- * ->execute();
- *
- * -or-
- *
- * id(new ConpherenceParticipantQuery())
- * ->withParticipantPHIDs(array($my_phid))
- * ->withParticipantCursor($bottom_participant)
- * ->setOrder(ConpherenceParticipantQuery::ORDER_OLDER)
- * ->execute();
- *
- * For counts of read, un-read, or all conpherences by participant, see
- * @{class:ConpherenceParticipantCountQuery}.
- */
final class ConpherenceParticipantQuery extends PhabricatorOffsetPagedQuery {
- const LIMIT = 100;
- const ORDER_NEWER = 'newer';
- const ORDER_OLDER = 'older';
-
private $participantPHIDs;
- private $participantCursor;
- private $order = self::ORDER_OLDER;
public function withParticipantPHIDs(array $phids) {
$this->participantPHIDs = $phids;
return $this;
}
- public function withParticipantCursor(ConpherenceParticipant $participant) {
- $this->participantCursor = $participant;
- return $this;
- }
-
- public function setOrder($order) {
- $this->order = $order;
- return $this;
- }
-
public function execute() {
$table = new ConpherenceParticipant();
- $conn_r = $table->establishConnection('r');
+ $thread = new ConpherenceThread();
+
+ $conn = $table->establishConnection('r');
$data = queryfx_all(
- $conn_r,
- 'SELECT * FROM %T participant %Q %Q %Q',
+ $conn,
+ 'SELECT * FROM %T participant JOIN %T thread
+ ON participant.conpherencePHID = thread.phid %Q %Q %Q',
$table->getTableName(),
- $this->buildWhereClause($conn_r),
- $this->buildOrderClause($conn_r),
- $this->buildLimitClause($conn_r));
+ $thread->getTableName(),
+ $this->buildWhereClause($conn),
+ $this->buildOrderClause($conn),
+ $this->buildLimitClause($conn));
$participants = $table->loadAllFromArray($data);
- $participants = mpull($participants, null, 'getConpherencePHID');
-
- if ($this->order == self::ORDER_NEWER) {
- $participants = array_reverse($participants);
+ // TODO: Fix this, it's bogus.
+ if ('garbage') {
+ if (count($this->participantPHIDs) !== 1) {
+ throw new Exception(
+ pht(
+ 'This query only works when querying for exactly one participant '.
+ 'PHID!'));
+ }
+ // This will throw results away if we aren't doing a query for exactly
+ // one participant PHID.
+ $participants = mpull($participants, null, 'getConpherencePHID');
}
return $participants;
}
- protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
+ protected function buildWhereClause(AphrontDatabaseConnection $conn) {
$where = array();
- if ($this->participantPHIDs) {
+ if ($this->participantPHIDs !== null) {
$where[] = qsprintf(
- $conn_r,
+ $conn,
'participantPHID IN (%Ls)',
$this->participantPHIDs);
}
- if ($this->participantCursor) {
- $date_touched = $this->participantCursor->getDateTouched();
- $id = $this->participantCursor->getID();
- if ($this->order == self::ORDER_OLDER) {
- $compare_date = '<';
- $compare_id = '<=';
- } else {
- $compare_date = '>';
- $compare_id = '>=';
- }
- $where[] = qsprintf(
- $conn_r,
- '(dateTouched %Q %d OR (dateTouched = %d AND id %Q %d))',
- $compare_date,
- $date_touched,
- $date_touched,
- $compare_id,
- $id);
- }
-
return $this->formatWhereClause($where);
}
- private function buildOrderClause(AphrontDatabaseConnection $conn_r) {
- $order_word = ($this->order == self::ORDER_OLDER) ? 'DESC' : 'ASC';
- // if these are different direction we won't get as efficient a query
- // see http://dev.mysql.com/doc/refman/5.5/en/order-by-optimization.html
- $order = qsprintf(
- $conn_r,
- 'ORDER BY dateTouched %Q, id %Q',
- $order_word,
- $order_word);
-
- return $order;
+ private function buildOrderClause(AphrontDatabaseConnection $conn) {
+ return qsprintf(
+ $conn,
+ 'ORDER BY thread.dateModified DESC, thread.id DESC, participant.id DESC');
}
}
diff --git a/src/applications/conpherence/storage/ConpherenceParticipant.php b/src/applications/conpherence/storage/ConpherenceParticipant.php
index cb4f7f401d..1c4178c2dc 100644
--- a/src/applications/conpherence/storage/ConpherenceParticipant.php
+++ b/src/applications/conpherence/storage/ConpherenceParticipant.php
@@ -1,59 +1,54 @@
<?php
final class ConpherenceParticipant extends ConpherenceDAO {
protected $participantPHID;
protected $conpherencePHID;
protected $seenMessageCount;
- protected $dateTouched;
protected $settings = array();
protected function getConfiguration() {
return array(
self::CONFIG_SERIALIZATION => array(
'settings' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
- 'dateTouched' => 'epoch',
'seenMessageCount' => 'uint64',
),
self::CONFIG_KEY_SCHEMA => array(
'conpherencePHID' => array(
'columns' => array('conpherencePHID', 'participantPHID'),
'unique' => true,
),
- 'participationIndex' => array(
- 'columns' => array('participantPHID', 'dateTouched', 'id'),
- ),
'key_thread' => array(
'columns' => array('participantPHID', 'conpherencePHID'),
),
),
) + parent::getConfiguration();
}
public function getSettings() {
return nonempty($this->settings, array());
}
public function markUpToDate(
ConpherenceThread $conpherence,
ConpherenceTransaction $xaction) {
if (!$this->isUpToDate($conpherence)) {
$this->setSeenMessageCount($conpherence->getMessageCount());
$this->save();
PhabricatorUserCache::clearCache(
PhabricatorUserMessageCountCacheType::KEY_COUNT,
$this->getParticipantPHID());
}
return $this;
}
public function isUpToDate(ConpherenceThread $conpherence) {
return ($this->getSeenMessageCount() == $conpherence->getMessageCount());
}
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Feb 7, 9:29 AM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34088
Default Alt Text
(23 KB)

Event Timeline