Page MenuHomestyx hydra

No OneTemporary

diff --git a/resources/sql/autopatches/20150806.ponder.status.1.sql b/resources/sql/autopatches/20150806.ponder.status.1.sql
new file mode 100644
index 0000000000..b0307ca2dc
--- /dev/null
+++ b/resources/sql/autopatches/20150806.ponder.status.1.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_ponder.ponder_question
+ MODIFY status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT};
diff --git a/resources/sql/autopatches/20150806.ponder.status.2.sql b/resources/sql/autopatches/20150806.ponder.status.2.sql
new file mode 100644
index 0000000000..89371cc505
--- /dev/null
+++ b/resources/sql/autopatches/20150806.ponder.status.2.sql
@@ -0,0 +1,2 @@
+UPDATE {$NAMESPACE}_ponder.ponder_question
+ SET status = 'open' WHERE status = 0;
diff --git a/resources/sql/autopatches/20150806.ponder.status.3.sql b/resources/sql/autopatches/20150806.ponder.status.3.sql
new file mode 100644
index 0000000000..3a8f5c719d
--- /dev/null
+++ b/resources/sql/autopatches/20150806.ponder.status.3.sql
@@ -0,0 +1,2 @@
+UPDATE {$NAMESPACE}_ponder.ponder_question
+ SET status = 'resolved' WHERE status = 1;
diff --git a/src/applications/ponder/application/PhabricatorPonderApplication.php b/src/applications/ponder/application/PhabricatorPonderApplication.php
index 11e90dc274..6ba5e3ca4f 100644
--- a/src/applications/ponder/application/PhabricatorPonderApplication.php
+++ b/src/applications/ponder/application/PhabricatorPonderApplication.php
@@ -1,113 +1,113 @@
<?php
final class PhabricatorPonderApplication extends PhabricatorApplication {
public function getBaseURI() {
return '/ponder/';
}
public function getName() {
return pht('Ponder');
}
public function getShortDescription() {
return pht('Questions and Answers');
}
public function getFontIcon() {
return 'fa-university';
}
public function getFactObjectsForAnalysis() {
return array(
new PonderQuestion(),
);
}
public function getTitleGlyph() {
return "\xE2\x97\xB3";
}
public function loadStatus(PhabricatorUser $user) {
// Replace with "x new unanswered questions" or some such
// make sure to use `self::formatStatusCount` and friends...!
$status = array();
return $status;
}
public function getRemarkupRules() {
return array(
new PonderRemarkupRule(),
);
}
public function isPrototype() {
return true;
}
public function getRoutes() {
return array(
'/Q(?P<id>[1-9]\d*)'
=> 'PonderQuestionViewController',
'/ponder/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PonderQuestionListController',
'answer/add/'
=> 'PonderAnswerSaveController',
'answer/edit/(?P<id>\d+)/'
=> 'PonderAnswerEditController',
'answer/comment/(?P<id>\d+)/'
=> 'PonderAnswerCommentController',
'answer/history/(?P<id>\d+)/'
=> 'PonderAnswerHistoryController',
'question/edit/(?:(?P<id>\d+)/)?'
=> 'PonderQuestionEditController',
'question/create/'
=> 'PonderQuestionEditController',
'question/comment/(?P<id>\d+)/'
=> 'PonderQuestionCommentController',
'question/history/(?P<id>\d+)/'
=> 'PonderQuestionHistoryController',
'preview/'
=> 'PhabricatorMarkupPreviewController',
- 'question/(?P<status>open|close)/(?P<id>[1-9]\d*)/'
+ 'question/status/(?P<id>[1-9]\d*)/'
=> 'PonderQuestionStatusController',
'vote/' => 'PonderVoteSaveController',
),
);
}
public function getMailCommandObjects() {
return array(
'question' => array(
'name' => pht('Email Commands: Questions'),
'header' => pht('Interacting with Ponder Questions'),
'object' => new PonderQuestion(),
'summary' => pht(
'This page documents the commands you can use to interact with '.
'questions in Ponder.'),
),
);
}
protected function getCustomCapabilities() {
return array(
PonderQuestionDefaultViewCapability::CAPABILITY => array(
'template' => PonderQuestionPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
),
PonderQuestionDefaultEditCapability::CAPABILITY => array(
'template' => PonderQuestionPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
),
);
}
public function getApplicationSearchDocumentTypes() {
return array(
PonderQuestionPHIDType::TYPECONST,
);
}
}
diff --git a/src/applications/ponder/constants/PonderQuestionStatus.php b/src/applications/ponder/constants/PonderQuestionStatus.php
index 2d26b9cb66..6c612ec4ae 100644
--- a/src/applications/ponder/constants/PonderQuestionStatus.php
+++ b/src/applications/ponder/constants/PonderQuestionStatus.php
@@ -1,41 +1,80 @@
<?php
final class PonderQuestionStatus extends PonderConstants {
- const STATUS_OPEN = 0;
- const STATUS_CLOSED = 1;
+ const STATUS_OPEN = 'open';
+ const STATUS_CLOSED_RESOLVED = 'resolved';
+ const STATUS_CLOSED_OBSOLETE = 'obsolete';
+ const STATUS_CLOSED_DUPLICATE = 'duplicate';
public static function getQuestionStatusMap() {
return array(
- self::STATUS_OPEN => pht('Open'),
- self::STATUS_CLOSED => pht('Closed'),
+ self::STATUS_OPEN => pht('Open'),
+ self::STATUS_CLOSED_RESOLVED => pht('Closed, Resolved'),
+ self::STATUS_CLOSED_OBSOLETE => pht('Closed, Obsolete'),
+ self::STATUS_CLOSED_DUPLICATE => pht('Closed, Duplicate'),
);
}
public static function getQuestionStatusFullName($status) {
$map = array(
- self::STATUS_OPEN => pht('Open'),
- self::STATUS_CLOSED => pht('Closed by author'),
+ self::STATUS_OPEN => pht('Open'),
+ self::STATUS_CLOSED_RESOLVED => pht('Closed, Resolved'),
+ self::STATUS_CLOSED_OBSOLETE => pht('Closed, Obsolete'),
+ self::STATUS_CLOSED_DUPLICATE => pht('Closed, Duplicate'),
+ );
+ return idx($map, $status, pht('Unknown'));
+ }
+
+ public static function getQuestionStatusDescription($status) {
+ $map = array(
+ self::STATUS_OPEN =>
+ pht('This question is open for answers.'),
+ self::STATUS_CLOSED_RESOLVED =>
+ pht('This question has been resolved.'),
+ self::STATUS_CLOSED_OBSOLETE =>
+ pht('This question is no longer valid or out of date.'),
+ self::STATUS_CLOSED_DUPLICATE =>
+ pht('This question is a duplicate of another question.'),
);
return idx($map, $status, pht('Unknown'));
}
public static function getQuestionStatusTagColor($status) {
$map = array(
self::STATUS_OPEN => PHUITagView::COLOR_BLUE,
- self::STATUS_CLOSED => PHUITagView::COLOR_BLACK,
+ self::STATUS_CLOSED_RESOLVED => PHUITagView::COLOR_BLACK,
+ self::STATUS_CLOSED_OBSOLETE => PHUITagView::COLOR_BLACK,
+ self::STATUS_CLOSED_DUPLICATE => PHUITagView::COLOR_BLACK,
);
return idx($map, $status);
}
public static function getQuestionStatusIcon($status) {
$map = array(
self::STATUS_OPEN => 'fa-question-circle',
- self::STATUS_CLOSED => 'fa-check-square-o',
+ self::STATUS_CLOSED_RESOLVED => 'fa-check',
+ self::STATUS_CLOSED_OBSOLETE => 'fa-ban',
+ self::STATUS_CLOSED_DUPLICATE => 'fa-clone',
);
return idx($map, $status);
}
+ public static function getQuestionStatusOpenMap() {
+ return array(
+ self::STATUS_OPEN,
+ );
+ }
+
+ public static function getQuestionStatusClosedMap() {
+ return array(
+ self::STATUS_CLOSED_RESOLVED,
+ self::STATUS_CLOSED_OBSOLETE,
+ self::STATUS_CLOSED_DUPLICATE,
+ );
+ }
+
+
}
diff --git a/src/applications/ponder/controller/PonderQuestionEditController.php b/src/applications/ponder/controller/PonderQuestionEditController.php
index cf8b5f9de1..d1a8d48f59 100644
--- a/src/applications/ponder/controller/PonderQuestionEditController.php
+++ b/src/applications/ponder/controller/PonderQuestionEditController.php
@@ -1,178 +1,193 @@
<?php
final class PonderQuestionEditController extends PonderController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
if ($id) {
$question = id(new PonderQuestionQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$question) {
return new Aphront404Response();
}
$v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
$question->getPHID(),
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
$v_projects = array_reverse($v_projects);
} else {
$question = PonderQuestion::initializeNewQuestion($viewer);
$v_projects = array();
}
$v_title = $question->getTitle();
$v_content = $question->getContent();
$v_view = $question->getViewPolicy();
$v_edit = $question->getEditPolicy();
$v_space = $question->getSpacePHID();
+ $v_status = $question->getStatus();
+
$errors = array();
$e_title = true;
if ($request->isFormPost()) {
$v_title = $request->getStr('title');
$v_content = $request->getStr('content');
$v_projects = $request->getArr('projects');
$v_view = $request->getStr('viewPolicy');
$v_edit = $request->getStr('editPolicy');
$v_space = $request->getStr('spacePHID');
+ $v_status = $request->getStr('status');
$len = phutil_utf8_strlen($v_title);
if ($len < 1) {
$errors[] = pht('Title must not be empty.');
$e_title = pht('Required');
} else if ($len > 255) {
$errors[] = pht('Title is too long.');
$e_title = pht('Too Long');
}
if (!$errors) {
$template = id(new PonderQuestionTransaction());
$xactions = array();
$xactions[] = id(clone $template)
->setTransactionType(PonderQuestionTransaction::TYPE_TITLE)
->setNewValue($v_title);
$xactions[] = id(clone $template)
->setTransactionType(PonderQuestionTransaction::TYPE_CONTENT)
->setNewValue($v_content);
+ $xactions[] = id(clone $template)
+ ->setTransactionType(PonderQuestionTransaction::TYPE_STATUS)
+ ->setNewValue($v_status);
+
$xactions[] = id(clone $template)
->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
->setNewValue($v_view);
$xactions[] = id(clone $template)
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
->setNewValue($v_edit);
$xactions[] = id(clone $template)
->setTransactionType(PhabricatorTransactions::TYPE_SPACE)
->setNewValue($v_space);
$proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
$xactions[] = id(new PonderQuestionTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $proj_edge_type)
->setNewValue(array('=' => array_fuse($v_projects)));
$editor = id(new PonderQuestionEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true);
$editor->applyTransactions($question, $xactions);
return id(new AphrontRedirectResponse())
->setURI('/Q'.$question->getID());
}
}
$policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->setObject($question)
->execute();
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Question'))
->setName('title')
->setValue($v_title)
->setError($e_title))
->appendChild(
id(new PhabricatorRemarkupControl())
->setUser($viewer)
->setName('content')
->setID('content')
->setValue($v_content)
->setLabel(pht('Description'))
->setUser($viewer))
->appendControl(
id(new AphrontFormPolicyControl())
->setName('viewPolicy')
->setPolicyObject($question)
->setSpacePHID($v_space)
->setPolicies($policies)
->setValue($v_view)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW))
->appendControl(
id(new AphrontFormPolicyControl())
->setName('editPolicy')
->setPolicyObject($question)
->setPolicies($policies)
->setValue($v_edit)
- ->setCapability(PhabricatorPolicyCapability::CAN_EDIT));
+ ->setCapability(PhabricatorPolicyCapability::CAN_EDIT))
+ ->appendChild(
+ id(new AphrontFormSelectControl())
+ ->setLabel(pht('Status'))
+ ->setName('status')
+ ->setValue($v_status)
+ ->setOptions(PonderQuestionStatus::getQuestionStatusMap()));
$form->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Projects'))
->setName('projects')
->setValue($v_projects)
->setDatasource(new PhabricatorProjectDatasource()));
$form->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($this->getApplicationURI())
->setValue(pht('Ask Away!')));
$preview = id(new PHUIRemarkupPreviewPanel())
->setHeader(pht('Question Preview'))
->setControlID('content')
->setPreviewURI($this->getApplicationURI('preview/'));
- $form_box = id(new PHUIObjectBoxView())
- ->setHeaderText(pht('Ask New Question'))
- ->setFormErrors($errors)
- ->setForm($form);
-
$crumbs = $this->buildApplicationCrumbs();
$id = $question->getID();
if ($id) {
$crumbs->addTextCrumb("Q{$id}", "/Q{$id}");
$crumbs->addTextCrumb(pht('Edit'));
+ $title = pht('Edit Question');
} else {
$crumbs->addTextCrumb(pht('Ask Question'));
+ $title = pht('Ask New Question');
}
+ $form_box = id(new PHUIObjectBoxView())
+ ->setHeaderText($title)
+ ->setFormErrors($errors)
+ ->setForm($form);
+
return $this->buildApplicationPage(
array(
$crumbs,
$form_box,
$preview,
),
array(
- 'title' => pht('Ask New Question'),
+ 'title' => $title,
));
}
}
diff --git a/src/applications/ponder/controller/PonderQuestionStatusController.php b/src/applications/ponder/controller/PonderQuestionStatusController.php
index db2d971c92..a4671d5915 100644
--- a/src/applications/ponder/controller/PonderQuestionStatusController.php
+++ b/src/applications/ponder/controller/PonderQuestionStatusController.php
@@ -1,49 +1,65 @@
<?php
final class PonderQuestionStatusController
extends PonderController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
- $status = $request->getURIData('status');
$question = id(new PonderQuestionQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$question) {
return new Aphront404Response();
}
- switch ($status) {
- case 'open':
- $status = PonderQuestionStatus::STATUS_OPEN;
- break;
- case 'close':
- $status = PonderQuestionStatus::STATUS_CLOSED;
- break;
- default:
- return new Aphront400Response();
+ $view_uri = '/Q'.$question->getID();
+ $v_status = $question->getStatus();
+
+ if ($request->isFormPost()) {
+ $v_status = $request->getStr('status');
+
+ $xactions = array();
+ $xactions[] = id(new PonderQuestionTransaction())
+ ->setTransactionType(PonderQuestionTransaction::TYPE_STATUS)
+ ->setNewValue($v_status);
+
+ $editor = id(new PonderQuestionEditor())
+ ->setActor($viewer)
+ ->setContentSourceFromRequest($request);
+
+ $editor->applyTransactions($question, $xactions);
+
+ return id(new AphrontRedirectResponse())->setURI($view_uri);
}
- $xactions = array();
- $xactions[] = id(new PonderQuestionTransaction())
- ->setTransactionType(PonderQuestionTransaction::TYPE_STATUS)
- ->setNewValue($status);
+ $radio = id(new AphrontFormRadioButtonControl())
+ ->setLabel(pht('Status'))
+ ->setName('status')
+ ->setValue($v_status);
+
+ foreach (PonderQuestionStatus::getQuestionStatusMap() as $value => $name) {
+ $description = PonderQuestionStatus::getQuestionStatusDescription($value);
+ $radio->addButton($value, $name, $description);
+ }
- $editor = id(new PonderQuestionEditor())
- ->setActor($viewer)
- ->setContentSourceFromRequest($request);
+ $form = id(new AphrontFormView())
+ ->setUser($viewer)
+ ->appendChild($radio);
- $editor->applyTransactions($question, $xactions);
+ return $this->newDialog()
+ ->setTitle(pht('Change Question Status'))
+ ->appendChild($form->buildLayoutView())
+ ->addSubmitButton(pht('Submit'))
+ ->addCancelButton($view_uri);
- return id(new AphrontRedirectResponse())->setURI('/Q'.$question->getID());
}
}
diff --git a/src/applications/ponder/controller/PonderQuestionViewController.php b/src/applications/ponder/controller/PonderQuestionViewController.php
index 51f165b408..c5349c5eab 100644
--- a/src/applications/ponder/controller/PonderQuestionViewController.php
+++ b/src/applications/ponder/controller/PonderQuestionViewController.php
@@ -1,403 +1,404 @@
<?php
final class PonderQuestionViewController extends PonderController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$question = id(new PonderQuestionQuery())
->setViewer($viewer)
->withIDs(array($id))
->needAnswers(true)
->needViewerVotes(true)
->executeOne();
if (!$question) {
return new Aphront404Response();
}
$question->attachVotes($viewer->getPHID());
$question_xactions = $this->buildQuestionTransactions($question);
$answers = $this->buildAnswers($question->getAnswers());
$authors = mpull($question->getAnswers(), null, 'getAuthorPHID');
if (isset($authors[$viewer->getPHID()])) {
$answer_add_panel = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->appendChild(
pht(
'You have already answered this question. You can not answer '.
'twice, but you can edit your existing answer.'));
} else {
$answer_add_panel = new PonderAddAnswerView();
$answer_add_panel
->setQuestion($question)
->setUser($viewer)
->setActionURI('/ponder/answer/add/');
}
$header = new PHUIHeaderView();
$header->setHeader($question->getTitle());
$header->setUser($viewer);
$header->setPolicyObject($question);
if ($question->getStatus() == PonderQuestionStatus::STATUS_OPEN) {
$header->setStatus('fa-square-o', 'bluegrey', pht('Open'));
} else {
- $header->setStatus('fa-check-square-o', 'dark', pht('Closed'));
+ $text = PonderQuestionStatus::getQuestionStatusFullName(
+ $question->getStatus());
+ $icon = PonderQuestionStatus::getQuestionStatusIcon(
+ $question->getStatus());
+ $header->setStatus($icon, 'dark', $text);
}
$actions = $this->buildActionListView($question);
$properties = $this->buildPropertyListView($question, $actions);
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$crumbs = $this->buildApplicationCrumbs($this->buildSideNavView());
$crumbs->addTextCrumb('Q'.$id, '/Q'.$id);
$ponder_view = phutil_tag(
'div',
array(
'class' => 'ponder-question-view',
),
array(
$crumbs,
$object_box,
$question_xactions,
$answers,
$answer_add_panel,
));
return $this->buildApplicationPage(
array(
$ponder_view,
),
array(
'title' => 'Q'.$question->getID().' '.$question->getTitle(),
'pageObjects' => array_merge(
array($question->getPHID()),
mpull($question->getAnswers(), 'getPHID')),
));
}
private function buildActionListView(PonderQuestion $question) {
$viewer = $this->getViewer();
$request = $this->getRequest();
$id = $question->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$question,
PhabricatorPolicyCapability::CAN_EDIT);
$view = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($question)
->setObjectURI($request->getRequestURI());
$view->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Question'))
->setHref($this->getApplicationURI("/question/edit/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
if ($question->getStatus() == PonderQuestionStatus::STATUS_OPEN) {
$name = pht('Close Question');
$icon = 'fa-check-square-o';
- $href = 'close';
} else {
$name = pht('Reopen Question');
$icon = 'fa-square-o';
- $href = 'open';
}
$view->addAction(
id(new PhabricatorActionView())
->setName($name)
->setIcon($icon)
- ->setRenderAsForm($can_edit)
- ->setWorkflow(!$can_edit)
+ ->setWorkflow(true)
->setDisabled(!$can_edit)
- ->setHref($this->getApplicationURI("/question/{$href}/{$id}/")));
+ ->setHref($this->getApplicationURI("/question/status/{$id}/")));
$view->addAction(
id(new PhabricatorActionView())
->setIcon('fa-list')
->setName(pht('View History'))
->setHref($this->getApplicationURI("/question/history/{$id}/")));
return $view;
}
private function buildPropertyListView(
PonderQuestion $question,
PhabricatorActionListView $actions) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($question)
->setActionList($actions);
$view->addProperty(
pht('Author'),
$viewer->renderHandle($question->getAuthorPHID()));
$view->addProperty(
pht('Created'),
phabricator_datetime($question->getDateCreated(), $viewer));
$view->invokeWillRenderEvent();
$votable = id(new PonderVotableView())
->setPHID($question->getPHID())
->setURI($this->getApplicationURI('vote/'))
->setCount($question->getVoteCount())
->setVote($question->getUserVote());
$view->addSectionHeader(pht('Question'));
$view->addTextContent(
array(
$votable,
phutil_tag(
'div',
array(
'class' => 'phabricator-remarkup',
),
PhabricatorMarkupEngine::renderOneObject(
$question,
$question->getMarkupField(),
$viewer)),
));
return $view;
}
private function buildQuestionTransactions(PonderQuestion $question) {
$viewer = $this->getViewer();
$id = $question->getID();
$timeline = $this->buildTransactionTimeline(
$question,
id(new PonderQuestionTransactionQuery())
->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT)));
$xactions = $timeline->getTransactions();
$add_comment = id(new PhabricatorApplicationTransactionCommentView())
->setUser($viewer)
->setObjectPHID($question->getPHID())
->setShowPreview(false)
->setHeaderText(pht('Question Comment'))
->setAction($this->getApplicationURI("/question/comment/{$id}/"))
->setSubmitButtonName(pht('Comment'));
return $this->wrapComments(
count($xactions),
array(
$timeline,
$add_comment,
));
}
/**
* This is fairly non-standard; building N timelines at once (N = number of
* answers) is tricky business.
*
* TODO - re-factor this to ajax in one answer panel at a time in a more
* standard fashion. This is necessary to scale this application.
*/
private function buildAnswers(array $answers) {
$viewer = $this->getViewer();
$out = array();
$xactions = id(new PonderAnswerTransactionQuery())
->setViewer($viewer)
->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT))
->withObjectPHIDs(mpull($answers, 'getPHID'))
->execute();
$engine = id(new PhabricatorMarkupEngine())
->setViewer($viewer);
foreach ($xactions as $xaction) {
if ($xaction->getComment()) {
$engine->addObject(
$xaction->getComment(),
PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
}
}
$engine->process();
$xaction_groups = mgroup($xactions, 'getObjectPHID');
foreach ($answers as $answer) {
$author_phid = $answer->getAuthorPHID();
$xactions = idx($xaction_groups, $answer->getPHID(), array());
$id = $answer->getID();
$out[] = phutil_tag('br');
$out[] = phutil_tag('br');
$out[] = id(new PhabricatorAnchorView())
->setAnchorName("A$id");
$header = id(new PHUIHeaderView())
->setHeader($viewer->renderHandle($author_phid));
$actions = $this->buildAnswerActions($answer);
$properties = $this->buildAnswerProperties($answer, $actions);
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$out[] = $object_box;
$details = array();
$details[] = id(new PhabricatorApplicationTransactionView())
->setUser($viewer)
->setObjectPHID($answer->getPHID())
->setTransactions($xactions)
->setMarkupEngine($engine);
$form = id(new PhabricatorApplicationTransactionCommentView())
->setUser($viewer)
->setObjectPHID($answer->getPHID())
->setShowPreview(false)
->setHeaderText(pht('Answer Comment'))
->setAction($this->getApplicationURI("/answer/comment/{$id}/"))
->setSubmitButtonName(pht('Comment'));
$details[] = $form;
$out[] = $this->wrapComments(
count($xactions),
$details);
}
$out[] = phutil_tag('br');
$out[] = phutil_tag('br');
return $out;
}
private function buildAnswerActions(PonderAnswer $answer) {
$viewer = $this->getViewer();
$request = $this->getRequest();
$id = $answer->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$answer,
PhabricatorPolicyCapability::CAN_EDIT);
$view = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($answer)
->setObjectURI($request->getRequestURI());
$view->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Answer'))
->setHref($this->getApplicationURI("/answer/edit/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$view->addAction(
id(new PhabricatorActionView())
->setIcon('fa-list')
->setName(pht('View History'))
->setHref($this->getApplicationURI("/answer/history/{$id}/")));
return $view;
}
private function buildAnswerProperties(
PonderAnswer $answer,
PhabricatorActionListView $actions) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($answer)
->setActionList($actions);
$view->addProperty(
pht('Created'),
phabricator_datetime($answer->getDateCreated(), $viewer));
$view->invokeWillRenderEvent();
$votable = id(new PonderVotableView())
->setPHID($answer->getPHID())
->setURI($this->getApplicationURI('vote/'))
->setCount($answer->getVoteCount())
->setVote($answer->getUserVote());
$view->addSectionHeader(pht('Answer'));
$view->addTextContent(
array(
$votable,
phutil_tag(
'div',
array(
'class' => 'phabricator-remarkup',
),
PhabricatorMarkupEngine::renderOneObject(
$answer,
$answer->getMarkupField(),
$viewer)),
));
return $view;
}
private function wrapComments($n, $stuff) {
if ($n == 0) {
$text = pht('Add a Comment');
} else {
$text = pht('Show %s Comments', new PhutilNumber($n));
}
$show_id = celerity_generate_unique_node_id();
$hide_id = celerity_generate_unique_node_id();
Javelin::initBehavior('phabricator-reveal-content');
require_celerity_resource('ponder-view-css');
$show = phutil_tag(
'div',
array(
'id' => $show_id,
'class' => 'ponder-show-comments',
),
javelin_tag(
'a',
array(
'href' => '#',
'sigil' => 'reveal-content',
'meta' => array(
'showIDs' => array($hide_id),
'hideIDs' => array($show_id),
),
),
$text));
$hide = phutil_tag(
'div',
array(
'class' => 'ponder-comments-view',
'id' => $hide_id,
'style' => 'display: none',
),
$stuff);
return array($show, $hide);
}
}
diff --git a/src/applications/ponder/query/PonderQuestionQuery.php b/src/applications/ponder/query/PonderQuestionQuery.php
index 74d8230327..1ed3344382 100644
--- a/src/applications/ponder/query/PonderQuestionQuery.php
+++ b/src/applications/ponder/query/PonderQuestionQuery.php
@@ -1,203 +1,184 @@
<?php
final class PonderQuestionQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
+ private $status;
private $authorPHIDs;
private $answererPHIDs;
private $needProjectPHIDs;
- private $status = 'status-any';
-
- const STATUS_ANY = 'status-any';
- const STATUS_OPEN = 'status-open';
- const STATUS_CLOSED = 'status-closed';
-
private $needAnswers;
private $needViewerVotes;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withAuthorPHIDs(array $phids) {
$this->authorPHIDs = $phids;
return $this;
}
- public function withStatus($status) {
+ public function withStatuses($status) {
$this->status = $status;
return $this;
}
public function withAnswererPHIDs(array $phids) {
$this->answererPHIDs = $phids;
return $this;
}
public function needAnswers($need_answers) {
$this->needAnswers = $need_answers;
return $this;
}
public function needViewerVotes($need_viewer_votes) {
$this->needViewerVotes = $need_viewer_votes;
return $this;
}
public function needProjectPHIDs($need_projects) {
$this->needProjectPHIDs = $need_projects;
return $this;
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'q.id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'q.phid IN (%Ls)',
$this->phids);
}
if ($this->authorPHIDs !== null) {
$where[] = qsprintf(
$conn,
'q.authorPHID IN (%Ls)',
$this->authorPHIDs);
}
if ($this->status !== null) {
- switch ($this->status) {
- case self::STATUS_ANY:
- break;
- case self::STATUS_OPEN:
- $where[] = qsprintf(
- $conn,
- 'q.status = %d',
- PonderQuestionStatus::STATUS_OPEN);
- break;
- case self::STATUS_CLOSED:
- $where[] = qsprintf(
- $conn,
- 'q.status = %d',
- PonderQuestionStatus::STATUS_CLOSED);
- break;
- default:
- throw new Exception(pht("Unknown status query '%s'!", $this->status));
- }
+ $where[] = qsprintf(
+ $conn,
+ 'q.status IN (%Ls)',
+ $this->status);
}
return $where;
}
public function newResultObject() {
return new PonderQuestion();
}
protected function loadPage() {
return $this->loadStandardPage(new PonderQuestion());
}
protected function willFilterPage(array $questions) {
$phids = mpull($questions, 'getPHID');
if ($this->needAnswers) {
$aquery = id(new PonderAnswerQuery())
->setViewer($this->getViewer())
->setOrderVector(array('-id'))
->withQuestionIDs(mpull($questions, 'getID'));
if ($this->needViewerVotes) {
$aquery->needViewerVotes($this->needViewerVotes);
}
$answers = $aquery->execute();
$answers = mgroup($answers, 'getQuestionID');
foreach ($questions as $question) {
$question_answers = idx($answers, $question->getID(), array());
$question->attachAnswers(mpull($question_answers, null, 'getPHID'));
}
}
if ($this->needViewerVotes) {
$viewer_phid = $this->getViewer()->getPHID();
$etype = PonderQuestionHasVotingUserEdgeType::EDGECONST;
$edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($phids)
->withDestinationPHIDs(array($viewer_phid))
->withEdgeTypes(array($etype))
->needEdgeData(true)
->execute();
foreach ($questions as $question) {
$user_edge = idx(
$edges[$question->getPHID()][$etype],
$viewer_phid,
array());
$question->attachUserVote($viewer_phid, idx($user_edge, 'data', 0));
}
}
if ($this->needProjectPHIDs) {
$edge_query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($phids)
->withEdgeTypes(
array(
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
));
$edge_query->execute();
foreach ($questions as $question) {
$project_phids = $edge_query->getDestinationPHIDs(
array($question->getPHID()));
$question->attachProjectPHIDs($project_phids);
}
}
return $questions;
}
private function buildJoinsClause(AphrontDatabaseConnection $conn_r) {
$joins = array();
if ($this->answererPHIDs) {
$answer_table = new PonderAnswer();
$joins[] = qsprintf(
$conn_r,
'JOIN %T a ON a.questionID = q.id AND a.authorPHID IN (%Ls)',
$answer_table->getTableName(),
$this->answererPHIDs);
}
return implode(' ', $joins);
}
protected function getPrimaryTableAlias() {
return 'q';
}
public function getQueryApplicationClass() {
return 'PhabricatorPonderApplication';
}
}
diff --git a/src/applications/ponder/query/PonderQuestionSearchEngine.php b/src/applications/ponder/query/PonderQuestionSearchEngine.php
index 1b475c7b58..b444ff8be9 100644
--- a/src/applications/ponder/query/PonderQuestionSearchEngine.php
+++ b/src/applications/ponder/query/PonderQuestionSearchEngine.php
@@ -1,177 +1,174 @@
<?php
final class PonderQuestionSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Ponder Questions');
}
public function getApplicationClassName() {
return 'PhabricatorPonderApplication';
}
public function newQuery() {
return id(new PonderQuestionQuery())
->needProjectPHIDs(true);
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
if ($map['authorPHIDs']) {
$query->withAuthorPHIDs($map['authorPHIDs']);
}
if ($map['answerers']) {
$query->withAnswererPHIDs($map['answerers']);
}
- $status = $map['status'];
- if ($status != null) {
- switch ($status) {
- case 0:
- $query->withStatus(PonderQuestionQuery::STATUS_OPEN);
- break;
- case 1:
- $query->withStatus(PonderQuestionQuery::STATUS_CLOSED);
- break;
- }
+ if ($map['statuses']) {
+ $query->withStatuses($map['statuses']);
}
return $query;
}
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorUsersSearchField())
->setKey('authorPHIDs')
->setAliases(array('authors'))
->setLabel(pht('Authors')),
id(new PhabricatorUsersSearchField())
->setKey('answerers')
->setAliases(array('answerers'))
->setLabel(pht('Answered By')),
- id(new PhabricatorSearchSelectField())
+ id(new PhabricatorSearchCheckboxesField())
->setLabel(pht('Status'))
- ->setKey('status')
+ ->setKey('statuses')
->setOptions(PonderQuestionStatus::getQuestionStatusMap()),
);
}
protected function getURI($path) {
return '/ponder/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'open' => pht('Open Questions'),
+ 'resolved' => pht('Resolved Questions'),
'all' => pht('All Questions'),
);
if ($this->requireViewer()->isLoggedIn()) {
$names['authored'] = pht('Authored');
$names['answered'] = pht('Answered');
}
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
case 'open':
- return $query->setParameter('status', PonderQuestionQuery::STATUS_OPEN);
+ return $query->setParameter(
+ 'statuses', array(PonderQuestionStatus::STATUS_OPEN));
+ case 'resolved':
+ return $query->setParameter(
+ 'statuses', array(PonderQuestionStatus::STATUS_CLOSED_RESOLVED));
case 'authored':
return $query->setParameter(
'authorPHIDs',
array($this->requireViewer()->getPHID()));
case 'answered':
return $query->setParameter(
'answererPHIDs',
array($this->requireViewer()->getPHID()));
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function getRequiredHandlePHIDsForResultList(
array $questions,
PhabricatorSavedQuery $query) {
return mpull($questions, 'getAuthorPHID');
}
protected function renderResultList(
array $questions,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($questions, 'PonderQuestion');
$viewer = $this->requireViewer();
$proj_phids = array();
foreach ($questions as $question) {
foreach ($question->getProjectPHIDs() as $project_phid) {
$proj_phids[] = $project_phid;
}
}
$proj_handles = id(new PhabricatorHandleQuery())
->setViewer($viewer)
->withPHIDs($proj_phids)
->execute();
$view = id(new PHUIObjectItemListView())
->setUser($viewer);
foreach ($questions as $question) {
$color = PonderQuestionStatus::getQuestionStatusTagColor(
$question->getStatus());
$icon = PonderQuestionStatus::getQuestionStatusIcon(
$question->getStatus());
$full_status = PonderQuestionStatus::getQuestionStatusFullName(
$question->getStatus());
$item = new PHUIObjectItemView();
$item->setObjectName('Q'.$question->getID());
$item->setHeader($question->getTitle());
$item->setHref('/Q'.$question->getID());
$item->setObject($question);
$item->setStatusIcon($icon.' '.$color, $full_status);
$project_handles = array_select_keys(
$proj_handles,
$question->getProjectPHIDs());
$created_date = phabricator_date($question->getDateCreated(), $viewer);
$item->addIcon('none', $created_date);
$item->addByline(
pht(
'Asked by %s',
$handles[$question->getAuthorPHID()]->renderLink()));
$item->addAttribute(
pht('%d Answer(s)', $question->getAnswerCount()));
if ($project_handles) {
$item->addAttribute(
id(new PHUIHandleTagListView())
->setLimit(4)
->setSlim(true)
->setHandles($project_handles));
}
$view->addItem($item);
}
$result = new PhabricatorApplicationSearchResultView();
$result->setObjectList($view);
$result->setNoDataString(pht('No questions found.'));
return $result;
}
}
diff --git a/src/applications/ponder/storage/PonderQuestion.php b/src/applications/ponder/storage/PonderQuestion.php
index f2d81c4838..0e1ea0226b 100644
--- a/src/applications/ponder/storage/PonderQuestion.php
+++ b/src/applications/ponder/storage/PonderQuestion.php
@@ -1,343 +1,343 @@
<?php
final class PonderQuestion extends PonderDAO
implements
PhabricatorApplicationTransactionInterface,
PhabricatorMarkupInterface,
PonderVotableInterface,
PhabricatorSubscribableInterface,
PhabricatorFlaggableInterface,
PhabricatorPolicyInterface,
PhabricatorTokenReceiverInterface,
PhabricatorProjectInterface,
PhabricatorDestructibleInterface,
PhabricatorSpacesInterface {
const MARKUP_FIELD_CONTENT = 'markup:content';
protected $title;
protected $phid;
protected $authorPHID;
protected $status;
protected $content;
protected $contentSource;
protected $viewPolicy;
protected $editPolicy;
protected $spacePHID;
protected $voteCount;
protected $answerCount;
protected $heat;
protected $mailKey;
private $answers;
private $vote;
private $comments;
private $projectPHIDs = self::ATTACHABLE;
public static function initializeNewQuestion(PhabricatorUser $actor) {
$app = id(new PhabricatorApplicationQuery())
->setViewer($actor)
->withClasses(array('PhabricatorPonderApplication'))
->executeOne();
$view_policy = $app->getPolicy(
PonderQuestionDefaultViewCapability::CAPABILITY);
$edit_policy = $app->getPolicy(
PonderQuestionDefaultEditCapability::CAPABILITY);
return id(new PonderQuestion())
->setAuthorPHID($actor->getPHID())
->setViewPolicy($view_policy)
->setEditPolicy($edit_policy)
->setStatus(PonderQuestionStatus::STATUS_OPEN)
->setVoteCount(0)
->setAnswerCount(0)
->setHeat(0.0)
->setSpacePHID($actor->getDefaultSpacePHID());
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'title' => 'text255',
'voteCount' => 'sint32',
- 'status' => 'uint32',
+ 'status' => 'text32',
'content' => 'text',
'heat' => 'double',
'answerCount' => 'uint32',
'mailKey' => 'bytes20',
// T6203/NULLABILITY
// This should always exist.
'contentSource' => 'text?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_phid' => null,
'phid' => array(
'columns' => array('phid'),
'unique' => true,
),
'authorPHID' => array(
'columns' => array('authorPHID'),
),
'heat' => array(
'columns' => array('heat'),
),
'status' => array(
'columns' => array('status'),
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(PonderQuestionPHIDType::TYPECONST);
}
public function setContentSource(PhabricatorContentSource $content_source) {
$this->contentSource = $content_source->serialize();
return $this;
}
public function getContentSource() {
return PhabricatorContentSource::newFromSerialized($this->contentSource);
}
public function attachVotes($user_phid) {
$qa_phids = mpull($this->answers, 'getPHID') + array($this->getPHID());
$edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(array($user_phid))
->withDestinationPHIDs($qa_phids)
->withEdgeTypes(
array(
PonderVotingUserHasQuestionEdgeType::EDGECONST,
PonderVotingUserHasAnswerEdgeType::EDGECONST,
))
->needEdgeData(true)
->execute();
$question_edge =
$edges[$user_phid][PonderVotingUserHasQuestionEdgeType::EDGECONST];
$answer_edges =
$edges[$user_phid][PonderVotingUserHasAnswerEdgeType::EDGECONST];
$edges = null;
$this->setUserVote(idx($question_edge, $this->getPHID()));
foreach ($this->answers as $answer) {
$answer->setUserVote(idx($answer_edges, $answer->getPHID()));
}
}
public function setUserVote($vote) {
$this->vote = $vote['data'];
if (!$this->vote) {
$this->vote = PonderVote::VOTE_NONE;
}
return $this;
}
public function attachUserVote($user_phid, $vote) {
$this->vote = $vote;
return $this;
}
public function getUserVote() {
return $this->vote;
}
public function setComments($comments) {
$this->comments = $comments;
return $this;
}
public function getComments() {
return $this->comments;
}
public function attachAnswers(array $answers) {
assert_instances_of($answers, 'PonderAnswer');
$this->answers = $answers;
return $this;
}
public function getAnswers() {
return $this->answers;
}
public function getProjectPHIDs() {
return $this->assertAttached($this->projectPHIDs);
}
public function attachProjectPHIDs(array $phids) {
$this->projectPHIDs = $phids;
return $this;
}
public function getMarkupField() {
return self::MARKUP_FIELD_CONTENT;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new PonderQuestionEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new PonderQuestionTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
// Markup interface
public function getMarkupFieldKey($field) {
$hash = PhabricatorHash::digest($this->getMarkupText($field));
$id = $this->getID();
return "ponder:Q{$id}:{$field}:{$hash}";
}
public function getMarkupText($field) {
return $this->getContent();
}
public function newMarkupEngine($field) {
return PhabricatorMarkupEngine::getEngine();
}
public function didMarkupText(
$field,
$output,
PhutilMarkupEngine $engine) {
return $output;
}
public function shouldUseMarkupCache($field) {
return (bool)$this->getID();
}
// votable interface
public function getUserVoteEdgeType() {
return PonderVotingUserHasQuestionEdgeType::EDGECONST;
}
public function getVotablePHID() {
return $this->getPHID();
}
public function save() {
if (!$this->getMailKey()) {
$this->setMailKey(Filesystem::readRandomCharacters(20));
}
return parent::save();
}
public function getOriginalTitle() {
// TODO: Make this actually save/return the original title.
return $this->getTitle();
}
public function getFullTitle() {
$id = $this->getID();
$title = $this->getTitle();
return "Q{$id}: {$title}";
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return $this->getEditPolicy();
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return ($viewer->getPHID() == $this->getAuthorPHID());
}
public function describeAutomaticCapability($capability) {
return pht('The user who asked a question can always view and edit it.');
}
/* -( PhabricatorSubscribableInterface )----------------------------------- */
public function isAutomaticallySubscribed($phid) {
return ($phid == $this->getAuthorPHID());
}
public function shouldShowSubscribersProperty() {
return true;
}
public function shouldAllowSubscription($phid) {
return true;
}
/* -( PhabricatorTokenReceiverInterface )---------------------------------- */
public function getUsersToNotifyOfTokenGiven() {
return array(
$this->getAuthorPHID(),
);
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->openTransaction();
$answers = id(new PonderAnswer())->loadAllWhere(
'questionID = %d',
$this->getID());
foreach ($answers as $answer) {
$engine->destroyObject($answer);
}
$this->delete();
$this->saveTransaction();
}
/* -( PhabricatorSpacesInterface )----------------------------------------- */
public function getSpacePHID() {
return $this->spacePHID;
}
}
diff --git a/src/applications/ponder/storage/PonderQuestionTransaction.php b/src/applications/ponder/storage/PonderQuestionTransaction.php
index 0e4d07b150..38f2b5e45b 100644
--- a/src/applications/ponder/storage/PonderQuestionTransaction.php
+++ b/src/applications/ponder/storage/PonderQuestionTransaction.php
@@ -1,323 +1,321 @@
<?php
final class PonderQuestionTransaction
extends PhabricatorApplicationTransaction {
const TYPE_TITLE = 'ponder.question:question';
const TYPE_CONTENT = 'ponder.question:content';
const TYPE_ANSWERS = 'ponder.question:answer';
const TYPE_STATUS = 'ponder.question:status';
public function getApplicationName() {
return 'ponder';
}
public function getTableName() {
return 'ponder_questiontransaction';
}
public function getApplicationTransactionType() {
return PonderQuestionPHIDType::TYPECONST;
}
public function getApplicationTransactionCommentObject() {
return new PonderQuestionTransactionComment();
}
public function getRequiredHandlePHIDs() {
$phids = parent::getRequiredHandlePHIDs();
switch ($this->getTransactionType()) {
case self::TYPE_ANSWERS:
$phids[] = $this->getNewAnswerPHID();
$phids[] = $this->getObjectPHID();
break;
}
return $phids;
}
public function getRemarkupBlocks() {
$blocks = parent::getRemarkupBlocks();
switch ($this->getTransactionType()) {
case self::TYPE_CONTENT:
$blocks[] = $this->getNewValue();
break;
}
return $blocks;
}
public function getTitle() {
$author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_TITLE:
if ($old === null) {
return pht(
'%s asked this question.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s edited the question title from "%s" to "%s".',
$this->renderHandleLink($author_phid),
$old,
$new);
}
case self::TYPE_CONTENT:
return pht(
'%s edited the question description.',
$this->renderHandleLink($author_phid));
case self::TYPE_ANSWERS:
$answer_handle = $this->getHandle($this->getNewAnswerPHID());
$question_handle = $this->getHandle($object_phid);
return pht(
'%s answered %s',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
case self::TYPE_STATUS:
switch ($new) {
case PonderQuestionStatus::STATUS_OPEN:
return pht(
'%s reopened this question.',
$this->renderHandleLink($author_phid));
- case PonderQuestionStatus::STATUS_CLOSED:
+ case PonderQuestionStatus::STATUS_CLOSED_RESOLVED:
+ return pht(
+ '%s closed this question as resolved.',
+ $this->renderHandleLink($author_phid));
+ case PonderQuestionStatus::STATUS_CLOSED_OBSOLETE:
return pht(
- '%s closed this question.',
+ '%s closed this question as obsolete.',
+ $this->renderHandleLink($author_phid));
+ case PonderQuestionStatus::STATUS_CLOSED_DUPLICATE:
+ return pht(
+ '%s closed this question as a duplicate.',
$this->renderHandleLink($author_phid));
}
}
return parent::getTitle();
}
public function getIcon() {
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_TITLE:
case self::TYPE_CONTENT:
return 'fa-pencil';
case self::TYPE_STATUS:
- switch ($new) {
- case PonderQuestionStatus::STATUS_OPEN:
- return 'fa-check-circle';
- case PonderQuestionStatus::STATUS_CLOSED:
- return 'fa-minus-circle';
- }
+ return PonderQuestionStatus::getQuestionStatusIcon($new);
case self::TYPE_ANSWERS:
return 'fa-plus';
}
return parent::getIcon();
}
public function getColor() {
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_TITLE:
case self::TYPE_CONTENT:
return PhabricatorTransactions::COLOR_BLUE;
case self::TYPE_ANSWERS:
return PhabricatorTransactions::COLOR_GREEN;
case self::TYPE_STATUS:
- switch ($new) {
- case PonderQuestionStatus::STATUS_OPEN:
- return PhabricatorTransactions::COLOR_GREEN;
- case PonderQuestionStatus::STATUS_CLOSED:
- return PhabricatorTransactions::COLOR_INDIGO;
- }
+ return PonderQuestionStatus::getQuestionStatusTagColor($new);
}
}
public function hasChangeDetails() {
switch ($this->getTransactionType()) {
case self::TYPE_CONTENT:
return true;
}
return parent::hasChangeDetails();
}
public function renderChangeDetails(PhabricatorUser $viewer) {
return $this->renderTextCorpusChangeDetails(
$viewer,
$this->getOldValue(),
$this->getNewValue());
}
public function getActionStrength() {
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_TITLE:
if ($old === null) {
return 3;
}
break;
case self::TYPE_ANSWERS:
return 2;
}
return parent::getActionStrength();
}
public function getActionName() {
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_TITLE:
if ($old === null) {
return pht('Asked');
}
break;
case self::TYPE_ANSWERS:
return pht('Answered');
}
return parent::getActionName();
}
public function shouldHide() {
switch ($this->getTransactionType()) {
case self::TYPE_CONTENT:
if ($this->getOldValue() === null) {
return true;
} else {
return false;
}
break;
}
return parent::shouldHide();
}
public function getTitleForFeed() {
$author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_TITLE:
if ($old === null) {
return pht(
'%s asked a question: %s',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
} else {
return pht(
'%s edited the title of %s (was "%s")',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid),
$old);
}
case self::TYPE_CONTENT:
return pht(
'%s edited the description of %s',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
case self::TYPE_ANSWERS:
$answer_handle = $this->getHandle($this->getNewAnswerPHID());
$question_handle = $this->getHandle($object_phid);
return pht(
'%s answered %s',
$this->renderHandleLink($author_phid),
$answer_handle->renderLink($question_handle->getFullName()));
case self::TYPE_STATUS:
switch ($new) {
case PonderQuestionStatus::STATUS_OPEN:
return pht(
'%s reopened %s',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
case PonderQuestionStatus::STATUS_CLOSED:
return pht(
'%s closed %s',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
}
}
return parent::getTitleForFeed();
}
public function getBodyForFeed(PhabricatorFeedStory $story) {
$new = $this->getNewValue();
$old = $this->getOldValue();
$body = null;
switch ($this->getTransactionType()) {
case self::TYPE_TITLE:
if ($old === null) {
$question = $story->getObject($this->getObjectPHID());
return phutil_escape_html_newlines(
id(new PhutilUTF8StringTruncator())
->setMaximumGlyphs(128)
->truncateString($question->getContent()));
}
break;
case self::TYPE_ANSWERS:
$answer = $this->getNewAnswerObject($story);
if ($answer) {
return phutil_escape_html_newlines(
id(new PhutilUTF8StringTruncator())
->setMaximumGlyphs(128)
->truncateString($answer->getContent()));
}
break;
}
return parent::getBodyForFeed($story);
}
/**
* Currently the application only supports adding answers one at a time.
* This data is stored as a list of phids. Use this function to get the
* new phid.
*/
private function getNewAnswerPHID() {
$new = $this->getNewValue();
$old = $this->getOldValue();
$add = array_diff($new, $old);
if (count($add) != 1) {
throw new Exception(
pht('There should be only one answer added at a time.'));
}
return reset($add);
}
/**
* Generally, the answer object is only available if the transaction
* type is `self::TYPE_ANSWERS`.
*
* Some stories - notably ones made before D7027 - will be of the more
* generic @{class:PhabricatorApplicationTransactionFeedStory}. These
* poor stories won't have the PonderAnswer loaded, and thus will have
* less cool information.
*/
private function getNewAnswerObject(PhabricatorFeedStory $story) {
if ($story instanceof PonderTransactionFeedStory) {
$answer_phid = $this->getNewAnswerPHID();
if ($answer_phid) {
return $story->getObject($answer_phid);
}
}
return null;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jul 3, 7:49 PM (4 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
166300
Default Alt Text
(59 KB)

Event Timeline