1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-09 16:32:39 +01:00

Add additional statuses to Ponder

Summary: Ref T9096. This is a first cut at adding additional statuses, happy to add or subtract as needed... maybe even configurable? Also, the dialog doesn't seem to fire, I'll keep debugging.

Test Plan: Close and Reopen many questions. Test applicationSearch params by seeing resolved questions, all questions.

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin

Maniphest Tasks: T9096

Differential Revision: https://secure.phabricator.com/D13826
This commit is contained in:
Chad Little 2015-08-08 10:23:33 -07:00
parent dc687dbd92
commit d2ef273ecd
12 changed files with 147 additions and 94 deletions

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_ponder.ponder_question
MODIFY status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,2 @@
UPDATE {$NAMESPACE}_ponder.ponder_question
SET status = 'open' WHERE status = 0;

View file

@ -0,0 +1,2 @@
UPDATE {$NAMESPACE}_ponder.ponder_question
SET status = 'resolved' WHERE status = 1;

View file

@ -71,7 +71,7 @@ final class PhabricatorPonderApplication extends PhabricatorApplication {
=> 'PonderQuestionHistoryController',
'preview/'
=> 'PhabricatorMarkupPreviewController',
'question/(?P<status>open|close)/(?P<id>[1-9]\d*)/'
'question/status/(?P<id>[1-9]\d*)/'
=> 'PonderQuestionStatusController',
'vote/' => 'PonderVoteSaveController',
),

View file

@ -2,20 +2,40 @@
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'));
}
@ -23,7 +43,9 @@ final class PonderQuestionStatus extends PonderConstants {
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);
@ -32,10 +54,27 @@ final class PonderQuestionStatus extends PonderConstants {
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,
);
}
}

View file

@ -33,6 +33,8 @@ final class PonderQuestionEditController extends PonderController {
$v_view = $question->getViewPolicy();
$v_edit = $question->getEditPolicy();
$v_space = $question->getSpacePHID();
$v_status = $question->getStatus();
$errors = array();
$e_title = true;
@ -43,6 +45,7 @@ final class PonderQuestionEditController extends PonderController {
$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) {
@ -65,6 +68,10 @@ final class PonderQuestionEditController extends PonderController {
->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);
@ -130,7 +137,13 @@ final class PonderQuestionEditController extends PonderController {
->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())
@ -149,21 +162,23 @@ final class PonderQuestionEditController extends PonderController {
->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,
@ -171,7 +186,7 @@ final class PonderQuestionEditController extends PonderController {
$preview,
),
array(
'title' => pht('Ask New Question'),
'title' => $title,
));
}

View file

@ -6,7 +6,6 @@ final class PonderQuestionStatusController
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$status = $request->getURIData('status');
$question = id(new PonderQuestionQuery())
->setViewer($viewer)
@ -21,29 +20,46 @@ final class PonderQuestionStatusController
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);
$editor = id(new PonderQuestionEditor())
->setActor($viewer)
->setContentSourceFromRequest($request);
foreach (PonderQuestionStatus::getQuestionStatusMap() as $value => $name) {
$description = PonderQuestionStatus::getQuestionStatusDescription($value);
$radio->addButton($value, $name, $description);
}
$editor->applyTransactions($question, $xactions);
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild($radio);
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());
}
}

View file

@ -45,7 +45,11 @@ final class PonderQuestionViewController extends PonderController {
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);
@ -109,21 +113,18 @@ final class PonderQuestionViewController extends PonderController {
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())

View file

@ -5,17 +5,12 @@ final class PonderQuestionQuery
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;
@ -34,7 +29,7 @@ final class PonderQuestionQuery
return $this;
}
public function withStatus($status) {
public function withStatuses($status) {
$this->status = $status;
return $this;
}
@ -84,24 +79,10 @@ final class PonderQuestionQuery
}
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;

View file

@ -27,16 +27,8 @@ final class PonderQuestionSearchEngine
$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;
@ -52,9 +44,9 @@ final class PonderQuestionSearchEngine
->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()),
);
}
@ -66,6 +58,7 @@ final class PonderQuestionSearchEngine
protected function getBuiltinQueryNames() {
$names = array(
'open' => pht('Open Questions'),
'resolved' => pht('Resolved Questions'),
'all' => pht('All Questions'),
);
@ -85,7 +78,11 @@ final class PonderQuestionSearchEngine
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',

View file

@ -65,7 +65,7 @@ final class PonderQuestion extends PonderDAO
self::CONFIG_COLUMN_SCHEMA => array(
'title' => 'text255',
'voteCount' => 'sint32',
'status' => 'uint32',
'status' => 'text32',
'content' => 'text',
'heat' => 'double',
'answerCount' => 'uint32',

View file

@ -87,9 +87,17 @@ final class PonderQuestionTransaction
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.',
'%s closed this question as resolved.',
$this->renderHandleLink($author_phid));
case PonderQuestionStatus::STATUS_CLOSED_OBSOLETE:
return pht(
'%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));
}
}
@ -106,12 +114,7 @@ final class PonderQuestionTransaction
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';
}
@ -130,12 +133,7 @@ final class PonderQuestionTransaction
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);
}
}