Index: src/__phutil_library_map__.php =================================================================== --- src/__phutil_library_map__.php +++ src/__phutil_library_map__.php @@ -1839,7 +1839,6 @@ 'PonderConstants' => 'applications/ponder/PonderConstants.php', 'PonderController' => 'applications/ponder/controller/PonderController.php', 'PonderDAO' => 'applications/ponder/storage/PonderDAO.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', @@ -1847,9 +1846,11 @@ 'PonderQuestionAskController' => 'applications/ponder/controller/PonderQuestionAskController.php', 'PonderQuestionDetailView' => 'applications/ponder/view/PonderQuestionDetailView.php', 'PonderQuestionEditor' => 'applications/ponder/editor/PonderQuestionEditor.php', + 'PonderQuestionListController' => 'applications/ponder/controller/PonderQuestionListController.php', 'PonderQuestionMailReceiver' => 'applications/ponder/mail/PonderQuestionMailReceiver.php', 'PonderQuestionPreviewController' => 'applications/ponder/controller/PonderQuestionPreviewController.php', 'PonderQuestionQuery' => 'applications/ponder/query/PonderQuestionQuery.php', + 'PonderQuestionSearchEngine' => 'applications/ponder/query/PonderQuestionSearchEngine.php', 'PonderQuestionSummaryView' => 'applications/ponder/view/PonderQuestionSummaryView.php', 'PonderQuestionViewController' => 'applications/ponder/controller/PonderQuestionViewController.php', 'PonderRemarkupRule' => 'applications/ponder/remarkup/PonderRemarkupRule.php', @@ -3866,7 +3867,6 @@ 'PonderCommentSaveController' => 'PonderController', 'PonderController' => 'PhabricatorController', 'PonderDAO' => 'PhabricatorLiskDAO', - 'PonderFeedController' => 'PonderController', 'PonderMail' => 'PhabricatorMail', 'PonderMentionMail' => 'PonderMail', 'PonderPostBodyView' => 'AphrontView', @@ -3882,9 +3882,15 @@ 'PonderQuestionAskController' => 'PonderController', 'PonderQuestionDetailView' => 'AphrontView', 'PonderQuestionEditor' => 'PhabricatorEditor', + 'PonderQuestionListController' => + array( + 0 => 'PonderController', + 1 => 'PhabricatorApplicationSearchResultsControllerInterface', + ), 'PonderQuestionMailReceiver' => 'PhabricatorObjectMailReceiver', 'PonderQuestionPreviewController' => 'PonderController', 'PonderQuestionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PonderQuestionSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PonderQuestionSummaryView' => 'AphrontView', 'PonderQuestionViewController' => 'PonderController', 'PonderRemarkupRule' => 'PhabricatorRemarkupRuleObject', Index: src/applications/ponder/application/PhabricatorApplicationPonder.php =================================================================== --- src/applications/ponder/application/PhabricatorApplicationPonder.php +++ src/applications/ponder/application/PhabricatorApplicationPonder.php @@ -49,9 +49,7 @@ return array( '/Q(?P[1-9]\d*)' => 'PonderQuestionViewController', '/ponder/' => array( - '(?Pfeed/)?' => 'PonderFeedController', - '(?Pquestions)/' => 'PonderFeedController', - '(?Panswers)/' => 'PonderFeedController', + '(?:query/(?P[^/]+)/)?' => 'PonderQuestionListController', 'answer/add/' => 'PonderAnswerSaveController', 'answer/preview/' => 'PonderAnswerPreviewController', 'question/ask/' => 'PonderQuestionAskController', Index: src/applications/ponder/controller/PonderController.php =================================================================== --- src/applications/ponder/controller/PonderController.php +++ src/applications/ponder/controller/PonderController.php @@ -2,30 +2,19 @@ abstract class PonderController extends PhabricatorController { - public function buildStandardPageResponse($view, array $data) { - - $page = $this->buildStandardPageView(); - $page->setApplicationName(pht('Ponder!')); - $page->setBaseURI('/ponder/'); - $page->setTitle(idx($data, 'title')); - $page->setGlyph("\xE2\x97\xB3"); - $page->appendChild($view); - $page->setSearchDefaultScope(PhabricatorSearchScope::SCOPE_QUESTIONS); - - $response = new AphrontWebpageResponse(); - return $response->setContent($page->render()); - } + protected function buildSideNavView() { + $user = $this->getRequest()->getUser(); + + $nav = new AphrontSideNavFilterView(); + $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); - protected function buildSideNavView(PonderQuestion $question = null) { - $side_nav = new AphrontSideNavFilterView(); - $side_nav->setBaseURI(new PhutilURI($this->getApplicationURI())); + id(new PonderQuestionSearchEngine()) + ->setViewer($user) + ->addNavigationItems($nav->getMenu()); - $side_nav->addLabel(pht('Questions')); - $side_nav->addFilter('feed', pht('All Questions')); - $side_nav->addFilter('questions', pht('Your Questions')); - $side_nav->addFilter('answers', pht('Your Answers')); + $nav->selectFilter(null); - return $side_nav; + return $nav; } public function buildApplicationCrumbs() { @@ -33,8 +22,8 @@ $crumbs ->addAction( id(new PHUIListItemView()) - ->setName(pht('New Question')) - ->setHref('/ponder/question/ask') + ->setName(pht('Create Question')) + ->setHref('/ponder/question/ask/') ->setIcon('create')); return $crumbs; Index: src/applications/ponder/controller/PonderFeedController.php =================================================================== --- src/applications/ponder/controller/PonderFeedController.php +++ /dev/null @@ -1,119 +0,0 @@ -page = idx($data, 'page'); - } - - public function processRequest() { - $request = $this->getRequest(); - $user = $request->getUser(); - $this->answerOffset = $request->getInt('aoff'); - - $pages = array( - 'feed' => pht('All Questions'), - 'questions' => pht('Your Questions'), - 'answers' => pht('Your Answers'), - ); - - $side_nav = $this->buildSideNavView(); - $this->page = $side_nav->selectFilter($this->page, 'feed'); - $title = $pages[$this->page]; - - $crumbs = $this->buildApplicationCrumbs($this->buildSideNavView()); - $crumbs->addCrumb( - id(new PhabricatorCrumbView()) - ->setName($title) - ->setHref($this->getApplicationURI())); - $side_nav->setCrumbs($crumbs); - - switch ($this->page) { - case 'feed': - case 'questions': - $pager = new AphrontPagerView(); - $pager->setOffset($request->getStr('offset')); - $pager->setURI($request->getRequestURI(), 'offset'); - - $query = id(new PonderQuestionQuery()) - ->setViewer($user); - - if ($this->page == 'feed') { - $query - ->setOrder(PonderQuestionQuery::ORDER_HOTTEST); - } else { - $query - ->setOrder(PonderQuestionQuery::ORDER_CREATED) - ->withAuthorPHIDs(array($user->getPHID())); - } - - $questions = $query->executeWithOffsetPager($pager); - - $this->loadHandles(mpull($questions, 'getAuthorPHID')); - - $view = $this->buildQuestionListView($questions); - $view->setPager($pager); - - $side_nav->appendChild($view); - break; - case 'answers': - $answers = PonderAnswerQuery::loadByAuthorWithQuestions( - $user, - $user->getPHID(), - $this->answerOffset, - self::PROFILE_ANSWER_PAGE_SIZE + 1); - - $side_nav->appendChild( - id(new PonderUserProfileView()) - ->setUser($user) - ->setAnswers($answers) - ->setAnswerOffset($this->answerOffset) - ->setPageSize(self::PROFILE_ANSWER_PAGE_SIZE) - ->setURI(new PhutilURI("/ponder/profile/"), "aoff")); - break; - } - - - return $this->buildApplicationPage( - $side_nav, - array( - 'device' => true, - 'title' => $title, - 'dust' => true, - )); - } - - private function buildQuestionListView(array $questions) { - assert_instances_of($questions, 'PonderQuestion'); - $user = $this->getRequest()->getUser(); - - $view = new PhabricatorObjectItemListView(); - $view->setUser($user); - $view->setNoDataString(pht('No matching questions.')); - foreach ($questions as $question) { - $item = new PhabricatorObjectItemView(); - $item->setObjectName('Q'.$question->getID()); - $item->setHeader($question->getTitle()); - $item->setHref('/Q'.$question->getID()); - $item->setObject($question); - - $item->addAttribute( - pht( - 'Asked by %s on %s', - $this->getHandle($question->getAuthorPHID())->renderLink(), - phabricator_date($question->getDateCreated(), $user))); - - $item->addAttribute( - pht('%d Answer(s)', $question->getAnswerCount())); - - $view->addItem($item); - } - - return $view; - } - -} Index: src/applications/ponder/controller/PonderQuestionAskController.php =================================================================== --- src/applications/ponder/controller/PonderQuestionAskController.php +++ src/applications/ponder/controller/PonderQuestionAskController.php @@ -52,8 +52,6 @@ ->setErrors($errors); } - $header = id(new PhabricatorHeaderView())->setHeader(pht('Ask Question')); - $form = id(new AphrontFormView()) ->setUser($user) ->setFlexible(true) @@ -72,7 +70,8 @@ ->setUser($user)) ->appendChild( id(new AphrontFormSubmitControl()) - ->setValue(pht('Ask Away!'))); + ->addCancelButton($this->getApplicationURI()) + ->setValue(pht('Ask Away!'))); $preview = hsprintf( '
'. @@ -91,19 +90,18 @@ 'question_id' => null )); - $nav = $this->buildSideNavView($question); - $nav->selectFilter($question->getID() ? null : 'question/ask'); + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addCrumb( + id(new PhabricatorCrumbView()) + ->setName(pht('Ask Question'))); - $nav->appendChild( + return $this->buildApplicationPage( array( - $header, + $crumbs, $error_view, $form, $preview, - )); - - return $this->buildApplicationPage( - $nav, + ), array( 'device' => true, 'dust' => true, Index: src/applications/ponder/controller/PonderQuestionListController.php =================================================================== --- /dev/null +++ src/applications/ponder/controller/PonderQuestionListController.php @@ -0,0 +1,65 @@ +queryKey = idx($data, 'queryKey'); + } + + public function processRequest() { + $request = $this->getRequest(); + $controller = id(new PhabricatorApplicationSearchController($request)) + ->setQueryKey($this->queryKey) + ->setSearchEngine(new PonderQuestionSearchEngine()) + ->setNavigation($this->buildSideNavView()); + + return $this->delegateToController($controller); + } + + public function renderResultsList( + array $questions, + PhabricatorSavedQuery $query) { + assert_instances_of($questions, 'PonderQuestion'); + $viewer = $this->getRequest()->getUser(); + + $phids = array(); + $phids[] = mpull($questions, 'getAuthorPHID'); + $phids = array_mergev($phids); + + $handles = $this->loadViewerHandles($phids); + + + $view = id(new PhabricatorObjectItemListView()) + ->setUser($viewer); + + foreach ($questions as $question) { + $item = new PhabricatorObjectItemView(); + $item->setObjectName('Q'.$question->getID()); + $item->setHeader($question->getTitle()); + $item->setHref('/Q'.$question->getID()); + $item->setObject($question); + + $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())); + + $view->addItem($item); + } + + return $view; + } + +} Index: src/applications/ponder/controller/PonderQuestionViewController.php =================================================================== --- src/applications/ponder/controller/PonderQuestionViewController.php +++ src/applications/ponder/controller/PonderQuestionViewController.php @@ -36,10 +36,7 @@ } } - $subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID( - $question->getPHID()); - - $object_phids = array_merge($object_phids, $subscribers); + $object_phids = array_merge($object_phids); $this->loadHandles($object_phids); $handles = $this->getLoadedHandles(); @@ -67,7 +64,7 @@ ->setHeader($question->getTitle()); $actions = $this->buildActionListView($question); - $properties = $this->buildPropertyListView($question, $subscribers); + $properties = $this->buildPropertyListView($question); $crumbs = $this->buildApplicationCrumbs($this->buildSideNavView()); $crumbs->setActionList($actions); @@ -76,8 +73,7 @@ ->setName('Q'.$this->questionID) ->setHref('/Q'.$this->questionID)); - $nav = $this->buildSideNavView($question); - $nav->appendChild( + return $this->buildApplicationPage( array( $crumbs, $header, @@ -86,12 +82,7 @@ $detail_panel, $responses_panel, $answer_add_panel - )); - $nav->selectFilter(null); - - - return $this->buildApplicationPage( - $nav, + ), array( 'device' => true, 'title' => 'Q'.$question->getID().' '.$question->getTitle(), @@ -108,8 +99,7 @@ } private function buildPropertyListView( - PonderQuestion $question, - array $subscribers) { + PonderQuestion $question) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorPropertyListView()) @@ -123,14 +113,6 @@ pht('Created'), phabricator_datetime($question->getDateCreated(), $viewer)); - if ($subscribers) { - $subscribers = $this->renderHandlesForPHIDs($subscribers); - } - - $view->addProperty( - pht('Subscribers'), - nonempty($subscribers, phutil_tag('em', array(), pht('None')))); - return $view; } } Index: src/applications/ponder/query/PonderAnswerQuery.php =================================================================== --- src/applications/ponder/query/PonderAnswerQuery.php +++ src/applications/ponder/query/PonderAnswerQuery.php @@ -27,39 +27,6 @@ return $this; } - public static function loadByAuthorWithQuestions( - $viewer, - $phid, - $offset, - $count) { - - if (!$viewer) { - throw new Exception("Must set viewer when calling loadByAuthor..."); - } - - $answers = id(new PonderAnswerQuery()) - ->withAuthorPHID($phid) - ->orderByNewest(true) - ->setOffset($offset) - ->setLimit($count) - ->execute(); - - $answerset = new LiskDAOSet(); - foreach ($answers as $answer) { - $answerset->addToSet($answer); - } - - foreach ($answers as $answer) { - $question = $answer->loadOneRelative( - new PonderQuestion(), - 'id', - 'getQuestionID'); - $answer->setQuestion($question); - } - - return $answers; - } - public static function loadByAuthor($viewer, $author_phid, $offset, $count) { if (!$viewer) { throw new Exception("Must set viewer when calling loadByAuthor"); Index: src/applications/ponder/query/PonderQuestionQuery.php =================================================================== --- src/applications/ponder/query/PonderQuestionQuery.php +++ src/applications/ponder/query/PonderQuestionQuery.php @@ -9,6 +9,7 @@ private $ids; private $phids; private $authorPHIDs; + private $answererPHIDs; private $order = self::ORDER_CREATED; public function withIDs(array $ids) { @@ -26,6 +27,11 @@ return $this; } + public function withAnswererPHIDs(array $phids) { + $this->answererPHIDs = $phids; + return $this; + } + public function setOrder($order) { $this->order = $order; return $this; @@ -57,15 +63,24 @@ $where = array(); if ($this->ids) { - $where[] = qsprintf($conn_r, 'q.id IN (%Ld)', $this->ids); + $where[] = qsprintf( + $conn_r, + 'q.id IN (%Ld)', + $this->ids); } if ($this->phids) { - $where[] = qsprintf($conn_r, 'q.phid IN (%Ls)', $this->phids); + $where[] = qsprintf( + $conn_r, + 'q.phid IN (%Ls)', + $this->phids); } if ($this->authorPHIDs) { - $where[] = qsprintf($conn_r, 'q.authorPHID IN (%Ls)', $this->authorPHIDs); + $where[] = qsprintf( + $conn_r, + 'q.authorPHID IN (%Ls)', + $this->authorPHIDs); } $where[] = $this->buildPagingClause($conn_r); @@ -88,19 +103,31 @@ $question = new PonderQuestion(); $conn_r = $question->establishConnection('r'); - $where = $this->buildWhereClause($conn_r); - $order_by = $this->buildOrderByClause($conn_r); - $limit = $this->buildLimitClause($conn_r); + $data = queryfx_all( + $conn_r, + 'SELECT q.* FROM %T q %Q %Q %Q %Q', + $question->getTableName(), + $this->buildJoinsClause($conn_r), + $this->buildWhereClause($conn_r), + $this->buildOrderByClause($conn_r), + $this->buildLimitClause($conn_r)); - return $question->loadAllFromArray( - queryfx_all( - $conn_r, - 'SELECT q.* FROM %T q %Q %Q %Q', - $question->getTableName(), - $where, - $order_by, - $limit)); + return $question->loadAllFromArray($data); } + 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); + } } Index: src/applications/ponder/query/PonderQuestionSearchEngine.php =================================================================== --- /dev/null +++ src/applications/ponder/query/PonderQuestionSearchEngine.php @@ -0,0 +1,105 @@ +setParameter( + 'authorPHIDs', + array_values($request->getArr('authors'))); + + $saved->setParameter( + 'answererPHIDs', + array_values($request->getArr('answerers'))); + + return $saved; + } + + public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { + $query = id(new PonderQuestionQuery()); + + $author_phids = $saved->getParameter('authorPHIDs'); + if ($author_phids) { + $query->withAuthorPHIDs($author_phids); + } + + $answerer_phids = $saved->getParameter('answererPHIDs'); + if ($answerer_phids) { + $query->withAnswererPHIDs($answerer_phids); + } + + return $query; + } + + public function buildSearchForm( + AphrontFormView $form, + PhabricatorSavedQuery $saved_query) { + + $author_phids = $saved_query->getParameter('authorPHIDs', array()); + $answerer_phids = $saved_query->getParameter('answererPHIDs', array()); + + $phids = array_merge($author_phids, $answerer_phids); + $handles = id(new PhabricatorObjectHandleData($phids)) + ->setViewer($this->requireViewer()) + ->loadHandles(); + $tokens = mpull($handles, 'getFullName', 'getPHID'); + + $author_tokens = array_select_keys($tokens, $author_phids); + $answerer_tokens = array_select_keys($tokens, $answerer_phids); + + $form + ->appendChild( + id(new AphrontFormTokenizerControl()) + ->setDatasource('/typeahead/common/users/') + ->setName('authors') + ->setLabel(pht('Authors')) + ->setValue($author_tokens)) + ->appendChild( + id(new AphrontFormTokenizerControl()) + ->setDatasource('/typeahead/common/users/') + ->setName('answerers') + ->setLabel(pht('Answered By')) + ->setValue($answerer_tokens)); + } + + protected function getURI($path) { + return '/ponder/'.$path; + } + + public function getBuiltinQueryNames() { + $names = array( + '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 'authored': + return $query->setParameter( + 'authorPHIDs', + array($this->requireViewer()->getPHID())); + case 'answered': + return $query->setParameter( + 'answererPHIDs', + array($this->requireViewer()->getPHID())); + } + + return parent::buildSavedQueryFromBuiltin($query_key); + } + +}