mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 08:42:41 +01:00
Hook Ponder to Subscriptions
Summary: Adding email aggro email aggro Test Plan: Added some content; annoyed some coworkers. Reviewers: epriestley, nh, vrana Reviewed By: epriestley CC: aran, Korvin Maniphest Tasks: T1808 Differential Revision: https://secure.phabricator.com/D3643
This commit is contained in:
parent
cd9d78c107
commit
3440839c99
14 changed files with 587 additions and 6 deletions
|
@ -1200,15 +1200,19 @@ phutil_register_library_map(array(
|
||||||
'PonderAnswerQuery' => 'applications/ponder/query/PonderAnswerQuery.php',
|
'PonderAnswerQuery' => 'applications/ponder/query/PonderAnswerQuery.php',
|
||||||
'PonderAnswerSaveController' => 'applications/ponder/controller/PonderAnswerSaveController.php',
|
'PonderAnswerSaveController' => 'applications/ponder/controller/PonderAnswerSaveController.php',
|
||||||
'PonderAnswerViewController' => 'applications/ponder/controller/PonderAnswerViewController.php',
|
'PonderAnswerViewController' => 'applications/ponder/controller/PonderAnswerViewController.php',
|
||||||
|
'PonderAnsweredMail' => 'applications/ponder/mail/PonderAnsweredMail.php',
|
||||||
'PonderComment' => 'applications/ponder/storage/PonderComment.php',
|
'PonderComment' => 'applications/ponder/storage/PonderComment.php',
|
||||||
'PonderCommentEditor' => 'applications/ponder/editor/PonderCommentEditor.php',
|
'PonderCommentEditor' => 'applications/ponder/editor/PonderCommentEditor.php',
|
||||||
'PonderCommentListView' => 'applications/ponder/view/PonderCommentListView.php',
|
'PonderCommentListView' => 'applications/ponder/view/PonderCommentListView.php',
|
||||||
|
'PonderCommentMail' => 'applications/ponder/mail/PonderCommentMail.php',
|
||||||
'PonderCommentQuery' => 'applications/ponder/query/PonderCommentQuery.php',
|
'PonderCommentQuery' => 'applications/ponder/query/PonderCommentQuery.php',
|
||||||
'PonderCommentSaveController' => 'applications/ponder/controller/PonderCommentSaveController.php',
|
'PonderCommentSaveController' => 'applications/ponder/controller/PonderCommentSaveController.php',
|
||||||
'PonderConstants' => 'applications/ponder/PonderConstants.php',
|
'PonderConstants' => 'applications/ponder/PonderConstants.php',
|
||||||
'PonderController' => 'applications/ponder/controller/PonderController.php',
|
'PonderController' => 'applications/ponder/controller/PonderController.php',
|
||||||
'PonderDAO' => 'applications/ponder/storage/PonderDAO.php',
|
'PonderDAO' => 'applications/ponder/storage/PonderDAO.php',
|
||||||
'PonderFeedController' => 'applications/ponder/controller/PonderFeedController.php',
|
'PonderFeedController' => 'applications/ponder/controller/PonderFeedController.php',
|
||||||
|
'PonderMail' => 'applications/ponder/mail/PonderMail.php',
|
||||||
|
'PonderMentionMail' => 'applications/ponder/mail/PonderMentionMail.php',
|
||||||
'PonderPostBodyView' => 'applications/ponder/view/PonderPostBodyView.php',
|
'PonderPostBodyView' => 'applications/ponder/view/PonderPostBodyView.php',
|
||||||
'PonderQuestion' => 'applications/ponder/storage/PonderQuestion.php',
|
'PonderQuestion' => 'applications/ponder/storage/PonderQuestion.php',
|
||||||
'PonderQuestionAskController' => 'applications/ponder/controller/PonderQuestionAskController.php',
|
'PonderQuestionAskController' => 'applications/ponder/controller/PonderQuestionAskController.php',
|
||||||
|
@ -1218,6 +1222,7 @@ phutil_register_library_map(array(
|
||||||
'PonderQuestionQuery' => 'applications/ponder/query/PonderQuestionQuery.php',
|
'PonderQuestionQuery' => 'applications/ponder/query/PonderQuestionQuery.php',
|
||||||
'PonderQuestionSummaryView' => 'applications/ponder/view/PonderQuestionSummaryView.php',
|
'PonderQuestionSummaryView' => 'applications/ponder/view/PonderQuestionSummaryView.php',
|
||||||
'PonderQuestionViewController' => 'applications/ponder/controller/PonderQuestionViewController.php',
|
'PonderQuestionViewController' => 'applications/ponder/controller/PonderQuestionViewController.php',
|
||||||
|
'PonderReplyHandler' => 'applications/ponder/PonderReplyHandler.php',
|
||||||
'PonderRuleQuestion' => 'infrastructure/markup/rule/PonderRuleQuestion.php',
|
'PonderRuleQuestion' => 'infrastructure/markup/rule/PonderRuleQuestion.php',
|
||||||
'PonderUserProfileView' => 'applications/ponder/view/PonderUserProfileView.php',
|
'PonderUserProfileView' => 'applications/ponder/view/PonderUserProfileView.php',
|
||||||
'PonderVotableInterface' => 'applications/ponder/storage/PonderVotableInterface.php',
|
'PonderVotableInterface' => 'applications/ponder/storage/PonderVotableInterface.php',
|
||||||
|
@ -2318,23 +2323,27 @@ phutil_register_library_map(array(
|
||||||
'PonderAnswerQuery' => 'PhabricatorOffsetPagedQuery',
|
'PonderAnswerQuery' => 'PhabricatorOffsetPagedQuery',
|
||||||
'PonderAnswerSaveController' => 'PonderController',
|
'PonderAnswerSaveController' => 'PonderController',
|
||||||
'PonderAnswerViewController' => 'PonderController',
|
'PonderAnswerViewController' => 'PonderController',
|
||||||
|
'PonderAnsweredMail' => 'PonderMail',
|
||||||
'PonderComment' =>
|
'PonderComment' =>
|
||||||
array(
|
array(
|
||||||
0 => 'PonderDAO',
|
0 => 'PonderDAO',
|
||||||
1 => 'PhabricatorMarkupInterface',
|
1 => 'PhabricatorMarkupInterface',
|
||||||
),
|
),
|
||||||
'PonderCommentListView' => 'AphrontView',
|
'PonderCommentListView' => 'AphrontView',
|
||||||
|
'PonderCommentMail' => 'PonderMail',
|
||||||
'PonderCommentQuery' => 'PhabricatorQuery',
|
'PonderCommentQuery' => 'PhabricatorQuery',
|
||||||
'PonderCommentSaveController' => 'PonderController',
|
'PonderCommentSaveController' => 'PonderController',
|
||||||
'PonderController' => 'PhabricatorController',
|
'PonderController' => 'PhabricatorController',
|
||||||
'PonderDAO' => 'PhabricatorLiskDAO',
|
'PonderDAO' => 'PhabricatorLiskDAO',
|
||||||
'PonderFeedController' => 'PonderController',
|
'PonderFeedController' => 'PonderController',
|
||||||
|
'PonderMentionMail' => 'PonderMail',
|
||||||
'PonderPostBodyView' => 'AphrontView',
|
'PonderPostBodyView' => 'AphrontView',
|
||||||
'PonderQuestion' =>
|
'PonderQuestion' =>
|
||||||
array(
|
array(
|
||||||
0 => 'PonderDAO',
|
0 => 'PonderDAO',
|
||||||
1 => 'PhabricatorMarkupInterface',
|
1 => 'PhabricatorMarkupInterface',
|
||||||
2 => 'PonderVotableInterface',
|
2 => 'PonderVotableInterface',
|
||||||
|
3 => 'PhabricatorSubscribableInterface',
|
||||||
),
|
),
|
||||||
'PonderQuestionAskController' => 'PonderController',
|
'PonderQuestionAskController' => 'PonderController',
|
||||||
'PonderQuestionDetailView' => 'AphrontView',
|
'PonderQuestionDetailView' => 'AphrontView',
|
||||||
|
@ -2342,6 +2351,7 @@ phutil_register_library_map(array(
|
||||||
'PonderQuestionQuery' => 'PhabricatorOffsetPagedQuery',
|
'PonderQuestionQuery' => 'PhabricatorOffsetPagedQuery',
|
||||||
'PonderQuestionSummaryView' => 'AphrontView',
|
'PonderQuestionSummaryView' => 'AphrontView',
|
||||||
'PonderQuestionViewController' => 'PonderController',
|
'PonderQuestionViewController' => 'PonderController',
|
||||||
|
'PonderReplyHandler' => 'PhabricatorMailReplyHandler',
|
||||||
'PonderRuleQuestion' => 'PhabricatorRemarkupRuleObjectName',
|
'PonderRuleQuestion' => 'PhabricatorRemarkupRuleObjectName',
|
||||||
'PonderUserProfileView' => 'AphrontView',
|
'PonderUserProfileView' => 'AphrontView',
|
||||||
'PonderVotableView' => 'AphrontView',
|
'PonderVotableView' => 'AphrontView',
|
||||||
|
|
48
src/applications/ponder/PonderReplyHandler.php
Normal file
48
src/applications/ponder/PonderReplyHandler.php
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
final class PonderReplyHandler extends PhabricatorMailReplyHandler {
|
||||||
|
|
||||||
|
public function validateMailReceiver($mail_receiver) {
|
||||||
|
if (!($mail_receiver instanceof PonderQuestion)) {
|
||||||
|
throw new Exception("Mail receiver is not a PonderQuestion!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrivateReplyHandlerEmailAddress(
|
||||||
|
PhabricatorObjectHandle $handle) {
|
||||||
|
return $this->getDefaultPrivateReplyHandlerEmailAddress($handle, 'Q');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPublicReplyHandlerEmailAddress() {
|
||||||
|
return $this->getDefaultPublicReplyHandlerEmailAddress('Q');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReplyHandlerDomain() {
|
||||||
|
return PhabricatorEnv::getEnvConfig(
|
||||||
|
'metamta.maniphest.reply-handler-domain');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReplyHandlerInstructions() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function receiveEmail(PhabricatorMetaMTAReceivedMail $mail) {
|
||||||
|
// ignore this entirely for now
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,6 +60,7 @@ final class PonderAnswerSaveController extends PonderController {
|
||||||
->setContentSource($content_source);
|
->setContentSource($content_source);
|
||||||
|
|
||||||
id(new PonderAnswerEditor())
|
id(new PonderAnswerEditor())
|
||||||
|
->setUser($user)
|
||||||
->setQuestion($question)
|
->setQuestion($question)
|
||||||
->setAnswer($res)
|
->setAnswer($res)
|
||||||
->saveAnswer();
|
->saveAnswer();
|
||||||
|
|
|
@ -38,6 +38,7 @@ final class PonderCommentSaveController extends PonderController {
|
||||||
if (!$objects) {
|
if (!$objects) {
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
$content = $request->getStr('content');
|
$content = $request->getStr('content');
|
||||||
|
|
||||||
if (!strlen(trim($content))) {
|
if (!strlen(trim($content))) {
|
||||||
|
@ -59,6 +60,8 @@ final class PonderCommentSaveController extends PonderController {
|
||||||
id(new PonderCommentEditor())
|
id(new PonderCommentEditor())
|
||||||
->setQuestion($question)
|
->setQuestion($question)
|
||||||
->setComment($res)
|
->setComment($res)
|
||||||
|
->setTargetPHID($target)
|
||||||
|
->setUser($user)
|
||||||
->save();
|
->save();
|
||||||
|
|
||||||
return id(new AphrontRedirectResponse())
|
return id(new AphrontRedirectResponse())
|
||||||
|
|
|
@ -53,6 +53,7 @@ final class PonderQuestionAskController extends PonderController {
|
||||||
|
|
||||||
id(new PonderQuestionEditor())
|
id(new PonderQuestionEditor())
|
||||||
->setQuestion($question)
|
->setQuestion($question)
|
||||||
|
->setUser($user)
|
||||||
->save();
|
->save();
|
||||||
|
|
||||||
return id(new AphrontRedirectResponse())
|
return id(new AphrontRedirectResponse())
|
||||||
|
|
|
@ -52,6 +52,11 @@ final class PonderQuestionViewController extends PonderController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID(
|
||||||
|
$question->getPHID());
|
||||||
|
|
||||||
|
$object_phids = array_merge($object_phids, $subscribers);
|
||||||
|
|
||||||
$handles = $this->loadViewerHandles($object_phids);
|
$handles = $this->loadViewerHandles($object_phids);
|
||||||
$this->loadHandles($object_phids);
|
$this->loadHandles($object_phids);
|
||||||
|
|
||||||
|
@ -79,7 +84,7 @@ final class PonderQuestionViewController extends PonderController {
|
||||||
->setHeader($question->getTitle());
|
->setHeader($question->getTitle());
|
||||||
|
|
||||||
$actions = $this->buildActionListView($question);
|
$actions = $this->buildActionListView($question);
|
||||||
$properties = $this->buildPropertyListView($question);
|
$properties = $this->buildPropertyListView($question, $subscribers);
|
||||||
|
|
||||||
$nav = $this->buildSideNavView($question);
|
$nav = $this->buildSideNavView($question);
|
||||||
$nav->appendChild(
|
$nav->appendChild(
|
||||||
|
@ -112,7 +117,10 @@ final class PonderQuestionViewController extends PonderController {
|
||||||
return $view;
|
return $view;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildPropertyListView(PonderQuestion $question) {
|
private function buildPropertyListView(
|
||||||
|
PonderQuestion $question,
|
||||||
|
array $subscribers) {
|
||||||
|
|
||||||
$viewer = $this->getRequest()->getUser();
|
$viewer = $this->getRequest()->getUser();
|
||||||
$view = new PhabricatorPropertyListView();
|
$view = new PhabricatorPropertyListView();
|
||||||
|
|
||||||
|
@ -124,6 +132,17 @@ final class PonderQuestionViewController extends PonderController {
|
||||||
pht('Created'),
|
pht('Created'),
|
||||||
phabricator_datetime($question->getDateCreated(), $viewer));
|
phabricator_datetime($question->getDateCreated(), $viewer));
|
||||||
|
|
||||||
|
if ($subscribers) {
|
||||||
|
foreach ($subscribers as $key => $subscriber) {
|
||||||
|
$subscribers[$key] = $this->getHandle($subscriber)->renderLink();
|
||||||
|
}
|
||||||
|
$subscribers = implode(', ', $subscribers);
|
||||||
|
}
|
||||||
|
|
||||||
|
$view->addProperty(
|
||||||
|
pht('Subscribers'),
|
||||||
|
nonempty($subscribers, '<em>'.pht('None').'</em>'));
|
||||||
|
|
||||||
return $view;
|
return $view;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,11 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
final class PonderAnswerEditor {
|
final class PonderAnswerEditor {
|
||||||
|
|
||||||
private $question;
|
private $question;
|
||||||
private $answer;
|
private $answer;
|
||||||
|
private $viewer;
|
||||||
|
private $shouldEmail = true;
|
||||||
|
|
||||||
public function setQuestion($question) {
|
public function setQuestion($question) {
|
||||||
$this->question = $question;
|
$this->question = $question;
|
||||||
|
@ -32,7 +32,15 @@ final class PonderAnswerEditor {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setUser(PhabricatorUser $user) {
|
||||||
|
$this->viewer = $user;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function saveAnswer() {
|
public function saveAnswer() {
|
||||||
|
if (!$this->viewer) {
|
||||||
|
throw new Exception("Must set user before saving question");
|
||||||
|
}
|
||||||
if (!$this->question) {
|
if (!$this->question) {
|
||||||
throw new Exception("Must set question before saving answer");
|
throw new Exception("Must set question before saving answer");
|
||||||
}
|
}
|
||||||
|
@ -42,6 +50,7 @@ final class PonderAnswerEditor {
|
||||||
|
|
||||||
$question = $this->question;
|
$question = $this->question;
|
||||||
$answer = $this->answer;
|
$answer = $this->answer;
|
||||||
|
$viewer = $this->viewer;
|
||||||
$conn = $answer->establishConnection('w');
|
$conn = $answer->establishConnection('w');
|
||||||
$trans = $conn->openTransaction();
|
$trans = $conn->openTransaction();
|
||||||
$trans->beginReadLocking();
|
$trans->beginReadLocking();
|
||||||
|
@ -63,5 +72,53 @@ final class PonderAnswerEditor {
|
||||||
|
|
||||||
$question->attachRelated();
|
$question->attachRelated();
|
||||||
PhabricatorSearchPonderIndexer::indexQuestion($question);
|
PhabricatorSearchPonderIndexer::indexQuestion($question);
|
||||||
|
|
||||||
|
// subscribe author and @mentions
|
||||||
|
$subeditor = id(new PhabricatorSubscriptionsEditor())
|
||||||
|
->setObject($question)
|
||||||
|
->setUser($viewer);
|
||||||
|
|
||||||
|
$subeditor->subscribeExplicit(array($answer->getAuthorPHID()));
|
||||||
|
|
||||||
|
$content = $answer->getContent();
|
||||||
|
$at_mention_phids = PhabricatorMarkupEngine::extractPHIDsFromMentions(
|
||||||
|
array($content)
|
||||||
|
);
|
||||||
|
$subeditor->subscribeImplicit($at_mention_phids);
|
||||||
|
$subeditor->save();
|
||||||
|
|
||||||
|
if ($this->shouldEmail) {
|
||||||
|
// now load subscribers, including implicitly-added @mention victims
|
||||||
|
$subscribers = PhabricatorSubscribersQuery
|
||||||
|
::loadSubscribersForPHID($question->getPHID());
|
||||||
|
|
||||||
|
|
||||||
|
// @mention emails (but not for anyone who has explicitly unsubscribed)
|
||||||
|
if (array_intersect($at_mention_phids, $subscribers)) {
|
||||||
|
id(new PonderMentionMail(
|
||||||
|
$question,
|
||||||
|
$answer,
|
||||||
|
$viewer))
|
||||||
|
->setToPHIDs($at_mention_phids)
|
||||||
|
->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
$other_subs =
|
||||||
|
array_diff(
|
||||||
|
$subscribers,
|
||||||
|
$at_mention_phids
|
||||||
|
);
|
||||||
|
|
||||||
|
// 'Answered' emails for subscribers who are not @mentiond (and excluding
|
||||||
|
// author depending on their MetaMTA settings).
|
||||||
|
if ($other_subs) {
|
||||||
|
id(new PonderAnsweredMail(
|
||||||
|
$question,
|
||||||
|
$answer,
|
||||||
|
$viewer))
|
||||||
|
->setToPHIDs($other_subs)
|
||||||
|
->send();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,9 @@ final class PonderCommentEditor {
|
||||||
|
|
||||||
private $question;
|
private $question;
|
||||||
private $comment;
|
private $comment;
|
||||||
|
private $targetPHID;
|
||||||
|
private $viewer;
|
||||||
|
private $shouldEmail = true;
|
||||||
|
|
||||||
public function setComment(PonderComment $comment) {
|
public function setComment(PonderComment $comment) {
|
||||||
$this->comment = $comment;
|
$this->comment = $comment;
|
||||||
|
@ -32,6 +35,16 @@ final class PonderCommentEditor {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setTargetPHID($target) {
|
||||||
|
$this->targetPHID = $target;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUser(PhabricatorUser $user) {
|
||||||
|
$this->viewer = $user;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function save() {
|
public function save() {
|
||||||
if (!$this->comment) {
|
if (!$this->comment) {
|
||||||
throw new Exception("Must set comment before saving it");
|
throw new Exception("Must set comment before saving it");
|
||||||
|
@ -39,13 +52,82 @@ final class PonderCommentEditor {
|
||||||
if (!$this->question) {
|
if (!$this->question) {
|
||||||
throw new Exception("Must set question before saving comment");
|
throw new Exception("Must set question before saving comment");
|
||||||
}
|
}
|
||||||
|
if (!$this->targetPHID) {
|
||||||
|
throw new Exception("Must set target before saving comment");
|
||||||
|
}
|
||||||
|
if (!$this->viewer) {
|
||||||
|
throw new Exception("Must set viewer before saving comment");
|
||||||
|
}
|
||||||
|
|
||||||
$comment = $this->comment;
|
$comment = $this->comment;
|
||||||
$question = $this->question;
|
$question = $this->question;
|
||||||
|
$target = $this->targetPHID;
|
||||||
|
$viewer = $this->viewer;
|
||||||
$comment->save();
|
$comment->save();
|
||||||
|
|
||||||
$question->attachRelated();
|
$question->attachRelated();
|
||||||
PhabricatorSearchPonderIndexer::indexQuestion($question);
|
PhabricatorSearchPonderIndexer::indexQuestion($question);
|
||||||
|
|
||||||
|
// subscribe author and @mentions
|
||||||
|
$subeditor = id(new PhabricatorSubscriptionsEditor())
|
||||||
|
->setObject($question)
|
||||||
|
->setUser($viewer);
|
||||||
|
|
||||||
|
$subeditor->subscribeExplicit(array($comment->getAuthorPHID()));
|
||||||
|
|
||||||
|
$content = $comment->getContent();
|
||||||
|
$at_mention_phids = PhabricatorMarkupEngine::extractPHIDsFromMentions(
|
||||||
|
array($content)
|
||||||
|
);
|
||||||
|
$subeditor->subscribeImplicit($at_mention_phids);
|
||||||
|
$subeditor->save();
|
||||||
|
|
||||||
|
if ($this->shouldEmail) {
|
||||||
|
// now load subscribers, including implicitly-added @mention victims
|
||||||
|
$subscribers = PhabricatorSubscribersQuery
|
||||||
|
::loadSubscribersForPHID($question->getPHID());
|
||||||
|
|
||||||
|
// @mention emails (but not for anyone who has explicitly unsubscribed)
|
||||||
|
if (array_intersect($at_mention_phids, $subscribers)) {
|
||||||
|
id(new PonderMentionMail(
|
||||||
|
$question,
|
||||||
|
$comment,
|
||||||
|
$viewer))
|
||||||
|
->setToPHIDs($at_mention_phids)
|
||||||
|
->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($target === $question->getPHID()) {
|
||||||
|
$target = $question;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$answers_by_phid = mgroup($question->getAnswers(), 'getPHID');
|
||||||
|
$target = head($answers_by_phid[$target]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// only send emails to others in the same thread
|
||||||
|
$thread = mpull($target->getComments(), 'getAuthorPHID');
|
||||||
|
$thread[] = $target->getAuthorPHID();
|
||||||
|
$thread[] = $question->getAuthorPHID();
|
||||||
|
|
||||||
|
$other_subs =
|
||||||
|
array_diff(
|
||||||
|
array_intersect($thread, $subscribers),
|
||||||
|
$at_mention_phids
|
||||||
|
);
|
||||||
|
|
||||||
|
// 'Comment' emails for subscribers who are in the same comment thread,
|
||||||
|
// including the author of the parent question and/or answer, excluding
|
||||||
|
// @mentions (and excluding the author, depending on their MetaMTA
|
||||||
|
// settings).
|
||||||
|
if ($other_subs) {
|
||||||
|
id(new PonderCommentMail(
|
||||||
|
$question,
|
||||||
|
$comment,
|
||||||
|
$viewer))
|
||||||
|
->setToPHIDs($other_subs)
|
||||||
|
->send();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,21 +21,60 @@ final class PonderQuestionEditor {
|
||||||
|
|
||||||
private $question;
|
private $question;
|
||||||
private $viewer;
|
private $viewer;
|
||||||
|
private $shouldEmail = true;
|
||||||
|
|
||||||
public function setQuestion(PonderQuestion $question) {
|
public function setQuestion(PonderQuestion $question) {
|
||||||
$this->question = $question;
|
$this->question = $question;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setUser(PhabricatorUser $user) {
|
||||||
|
$this->viewer = $user;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setShouldEmail($se) {
|
||||||
|
$this->shouldEmail = $se;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function save() {
|
public function save() {
|
||||||
|
if (!$this->viewer) {
|
||||||
|
throw new Exception("Must set user before saving question");
|
||||||
|
}
|
||||||
if (!$this->question) {
|
if (!$this->question) {
|
||||||
throw new Exception("Must set question before saving it");
|
throw new Exception("Must set question before saving it");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$viewer = $this->viewer;
|
||||||
$question = $this->question;
|
$question = $this->question;
|
||||||
$question->save();
|
$question->save();
|
||||||
|
|
||||||
|
// search index
|
||||||
$question->attachRelated();
|
$question->attachRelated();
|
||||||
PhabricatorSearchPonderIndexer::indexQuestion($question);
|
PhabricatorSearchPonderIndexer::indexQuestion($question);
|
||||||
|
|
||||||
|
// subscribe author and @mentions
|
||||||
|
$subeditor = id(new PhabricatorSubscriptionsEditor())
|
||||||
|
->setObject($question)
|
||||||
|
->setUser($viewer);
|
||||||
|
|
||||||
|
$subeditor->subscribeExplicit(array($question->getAuthorPHID()));
|
||||||
|
|
||||||
|
$content = $question->getContent();
|
||||||
|
$at_mention_phids = PhabricatorMarkupEngine::extractPHIDsFromMentions(
|
||||||
|
array($content)
|
||||||
|
);
|
||||||
|
$subeditor->subscribeImplicit($at_mention_phids);
|
||||||
|
$subeditor->save();
|
||||||
|
|
||||||
|
if ($this->shouldEmail && $at_mention_phids) {
|
||||||
|
id(new PonderMentionMail(
|
||||||
|
$question,
|
||||||
|
$question,
|
||||||
|
$viewer))
|
||||||
|
->setToPHIDs($at_mention_phids)
|
||||||
|
->send();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
53
src/applications/ponder/mail/PonderAnsweredMail.php
Normal file
53
src/applications/ponder/mail/PonderAnsweredMail.php
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
final class PonderAnsweredMail extends PonderMail {
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
PonderQuestion $question,
|
||||||
|
PonderAnswer $target,
|
||||||
|
PhabricatorUser $actor) {
|
||||||
|
|
||||||
|
$this->setQuestion($question);
|
||||||
|
$this->setTarget($target);
|
||||||
|
$this->setActorHandle($actor);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function renderVaryPrefix() {
|
||||||
|
return "[Answered]";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function renderBody() {
|
||||||
|
$question = $this->getQuestion();
|
||||||
|
$target = $this->getTarget();
|
||||||
|
$actor = $this->getActorName();
|
||||||
|
$name = $question->getTitle();
|
||||||
|
|
||||||
|
$body = array();
|
||||||
|
$body[] = "{$actor} answered a question that you are subscribed to.";
|
||||||
|
$body[] = null;
|
||||||
|
|
||||||
|
$content = $target->getContent();
|
||||||
|
if (strlen($content)) {
|
||||||
|
$body[] = $this->formatText($content);
|
||||||
|
$body[] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode("\n", $body);
|
||||||
|
}
|
||||||
|
}
|
53
src/applications/ponder/mail/PonderCommentMail.php
Normal file
53
src/applications/ponder/mail/PonderCommentMail.php
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
final class PonderCommentMail extends PonderMail {
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
PonderQuestion $question,
|
||||||
|
PonderComment $target,
|
||||||
|
PhabricatorUser $actor) {
|
||||||
|
|
||||||
|
$this->setQuestion($question);
|
||||||
|
$this->setTarget($target);
|
||||||
|
$this->setActorHandle($actor);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function renderVaryPrefix() {
|
||||||
|
return "[Commented]";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function renderBody() {
|
||||||
|
$question = $this->getQuestion();
|
||||||
|
$target = $this->getTarget();
|
||||||
|
$actor = $this->getActorName();
|
||||||
|
$name = $question->getTitle();
|
||||||
|
|
||||||
|
$body = array();
|
||||||
|
$body[] = "{$actor} commented on a question that you are subscribed to.";
|
||||||
|
$body[] = null;
|
||||||
|
|
||||||
|
$content = $target->getContent();
|
||||||
|
if (strlen($content)) {
|
||||||
|
$body[] = $this->formatText($content);
|
||||||
|
$body[] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode("\n", $body);
|
||||||
|
}
|
||||||
|
}
|
144
src/applications/ponder/mail/PonderMail.php
Normal file
144
src/applications/ponder/mail/PonderMail.php
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abstract class PonderMail {
|
||||||
|
|
||||||
|
protected $to = array();
|
||||||
|
protected $actorHandle;
|
||||||
|
protected $question;
|
||||||
|
protected $target;
|
||||||
|
|
||||||
|
protected $isFirstMailAboutQuestion;
|
||||||
|
|
||||||
|
// protected $replyHandler;
|
||||||
|
|
||||||
|
protected $parentMessageID;
|
||||||
|
|
||||||
|
protected function renderSubject() {
|
||||||
|
$question = $this->getQuestion();
|
||||||
|
$title = $question->getTitle();
|
||||||
|
$id = $question->getID();
|
||||||
|
return "Q{$id}: {$title}";
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected function renderVaryPrefix();
|
||||||
|
abstract protected function renderBody();
|
||||||
|
|
||||||
|
public function setActorHandle($actor_handle) {
|
||||||
|
$this->actorHandle = $actor_handle;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getActorHandle() {
|
||||||
|
return $this->actorHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getActorName() {
|
||||||
|
return $this->actorHandle->getRealName();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getSubjectPrefix() {
|
||||||
|
return "[Ponder]";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setToPHIDs(array $to) {
|
||||||
|
$this->to = $to;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getToPHIDs() {
|
||||||
|
return $this->to;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setQuestion($question) {
|
||||||
|
$this->question = $question;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQuestion() {
|
||||||
|
return $this->question;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTarget($target) {
|
||||||
|
$this->target = $target;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTarget() {
|
||||||
|
return $this->target;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getThreadID() {
|
||||||
|
$phid = $this->getQuestion()->getPHID();
|
||||||
|
return "ponder-ques-{$phid}";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getThreadTopic() {
|
||||||
|
$id = $this->getQuestion()->getID();
|
||||||
|
$title = $this->getQuestion()->getTitle();
|
||||||
|
return "Q{$id}: {$title}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send() {
|
||||||
|
$email_to = array_filter(array_unique($this->to));
|
||||||
|
$question = $this->getQuestion();
|
||||||
|
$target = $this->getTarget();
|
||||||
|
|
||||||
|
$uri = PhabricatorEnv::getURI('/Q'. $question->getID());
|
||||||
|
$thread_id = $this->getThreadID();
|
||||||
|
|
||||||
|
$handles = id(new PhabricatorObjectHandleData($email_to))
|
||||||
|
->loadHandles();
|
||||||
|
|
||||||
|
$reply_handler = new PonderReplyHandler();
|
||||||
|
|
||||||
|
$body = new PhabricatorMetaMTAMailBody();
|
||||||
|
$body->addRawSection($this->renderBody());
|
||||||
|
$body->addTextSection(pht('QUESTION DETAIL'), $uri);
|
||||||
|
|
||||||
|
$template = id(new PhabricatorMetaMTAMail())
|
||||||
|
->setSubject($this->getThreadTopic())
|
||||||
|
->setSubjectPrefix($this->getSubjectPrefix())
|
||||||
|
->setVarySubjectPrefix($this->renderVaryPrefix())
|
||||||
|
->setFrom($target->getAuthorPHID())
|
||||||
|
->setParentMessageID($this->parentMessageID)
|
||||||
|
->addHeader('Thread-Topic', $this->getThreadTopic())
|
||||||
|
->setThreadID($this->getThreadID(), false)
|
||||||
|
->setRelatedPHID($question->getPHID())
|
||||||
|
->setIsBulk(true)
|
||||||
|
->setBody($body->render());
|
||||||
|
|
||||||
|
$mails = $reply_handler->multiplexMail(
|
||||||
|
$template,
|
||||||
|
array_select_keys($handles, $email_to),
|
||||||
|
array());
|
||||||
|
|
||||||
|
foreach ($mails as $mail) {
|
||||||
|
$mail->saveAndSend();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function formatText($text) {
|
||||||
|
$text = explode("\n", rtrim($text));
|
||||||
|
foreach ($text as &$line) {
|
||||||
|
$line = rtrim(' '.$line);
|
||||||
|
}
|
||||||
|
unset($line);
|
||||||
|
return implode("\n", $text);
|
||||||
|
}
|
||||||
|
}
|
64
src/applications/ponder/mail/PonderMentionMail.php
Normal file
64
src/applications/ponder/mail/PonderMentionMail.php
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
final class PonderMentionMail extends PonderMail {
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
PonderQuestion $question,
|
||||||
|
$target,
|
||||||
|
PhabricatorUser $actor) {
|
||||||
|
|
||||||
|
$this->setQuestion($question);
|
||||||
|
$this->setTarget($target);
|
||||||
|
$this->setActorHandle($actor);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function renderVaryPrefix() {
|
||||||
|
return "[Mentioned]";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function renderBody() {
|
||||||
|
$question = $this->getQuestion();
|
||||||
|
$target = $this->getTarget();
|
||||||
|
$actor = $this->getActorName();
|
||||||
|
$name = $question->getTitle();
|
||||||
|
|
||||||
|
$targetkind = "somewhere";
|
||||||
|
if ($target instanceof PonderQuestion) {
|
||||||
|
$targetkind = "in a question";
|
||||||
|
}
|
||||||
|
else if ($target instanceof PonderAnswer) {
|
||||||
|
$targetkind = "in an answer";
|
||||||
|
}
|
||||||
|
else if ($target instanceof PonderComment) {
|
||||||
|
$targetkind = "in a comment";
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = array();
|
||||||
|
$body[] = "{$actor} mentioned you {$targetkind}.";
|
||||||
|
$body[] = null;
|
||||||
|
|
||||||
|
$content = $target->getContent();
|
||||||
|
if (strlen($content)) {
|
||||||
|
$body[] = $this->formatText($content);
|
||||||
|
$body[] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode("\n", $body);
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
final class PonderQuestion extends PonderDAO
|
final class PonderQuestion extends PonderDAO
|
||||||
implements PhabricatorMarkupInterface, PonderVotableInterface {
|
implements
|
||||||
|
PhabricatorMarkupInterface,
|
||||||
|
PonderVotableInterface,
|
||||||
|
PhabricatorSubscribableInterface {
|
||||||
|
|
||||||
const MARKUP_FIELD_CONTENT = 'markup:content';
|
const MARKUP_FIELD_CONTENT = 'markup:content';
|
||||||
|
|
||||||
|
@ -168,4 +171,8 @@ final class PonderQuestion extends PonderDAO
|
||||||
public function getVotablePHID() {
|
public function getVotablePHID() {
|
||||||
return $this->getPHID();
|
return $this->getPHID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isAutomaticallySubscribed($phid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue