1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-01-02 02:40:58 +01:00

Use ApplicationSearch in Ponder

Summary:
Ref T2625. Also modernize some other things:

  - Fix double-"subscribers".
  - Use byline and more standard date.
  - Modernize some of the use of crumbs and navigation.
  - Delete some dead / uncalled code.

Test Plan: {F50669}

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T3578, T2625

Differential Revision: https://secure.phabricator.com/D6486
This commit is contained in:
epriestley 2013-07-18 12:40:51 -07:00
parent 21d5992a58
commit 85798368be
10 changed files with 245 additions and 227 deletions

View file

@ -1839,7 +1839,6 @@ phutil_register_library_map(array(
'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 @@ phutil_register_library_map(array(
'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 @@ phutil_register_library_map(array(
'PonderCommentSaveController' => 'PonderController',
'PonderController' => 'PhabricatorController',
'PonderDAO' => 'PhabricatorLiskDAO',
'PonderFeedController' => 'PonderController',
'PonderMail' => 'PhabricatorMail',
'PonderMentionMail' => 'PonderMail',
'PonderPostBodyView' => 'AphrontView',
@ -3882,9 +3882,15 @@ phutil_register_library_map(array(
'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',

View file

@ -49,9 +49,7 @@ final class PhabricatorApplicationPonder extends PhabricatorApplication {
return array(
'/Q(?P<id>[1-9]\d*)' => 'PonderQuestionViewController',
'/ponder/' => array(
'(?P<page>feed/)?' => 'PonderFeedController',
'(?P<page>questions)/' => 'PonderFeedController',
'(?P<page>answers)/' => 'PonderFeedController',
'(?:query/(?P<queryKey>[^/]+)/)?' => 'PonderQuestionListController',
'answer/add/' => 'PonderAnswerSaveController',
'answer/preview/' => 'PonderAnswerPreviewController',
'question/ask/' => 'PonderQuestionAskController',

View file

@ -2,30 +2,19 @@
abstract class PonderController extends PhabricatorController {
public function buildStandardPageResponse($view, array $data) {
protected function buildSideNavView() {
$user = $this->getRequest()->getUser();
$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);
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
$response = new AphrontWebpageResponse();
return $response->setContent($page->render());
}
id(new PonderQuestionSearchEngine())
->setViewer($user)
->addNavigationItems($nav->getMenu());
protected function buildSideNavView(PonderQuestion $question = null) {
$side_nav = new AphrontSideNavFilterView();
$side_nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
$nav->selectFilter(null);
$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'));
return $side_nav;
return $nav;
}
public function buildApplicationCrumbs() {
@ -33,8 +22,8 @@ abstract class PonderController extends PhabricatorController {
$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;

View file

@ -1,119 +0,0 @@
<?php
final class PonderFeedController extends PonderController {
private $page;
private $answerOffset;
const PROFILE_ANSWER_PAGE_SIZE = 10;
public function willProcessRequest(array $data) {
$this->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;
}
}

View file

@ -52,8 +52,6 @@ final class PonderQuestionAskController extends PonderController {
->setErrors($errors);
}
$header = id(new PhabricatorHeaderView())->setHeader(pht('Ask Question'));
$form = id(new AphrontFormView())
->setUser($user)
->setFlexible(true)
@ -72,7 +70,8 @@ final class PonderQuestionAskController extends PonderController {
->setUser($user))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Ask Away!')));
->addCancelButton($this->getApplicationURI())
->setValue(pht('Ask Away!')));
$preview = hsprintf(
'<div class="aphront-panel-flush">'.
@ -91,19 +90,18 @@ final class PonderQuestionAskController extends PonderController {
'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,

View file

@ -0,0 +1,65 @@
<?php
final class PonderQuestionListController extends PonderController
implements PhabricatorApplicationSearchResultsControllerInterface {
private $queryKey;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->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;
}
}

View file

@ -36,10 +36,7 @@ final class PonderQuestionViewController extends PonderController {
}
}
$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 @@ final class PonderQuestionViewController extends PonderController {
->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 @@ final class PonderQuestionViewController extends PonderController {
->setName('Q'.$this->questionID)
->setHref('/Q'.$this->questionID));
$nav = $this->buildSideNavView($question);
$nav->appendChild(
return $this->buildApplicationPage(
array(
$crumbs,
$header,
@ -86,12 +82,7 @@ final class PonderQuestionViewController extends PonderController {
$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 @@ final class PonderQuestionViewController extends PonderController {
}
private function buildPropertyListView(
PonderQuestion $question,
array $subscribers) {
PonderQuestion $question) {
$viewer = $this->getRequest()->getUser();
$view = id(new PhabricatorPropertyListView())
@ -123,14 +113,6 @@ final class PonderQuestionViewController extends PonderController {
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;
}
}

View file

@ -27,39 +27,6 @@ final class PonderAnswerQuery extends PhabricatorOffsetPagedQuery {
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");

View file

@ -9,6 +9,7 @@ final class PonderQuestionQuery
private $ids;
private $phids;
private $authorPHIDs;
private $answererPHIDs;
private $order = self::ORDER_CREATED;
public function withIDs(array $ids) {
@ -26,6 +27,11 @@ final class PonderQuestionQuery
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 @@ final class PonderQuestionQuery
$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 @@ final class PonderQuestionQuery
$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);
}
}

View file

@ -0,0 +1,105 @@
<?php
final class PonderQuestionSearchEngine
extends PhabricatorApplicationSearchEngine {
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->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);
}
}