From 70a5ec600d2644332636304cd7c59aab0ce391fe Mon Sep 17 00:00:00 2001 From: Gareth Evans Date: Sat, 27 Jul 2013 18:37:17 -0700 Subject: [PATCH] Allowing Closing of questions Summary: Fixes T3579 As a basic overview, this enables the author of a question to open/close a question. Other bits; - Add "Open" filter to the builtin queries - Add "Status" to search form - Refactor ponder constants - Add coloured bars for different question statuses Test Plan: - Open/Close questions - Search for some bits - Use filters Reviewers: epriestley Reviewed By: epriestley CC: aran, Korvin Maniphest Tasks: T3579 Differential Revision: https://secure.phabricator.com/D6590 Conflicts: src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php --- .../patches/20130727.ponderquestionstatus.sql | 5 +++ src/__phutil_library_map__.php | 10 ++++- src/applications/ponder/PonderConstants.php | 10 ----- .../PhabricatorApplicationPonder.php | 2 + .../ponder/constants/PonderConstants.php | 4 ++ .../ponder/constants/PonderLiterals.php | 10 +++++ .../ponder/constants/PonderQuestionStatus.php | 34 ++++++++++++++++ .../ponder/constants/PonderVote.php | 11 +++++ .../PonderAnswerPreviewController.php | 2 +- .../PonderQuestionAskController.php | 1 + .../PonderQuestionListController.php | 3 ++ .../PonderQuestionStatusController.php | 40 +++++++++++++++++++ .../PonderQuestionViewController.php | 33 ++++++++++++++- .../ponder/editor/PonderVoteEditor.php | 2 +- .../ponder/query/PonderQuestionQuery.php | 31 ++++++++++++++ .../query/PonderQuestionSearchEngine.php | 27 ++++++++++++- .../ponder/storage/PonderAnswer.php | 2 +- .../ponder/storage/PonderQuestion.php | 3 +- .../ponder/view/PonderAnswerListView.php | 2 +- .../ponder/view/PonderQuestionDetailView.php | 2 +- .../patch/PhabricatorBuiltinPatchList.php | 12 ++++-- 21 files changed, 222 insertions(+), 24 deletions(-) create mode 100644 resources/sql/patches/20130727.ponderquestionstatus.sql delete mode 100644 src/applications/ponder/PonderConstants.php create mode 100644 src/applications/ponder/constants/PonderConstants.php create mode 100644 src/applications/ponder/constants/PonderLiterals.php create mode 100644 src/applications/ponder/constants/PonderQuestionStatus.php create mode 100644 src/applications/ponder/constants/PonderVote.php create mode 100644 src/applications/ponder/controller/PonderQuestionStatusController.php diff --git a/resources/sql/patches/20130727.ponderquestionstatus.sql b/resources/sql/patches/20130727.ponderquestionstatus.sql new file mode 100644 index 0000000000..4f8d19957d --- /dev/null +++ b/resources/sql/patches/20130727.ponderquestionstatus.sql @@ -0,0 +1,5 @@ +ALTER TABLE {$NAMESPACE}_ponder.ponder_question + ADD COLUMN `status` INT(10) UNSIGNED NOT NULL AFTER `authorPHID`; + +ALTER TABLE {$NAMESPACE}_ponder.ponder_question + ADD INDEX `status` (`status`); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index bbdaccf6ea..a9a4d59034 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1881,9 +1881,10 @@ phutil_register_library_map(array( 'PonderCommentMail' => 'applications/ponder/mail/PonderCommentMail.php', 'PonderCommentQuery' => 'applications/ponder/query/PonderCommentQuery.php', 'PonderCommentSaveController' => 'applications/ponder/controller/PonderCommentSaveController.php', - 'PonderConstants' => 'applications/ponder/PonderConstants.php', + 'PonderConstants' => 'applications/ponder/constants/PonderConstants.php', 'PonderController' => 'applications/ponder/controller/PonderController.php', 'PonderDAO' => 'applications/ponder/storage/PonderDAO.php', + 'PonderLiterals' => 'applications/ponder/constants/PonderLiterals.php', 'PonderMail' => 'applications/ponder/mail/PonderMail.php', 'PonderMentionMail' => 'applications/ponder/mail/PonderMentionMail.php', 'PonderPHIDTypeQuestion' => 'applications/ponder/phid/PonderPHIDTypeQuestion.php', @@ -1897,6 +1898,8 @@ phutil_register_library_map(array( 'PonderQuestionPreviewController' => 'applications/ponder/controller/PonderQuestionPreviewController.php', 'PonderQuestionQuery' => 'applications/ponder/query/PonderQuestionQuery.php', 'PonderQuestionSearchEngine' => 'applications/ponder/query/PonderQuestionSearchEngine.php', + 'PonderQuestionStatus' => 'applications/ponder/constants/PonderQuestionStatus.php', + 'PonderQuestionStatusController' => 'applications/ponder/controller/PonderQuestionStatusController.php', 'PonderQuestionSummaryView' => 'applications/ponder/view/PonderQuestionSummaryView.php', 'PonderQuestionViewController' => 'applications/ponder/controller/PonderQuestionViewController.php', 'PonderRemarkupRule' => 'applications/ponder/remarkup/PonderRemarkupRule.php', @@ -1905,6 +1908,7 @@ phutil_register_library_map(array( 'PonderUserProfileView' => 'applications/ponder/view/PonderUserProfileView.php', 'PonderVotableInterface' => 'applications/ponder/storage/PonderVotableInterface.php', 'PonderVotableView' => 'applications/ponder/view/PonderVotableView.php', + 'PonderVote' => 'applications/ponder/constants/PonderVote.php', 'PonderVoteEditor' => 'applications/ponder/editor/PonderVoteEditor.php', 'PonderVoteSaveController' => 'applications/ponder/controller/PonderVoteSaveController.php', 'ProjectRemarkupRule' => 'applications/project/remarkup/ProjectRemarkupRule.php', @@ -3996,6 +4000,7 @@ phutil_register_library_map(array( 'PonderCommentSaveController' => 'PonderController', 'PonderController' => 'PhabricatorController', 'PonderDAO' => 'PhabricatorLiskDAO', + 'PonderLiterals' => 'PonderConstants', 'PonderMail' => 'PhabricatorMail', 'PonderMentionMail' => 'PonderMail', 'PonderPHIDTypeQuestion' => 'PhabricatorPHIDType', @@ -4021,6 +4026,8 @@ phutil_register_library_map(array( 'PonderQuestionPreviewController' => 'PonderController', 'PonderQuestionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PonderQuestionSearchEngine' => 'PhabricatorApplicationSearchEngine', + 'PonderQuestionStatus' => 'PonderConstants', + 'PonderQuestionStatusController' => 'PonderController', 'PonderQuestionSummaryView' => 'AphrontView', 'PonderQuestionViewController' => 'PonderController', 'PonderRemarkupRule' => 'PhabricatorRemarkupRuleObject', @@ -4028,6 +4035,7 @@ phutil_register_library_map(array( 'PonderSearchIndexer' => 'PhabricatorSearchDocumentIndexer', 'PonderUserProfileView' => 'AphrontView', 'PonderVotableView' => 'AphrontView', + 'PonderVote' => 'PonderConstants', 'PonderVoteEditor' => 'PhabricatorEditor', 'PonderVoteSaveController' => 'PonderController', 'ProjectRemarkupRule' => 'PhabricatorRemarkupRuleObject', diff --git a/src/applications/ponder/PonderConstants.php b/src/applications/ponder/PonderConstants.php deleted file mode 100644 index 0d0c5e2565..0000000000 --- a/src/applications/ponder/PonderConstants.php +++ /dev/null @@ -1,10 +0,0 @@ - 'PonderAnswerPreviewController', 'question/ask/' => 'PonderQuestionAskController', 'question/preview/' => 'PonderQuestionPreviewController', + 'question/(?Popen|close)/(?P[1-9]\d*)/' => + 'PonderQuestionStatusController', 'comment/add/' => 'PonderCommentSaveController', '(?Pquestion)/vote/' => 'PonderVoteSaveController', '(?Panswer)/vote/' => 'PonderVoteSaveController' diff --git a/src/applications/ponder/constants/PonderConstants.php b/src/applications/ponder/constants/PonderConstants.php new file mode 100644 index 0000000000..a61c66926f --- /dev/null +++ b/src/applications/ponder/constants/PonderConstants.php @@ -0,0 +1,4 @@ + pht('Open'), + self::STATUS_CLOSED => pht('Closed'), + ); + } + + public static function getQuestionStatusFullName($status) { + $map = array( + self::STATUS_OPEN => pht('Open'), + self::STATUS_CLOSED => pht('Closed by author'), + ); + return idx($map, $status, '???'); + } + + public static function getQuestionStatusTagColor($status) { + $map = array( + self::STATUS_CLOSED => PhabricatorTagView::COLOR_BLACK, + ); + + return idx($map, $status); + } + +} diff --git a/src/applications/ponder/constants/PonderVote.php b/src/applications/ponder/constants/PonderVote.php new file mode 100644 index 0000000000..098362dab7 --- /dev/null +++ b/src/applications/ponder/constants/PonderVote.php @@ -0,0 +1,11 @@ +setPreview(true) ->setUser($user) ->setHandles($handles) - ->setAction(PonderConstants::ANSWERED_LITERAL); + ->setAction(PonderLiterals::LITERAL_ANSWERED); return id(new AphrontAjaxResponse()) ->setContent($view->render()); diff --git a/src/applications/ponder/controller/PonderQuestionAskController.php b/src/applications/ponder/controller/PonderQuestionAskController.php index 9be27e81e0..b2293119ae 100644 --- a/src/applications/ponder/controller/PonderQuestionAskController.php +++ b/src/applications/ponder/controller/PonderQuestionAskController.php @@ -17,6 +17,7 @@ final class PonderQuestionAskController extends PonderController { if ($request->isFormPost()) { $question->setTitle($request->getStr('title')); $question->setContent($request->getStr('content')); + $question->setStatus(PonderQuestionStatus::STATUS_OPEN); $len = phutil_utf8_strlen($question->getTitle()); if ($len < 1) { diff --git a/src/applications/ponder/controller/PonderQuestionListController.php b/src/applications/ponder/controller/PonderQuestionListController.php index 630bf50278..d36990a4bd 100644 --- a/src/applications/ponder/controller/PonderQuestionListController.php +++ b/src/applications/ponder/controller/PonderQuestionListController.php @@ -45,6 +45,9 @@ final class PonderQuestionListController extends PonderController $item->setHeader($question->getTitle()); $item->setHref('/Q'.$question->getID()); $item->setObject($question); + $item->setBarColor( + PonderQuestionStatus::getQuestionStatusTagColor( + $question->getStatus())); $created_date = phabricator_date($question->getDateCreated(), $viewer); $item->addIcon('none', $created_date); diff --git a/src/applications/ponder/controller/PonderQuestionStatusController.php b/src/applications/ponder/controller/PonderQuestionStatusController.php new file mode 100644 index 0000000000..0b5ccc65cf --- /dev/null +++ b/src/applications/ponder/controller/PonderQuestionStatusController.php @@ -0,0 +1,40 @@ +status = idx($data, 'status'); + $this->id = idx($data, 'id'); + } + + public function processRequest() { + + $request = $this->getRequest(); + $user = $request->getUser(); + + $question = id(new PonderQuestion())->load($this->id); + if (!$question) { + return new Aphront404Response(); + } + + switch ($this->status) { + case 'open': + $question->setStatus(PonderQuestionStatus::STATUS_OPEN); + break; + case 'close': + $question->setStatus(PonderQuestionStatus::STATUS_CLOSED); + break; + default: + return new Aphront400Response(); + } + + $question->save(); + + 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 3b1381e6f0..9086d6184e 100644 --- a/src/applications/ponder/controller/PonderQuestionViewController.php +++ b/src/applications/ponder/controller/PonderQuestionViewController.php @@ -92,10 +92,34 @@ final class PonderQuestionViewController extends PonderController { private function buildActionListView(PonderQuestion $question) { $request = $this->getRequest(); - return id(new PhabricatorActionListView()) - ->setUser($request->getUser()) + $user = $request->getUser(); + + $action_list = id(new PhabricatorActionListView()) + ->setUser($user) ->setObject($question) ->setObjectURI($request->getRequestURI()); + + if ($user->getPhid() === $question->getAuthorPhid()) { + if ($question->getStatus() == PonderQuestionStatus::STATUS_OPEN) { + $name = pht("Close Question"); + $icon = "delete"; + $href = "close"; + } else { + $name = pht("Open Question"); + $icon = "enable"; + $href = "open"; + } + $action_list->addAction( + id(new PhabricatorActionView()) + ->setName($name) + ->setIcon($icon) + ->setRenderAsForm(true) + ->setHref( + $this->getApplicationURI( + "/question/{$href}/{$this->questionID}/"))); + } + + return $action_list; } private function buildPropertyListView( @@ -105,6 +129,11 @@ final class PonderQuestionViewController extends PonderController { $view = id(new PhabricatorPropertyListView()) ->setUser($viewer) ->setObject($question); + + $view->addProperty( + pht('Status'), + PonderQuestionStatus::getQuestionStatusFullName($question->getStatus())); + $view->addProperty( pht('Author'), $this->getHandle($question->getAuthorPHID())->renderLink()); diff --git a/src/applications/ponder/editor/PonderVoteEditor.php b/src/applications/ponder/editor/PonderVoteEditor.php index baa81a476e..a47726535a 100644 --- a/src/applications/ponder/editor/PonderVoteEditor.php +++ b/src/applications/ponder/editor/PonderVoteEditor.php @@ -56,7 +56,7 @@ final class PonderVoteEditor extends PhabricatorEditor { $votable->getVotablePHID()); if (!$curvote) { - $curvote = PonderConstants::NONE_VOTE; + $curvote = PonderVote::VOTE_NONE; } // adjust votable's score by this much diff --git a/src/applications/ponder/query/PonderQuestionQuery.php b/src/applications/ponder/query/PonderQuestionQuery.php index 29b08e5587..1fd093821c 100644 --- a/src/applications/ponder/query/PonderQuestionQuery.php +++ b/src/applications/ponder/query/PonderQuestionQuery.php @@ -12,6 +12,11 @@ final class PonderQuestionQuery private $answererPHIDs; private $order = self::ORDER_CREATED; + private $status = 'status-any'; + const STATUS_ANY = 'status-any'; + const STATUS_OPEN = 'status-open'; + const STATUS_CLOSED = 'status-closed'; + public function withIDs(array $ids) { $this->ids = $ids; return $this; @@ -27,6 +32,11 @@ final class PonderQuestionQuery return $this; } + public function withStatus($status) { + $this->status = $status; + return $this; + } + public function withAnswererPHIDs(array $phids) { $this->answererPHIDs = $phids; return $this; @@ -83,6 +93,27 @@ final class PonderQuestionQuery $this->authorPHIDs); } + if ($this->status) { + switch ($this->status) { + case self::STATUS_ANY: + break; + case self::STATUS_OPEN: + $where[] = qsprintf( + $conn_r, + 'q.status = %d', + PonderQuestionStatus::STATUS_OPEN); + break; + case self::STATUS_CLOSED: + $where[] = qsprintf( + $conn_r, + 'q.status = %d', + PonderQuestionStatus::STATUS_CLOSED); + break; + default: + throw new Exception("Unknown status query '{$this->status}'!"); + } + } + $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); diff --git a/src/applications/ponder/query/PonderQuestionSearchEngine.php b/src/applications/ponder/query/PonderQuestionSearchEngine.php index 15845cbada..6de6fc220c 100644 --- a/src/applications/ponder/query/PonderQuestionSearchEngine.php +++ b/src/applications/ponder/query/PonderQuestionSearchEngine.php @@ -14,6 +14,8 @@ final class PonderQuestionSearchEngine 'answererPHIDs', array_values($request->getArr('answerers'))); + $saved->setParameter('status', $request->getStr('status')); + return $saved; } @@ -30,6 +32,18 @@ final class PonderQuestionSearchEngine $query->withAnswererPHIDs($answerer_phids); } + $status = $saved->getParameter('status'); + if ($status != null) { + switch ($status) { + case 0: + $query->withStatus(PonderQuestionQuery::STATUS_OPEN); + break; + case 1: + $query->withStatus(PonderQuestionQuery::STATUS_CLOSED); + break; + } + } + return $query; } @@ -39,6 +53,8 @@ final class PonderQuestionSearchEngine $author_phids = $saved_query->getParameter('authorPHIDs', array()); $answerer_phids = $saved_query->getParameter('answererPHIDs', array()); + $status = $saved_query->getParameter( + 'status', PonderQuestionStatus::STATUS_OPEN); $phids = array_merge($author_phids, $answerer_phids); $handles = id(new PhabricatorObjectHandleData($phids)) @@ -61,7 +77,13 @@ final class PonderQuestionSearchEngine ->setDatasource('/typeahead/common/users/') ->setName('answerers') ->setLabel(pht('Answered By')) - ->setValue($answerer_tokens)); + ->setValue($answerer_tokens)) + ->appendChild( + id(new AphrontFormSelectControl()) + ->setLabel(pht('Status')) + ->setName('status') + ->setValue($status) + ->setOptions(PonderQuestionStatus::getQuestionStatusMap())); } protected function getURI($path) { @@ -70,6 +92,7 @@ final class PonderQuestionSearchEngine public function getBuiltinQueryNames() { $names = array( + 'open' => pht('Open Questions'), 'all' => pht('All Questions'), ); @@ -89,6 +112,8 @@ final class PonderQuestionSearchEngine switch ($query_key) { case 'all': return $query; + case 'open': + return $query->setParameter('status', PonderQuestionQuery::STATUS_OPEN); case 'authored': return $query->setParameter( 'authorPHIDs', diff --git a/src/applications/ponder/storage/PonderAnswer.php b/src/applications/ponder/storage/PonderAnswer.php index 1a5c8fb736..11490b2dae 100644 --- a/src/applications/ponder/storage/PonderAnswer.php +++ b/src/applications/ponder/storage/PonderAnswer.php @@ -29,7 +29,7 @@ final class PonderAnswer extends PonderDAO public function setUserVote($vote) { $this->vote = $vote['data']; if (!$this->vote) { - $this->vote = PonderConstants::NONE_VOTE; + $this->vote = PonderVote::VOTE_NONE; } return $this; } diff --git a/src/applications/ponder/storage/PonderQuestion.php b/src/applications/ponder/storage/PonderQuestion.php index 987e9bb3ac..b473a2c335 100644 --- a/src/applications/ponder/storage/PonderQuestion.php +++ b/src/applications/ponder/storage/PonderQuestion.php @@ -14,6 +14,7 @@ final class PonderQuestion extends PonderDAO protected $phid; protected $authorPHID; + protected $status; protected $content; protected $contentSource; @@ -95,7 +96,7 @@ final class PonderQuestion extends PonderDAO public function setUserVote($vote) { $this->vote = $vote['data']; if (!$this->vote) { - $this->vote = PonderConstants::NONE_VOTE; + $this->vote = PonderVote::VOTE_NONE; } return $this; } diff --git a/src/applications/ponder/view/PonderAnswerListView.php b/src/applications/ponder/view/PonderAnswerListView.php index 174e1c19bf..26ceb95556 100644 --- a/src/applications/ponder/view/PonderAnswerListView.php +++ b/src/applications/ponder/view/PonderAnswerListView.php @@ -55,7 +55,7 @@ final class PonderAnswerListView extends AphrontView { $view ->setQuestion($question) ->setTarget($cur_answer) - ->setAction(PonderConstants::ANSWERED_LITERAL) + ->setAction(PonderLiterals::LITERAL_ANSWERED) ->setHandles($handles) ->setUser($user); diff --git a/src/applications/ponder/view/PonderQuestionDetailView.php b/src/applications/ponder/view/PonderQuestionDetailView.php index 9a5f1db018..8577321c76 100644 --- a/src/applications/ponder/view/PonderQuestionDetailView.php +++ b/src/applications/ponder/view/PonderQuestionDetailView.php @@ -30,7 +30,7 @@ final class PonderQuestionDetailView extends AphrontView { ->setQuestion($question) ->setUser($user) ->setHandles($handles) - ->setAction(PonderConstants::ASKED_LITERAL); + ->setAction(PonderLiterals::LITERAL_ASKED); $commentview = new PonderCommentListView(); $commentview diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php index b74267fd5a..caa195dfa1 100644 --- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php +++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php @@ -1471,14 +1471,18 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList { 'type' => 'php', 'name' => $this->getPatchPath('20130716.archivememberlessprojects.php'), ), - '20130723.taskstarttime.sql' => array( - 'type' => 'sql', - 'name' => $this->getPatchPath('20130723.taskstarttime.sql'), - ), '20130722.pholioreplace.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130722.pholioreplace.sql'), ), + '20130723.taskstarttime.sql' => array( + 'type' => 'sql', + 'name' => $this->getPatchPath('20130723.taskstarttime.sql'), + ), + '20130727.ponderquestionstatus.sql' => array( + 'type' => 'sql', + 'name' => $this->getPatchPath('20130727.ponderquestionstatus.sql'), + ), ); } }