Correct a parameter order swap in "diffusion.historyquery" for Mercurial
[phabricator.git] / src / applications / ponder / controller / PonderQuestionViewController.php
blob2cd555204cdc62243caeb8716b9d428ddd91dc3f
1 <?php
3 final class PonderQuestionViewController extends PonderController {
5 public function shouldAllowPublic() {
6 return true;
9 public function handleRequest(AphrontRequest $request) {
10 $viewer = $request->getViewer();
11 $id = $request->getURIData('id');
13 $question = id(new PonderQuestionQuery())
14 ->setViewer($viewer)
15 ->withIDs(array($id))
16 ->needAnswers(true)
17 ->needProjectPHIDs(true)
18 ->executeOne();
19 if (!$question) {
20 return new Aphront404Response();
23 $answers = $this->buildAnswers($question);
25 $answer_add_panel = id(new PonderAddAnswerView())
26 ->setQuestion($question)
27 ->setUser($viewer)
28 ->setActionURI('/ponder/answer/add/');
30 $header = new PHUIHeaderView();
31 $header->setHeader($question->getTitle());
32 $header->setUser($viewer);
33 $header->setPolicyObject($question);
34 $header->setHeaderIcon('fa-university');
36 if ($question->getStatus() == PonderQuestionStatus::STATUS_OPEN) {
37 $header->setStatus('fa-square-o', 'bluegrey', pht('Open'));
38 } else {
39 $text = PonderQuestionStatus::getQuestionStatusFullName(
40 $question->getStatus());
41 $icon = PonderQuestionStatus::getQuestionStatusIcon(
42 $question->getStatus());
43 $header->setStatus($icon, 'dark', $text);
46 $curtain = $this->buildCurtain($question);
47 $details = $this->buildPropertySectionView($question);
49 $can_edit = PhabricatorPolicyFilter::hasCapability(
50 $viewer,
51 $question,
52 PhabricatorPolicyCapability::CAN_EDIT);
54 $content_id = celerity_generate_unique_node_id();
55 $timeline = $this->buildTransactionTimeline(
56 $question,
57 id(new PonderQuestionTransactionQuery())
58 ->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT)));
59 $xactions = $timeline->getTransactions();
61 $add_comment = id(new PhabricatorApplicationTransactionCommentView())
62 ->setUser($viewer)
63 ->setObjectPHID($question->getPHID())
64 ->setShowPreview(false)
65 ->setAction($this->getApplicationURI("/question/comment/{$id}/"))
66 ->setSubmitButtonName(pht('Comment'));
68 $add_comment = phutil_tag_div(
69 'ponder-question-add-comment-view', $add_comment);
71 $comment_view = phutil_tag(
72 'div',
73 array(
74 'id' => $content_id,
75 'style' => 'display: none;',
77 array(
78 $timeline,
79 $add_comment,
80 ));
82 $footer = id(new PonderFooterView())
83 ->setContentID($content_id)
84 ->setCount(count($xactions));
86 $crumbs = $this->buildApplicationCrumbs();
87 $crumbs->addTextCrumb('Q'.$id, '/Q'.$id);
88 $crumbs->setBorder(true);
90 $subheader = $this->buildSubheaderView($question);
92 $answer_wiki = null;
93 if ($question->getAnswerWiki()) {
94 $wiki = new PHUIRemarkupView($viewer, $question->getAnswerWiki());
95 $answer_wiki = id(new PHUIObjectBoxView())
96 ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
97 ->setHeaderText(pht('Answer Summary'))
98 ->appendChild($wiki)
99 ->addClass('ponder-answer-wiki');
102 require_celerity_resource('ponder-view-css');
104 $ponder_content = phutil_tag(
105 'div',
106 array(
107 'class' => 'ponder-question-content',
109 array(
110 $answer_wiki,
111 $footer,
112 $comment_view,
113 $answers,
114 $answer_add_panel,
117 $ponder_view = id(new PHUITwoColumnView())
118 ->setHeader($header)
119 ->setSubheader($subheader)
120 ->setCurtain($curtain)
121 ->setMainColumn($ponder_content)
122 ->addPropertySection(pht('Details'), $details)
123 ->addClass('ponder-question-view');
125 $page_objects = array_merge(
126 array($question->getPHID()),
127 mpull($question->getAnswers(), 'getPHID'));
129 return $this->newPage()
130 ->setTitle('Q'.$question->getID().' '.$question->getTitle())
131 ->setCrumbs($crumbs)
132 ->setPageObjectPHIDs($page_objects)
133 ->appendChild($ponder_view);
136 private function buildCurtain(PonderQuestion $question) {
137 $viewer = $this->getViewer();
138 $id = $question->getID();
140 $can_edit = PhabricatorPolicyFilter::hasCapability(
141 $viewer,
142 $question,
143 PhabricatorPolicyCapability::CAN_EDIT);
145 $curtain = $this->newCurtainView($question);
147 if ($question->getStatus() == PonderQuestionStatus::STATUS_OPEN) {
148 $name = pht('Close Question');
149 $icon = 'fa-check-square-o';
150 } else {
151 $name = pht('Reopen Question');
152 $icon = 'fa-square-o';
155 $curtain->addAction(
156 id(new PhabricatorActionView())
157 ->setIcon('fa-pencil')
158 ->setName(pht('Edit Question'))
159 ->setHref($this->getApplicationURI("/question/edit/{$id}/"))
160 ->setDisabled(!$can_edit)
161 ->setWorkflow(!$can_edit));
163 $curtain->addAction(
164 id(new PhabricatorActionView())
165 ->setName($name)
166 ->setIcon($icon)
167 ->setWorkflow(true)
168 ->setDisabled(!$can_edit)
169 ->setHref($this->getApplicationURI("/question/status/{$id}/")));
171 $curtain->addAction(
172 id(new PhabricatorActionView())
173 ->setIcon('fa-list')
174 ->setName(pht('View History'))
175 ->setHref($this->getApplicationURI("/question/history/{$id}/")));
177 return $curtain;
180 private function buildSubheaderView(
181 PonderQuestion $question) {
182 $viewer = $this->getViewer();
184 $asker = $viewer->renderHandle($question->getAuthorPHID())->render();
185 $date = phabricator_datetime($question->getDateCreated(), $viewer);
186 $asker = phutil_tag('strong', array(), $asker);
188 $author = id(new PhabricatorPeopleQuery())
189 ->setViewer($viewer)
190 ->withPHIDs(array($question->getAuthorPHID()))
191 ->needProfileImage(true)
192 ->executeOne();
194 $image_uri = $author->getProfileImageURI();
195 $image_href = '/p/'.$author->getUsername();
197 $content = pht('Asked by %s on %s.', $asker, $date);
199 return id(new PHUIHeadThingView())
200 ->setImage($image_uri)
201 ->setImageHref($image_href)
202 ->setContent($content);
205 private function buildPropertySectionView(
206 PonderQuestion $question) {
207 $viewer = $this->getViewer();
209 $question_details = PhabricatorMarkupEngine::renderOneObject(
210 $question,
211 $question->getMarkupField(),
212 $viewer);
214 if (!$question_details) {
215 $question_details = phutil_tag(
216 'em',
217 array(),
218 pht('No further details for this question.'));
221 $question_details = phutil_tag_div(
222 'phabricator-remarkup ml', $question_details);
224 return $question_details;
228 * This is fairly non-standard; building N timelines at once (N = number of
229 * answers) is tricky business.
231 * TODO - re-factor this to ajax in one answer panel at a time in a more
232 * standard fashion. This is necessary to scale this application.
234 private function buildAnswers(PonderQuestion $question) {
235 $viewer = $this->getViewer();
236 $answers = $question->getAnswers();
238 if ($answers) {
239 $author_phids = mpull($answers, 'getAuthorPHID');
240 $handles = $this->loadViewerHandles($author_phids);
242 $view = array();
243 foreach ($answers as $answer) {
244 $id = $answer->getID();
245 $handle = $handles[$answer->getAuthorPHID()];
247 $timeline = $this->buildTransactionTimeline(
248 $answer,
249 id(new PonderAnswerTransactionQuery())
250 ->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT)));
251 $xactions = $timeline->getTransactions();
253 $view[] = id(new PonderAnswerView())
254 ->setUser($viewer)
255 ->setAnswer($answer)
256 ->setTransactions($xactions)
257 ->setTimeline($timeline)
258 ->setHandle($handle);
262 $header = id(new PHUIHeaderView())
263 ->setHeader('Answers');
266 return id(new PHUIBoxView())
267 ->addClass('ponder-answer-section')
268 ->appendChild($header)
269 ->appendChild($view);
272 return null;