diff --git a/src/applications/ponder/controller/PonderQuestionEditController.php b/src/applications/ponder/controller/PonderQuestionEditController.php index 293bbe3c79..71b9615cca 100644 --- a/src/applications/ponder/controller/PonderQuestionEditController.php +++ b/src/applications/ponder/controller/PonderQuestionEditController.php @@ -1,180 +1,186 @@ getViewer(); $id = $request->getURIData('id'); if ($id) { $question = id(new PonderQuestionQuery()) ->setViewer($viewer) ->withIDs(array($id)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); if (!$question) { return new Aphront404Response(); } $v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs( $question->getPHID(), PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); $v_projects = array_reverse($v_projects); + $is_new = false; } else { + $is_new = true; $question = PonderQuestion::initializeNewQuestion($viewer); $v_projects = array(); } $v_title = $question->getTitle(); $v_content = $question->getContent(); $v_view = $question->getViewPolicy(); $v_space = $question->getSpacePHID(); $v_status = $question->getStatus(); $errors = array(); $e_title = true; if ($request->isFormPost()) { $v_title = $request->getStr('title'); $v_content = $request->getStr('content'); $v_projects = $request->getArr('projects'); $v_view = $request->getStr('viewPolicy'); $v_space = $request->getStr('spacePHID'); $v_status = $request->getStr('status'); $len = phutil_utf8_strlen($v_title); if ($len < 1) { $errors[] = pht('Title must not be empty.'); $e_title = pht('Required'); } else if ($len > 255) { $errors[] = pht('Title is too long.'); $e_title = pht('Too Long'); } if (!$errors) { $template = id(new PonderQuestionTransaction()); $xactions = array(); $xactions[] = id(clone $template) ->setTransactionType(PonderQuestionTransaction::TYPE_TITLE) ->setNewValue($v_title); $xactions[] = id(clone $template) ->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); $xactions[] = id(clone $template) ->setTransactionType(PhabricatorTransactions::TYPE_SPACE) ->setNewValue($v_space); $proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; $xactions[] = id(new PonderQuestionTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) ->setMetadataValue('edge:type', $proj_edge_type) ->setNewValue(array('=' => array_fuse($v_projects))); $editor = id(new PonderQuestionEditor()) ->setActor($viewer) ->setContentSourceFromRequest($request) ->setContinueOnNoEffect(true); $editor->applyTransactions($question, $xactions); return id(new AphrontRedirectResponse()) ->setURI('/Q'.$question->getID()); } } $policies = id(new PhabricatorPolicyQuery()) ->setViewer($viewer) ->setObject($question) ->execute(); $form = id(new AphrontFormView()) ->setUser($viewer) ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('Question')) ->setName('title') ->setValue($v_title) ->setError($e_title)) ->appendChild( id(new PhabricatorRemarkupControl()) ->setUser($viewer) ->setName('content') ->setID('content') ->setValue($v_content) ->setLabel(pht('Description')) ->setUser($viewer)) ->appendControl( id(new AphrontFormPolicyControl()) ->setName('viewPolicy') ->setPolicyObject($question) ->setSpacePHID($v_space) ->setPolicies($policies) ->setValue($v_view) - ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Status')) - ->setName('status') - ->setValue($v_status) - ->setOptions(PonderQuestionStatus::getQuestionStatusMap())); + ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)); + + + if (!$is_new) { + $form->appendChild( + id(new AphrontFormSelectControl()) + ->setLabel(pht('Status')) + ->setName('status') + ->setValue($v_status) + ->setOptions(PonderQuestionStatus::getQuestionStatusMap())); + } $form->appendControl( id(new AphrontFormTokenizerControl()) ->setLabel(pht('Projects')) ->setName('projects') ->setValue($v_projects) ->setDatasource(new PhabricatorProjectDatasource())); $form->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($this->getApplicationURI()) - ->setValue(pht('Ask Away!'))); + ->setValue(pht('Submit'))); $preview = id(new PHUIRemarkupPreviewPanel()) ->setHeader(pht('Question Preview')) ->setControlID('content') ->setPreviewURI($this->getApplicationURI('preview/')); $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, $form_box, $preview, ), array( 'title' => $title, )); } } diff --git a/src/applications/ponder/controller/PonderQuestionViewController.php b/src/applications/ponder/controller/PonderQuestionViewController.php index 2f0ae5099f..a1e526648d 100644 --- a/src/applications/ponder/controller/PonderQuestionViewController.php +++ b/src/applications/ponder/controller/PonderQuestionViewController.php @@ -1,279 +1,282 @@ getViewer(); $id = $request->getURIData('id'); $question = id(new PonderQuestionQuery()) ->setViewer($viewer) ->withIDs(array($id)) ->needAnswers(true) ->needProjectPHIDs(true) ->executeOne(); if (!$question) { return new Aphront404Response(); } $answers = $this->buildAnswers($question->getAnswers()); $answer_add_panel = id(new PonderAddAnswerView()) ->setQuestion($question) ->setUser($viewer) ->setActionURI('/ponder/answer/add/'); $header = new PHUIHeaderView(); $header->setHeader($question->getTitle()); $header->setUser($viewer); $header->setPolicyObject($question); if ($question->getStatus() == PonderQuestionStatus::STATUS_OPEN) { $header->setStatus('fa-square-o', 'bluegrey', pht('Open')); } else { $text = PonderQuestionStatus::getQuestionStatusFullName( $question->getStatus()); $icon = PonderQuestionStatus::getQuestionStatusIcon( $question->getStatus()); $header->setStatus($icon, 'dark', $text); } $actions = $this->buildActionListView($question); $properties = $this->buildPropertyListView($question, $actions); $sidebar = $this->buildSidebar($question); $content_id = celerity_generate_unique_node_id(); $timeline = $this->buildTransactionTimeline( $question, id(new PonderQuestionTransactionQuery()) ->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT))); $xactions = $timeline->getTransactions(); $add_comment = id(new PhabricatorApplicationTransactionCommentView()) ->setUser($viewer) ->setObjectPHID($question->getPHID()) ->setShowPreview(false) ->setHeaderText(pht('Question Comment')) ->setAction($this->getApplicationURI("/question/comment/{$id}/")) ->setSubmitButtonName(pht('Comment')); $comment_view = phutil_tag( 'div', array( 'id' => $content_id, 'style' => 'display: none;', ), array( $timeline, $add_comment, )); $footer = id(new PonderFooterView()) ->setContentID($content_id) ->setCount(count($xactions)); $object_box = id(new PHUIObjectBoxView()) ->setHeader($header) ->addPropertyList($properties) ->appendChild($footer); $crumbs = $this->buildApplicationCrumbs($this->buildSideNavView()); $crumbs->addTextCrumb('Q'.$id, '/Q'.$id); $ponder_view = id(new PHUITwoColumnView()) ->setMainColumn(array( $object_box, $comment_view, $answers, $answer_add_panel, )) ->setSideColumn($sidebar) ->addClass('ponder-question-view'); return $this->buildApplicationPage( array( $crumbs, $ponder_view, ), array( 'title' => 'Q'.$question->getID().' '.$question->getTitle(), 'pageObjects' => array_merge( array($question->getPHID()), mpull($question->getAnswers(), 'getPHID')), )); } private function buildActionListView(PonderQuestion $question) { $viewer = $this->getViewer(); $request = $this->getRequest(); $id = $question->getID(); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $question, PhabricatorPolicyCapability::CAN_EDIT); $view = id(new PhabricatorActionListView()) ->setUser($viewer) ->setObject($question) ->setObjectURI($request->getRequestURI()); $view->addAction( id(new PhabricatorActionView()) ->setIcon('fa-pencil') ->setName(pht('Edit Question')) ->setHref($this->getApplicationURI("/question/edit/{$id}/")) ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); if ($question->getStatus() == PonderQuestionStatus::STATUS_OPEN) { $name = pht('Close Question'); $icon = 'fa-check-square-o'; } else { $name = pht('Reopen Question'); $icon = 'fa-square-o'; } $view->addAction( id(new PhabricatorActionView()) ->setName($name) ->setIcon($icon) ->setWorkflow(true) ->setDisabled(!$can_edit) ->setHref($this->getApplicationURI("/question/status/{$id}/"))); $view->addAction( id(new PhabricatorActionView()) ->setIcon('fa-list') ->setName(pht('View History')) ->setHref($this->getApplicationURI("/question/history/{$id}/"))); return $view; } private function buildPropertyListView( PonderQuestion $question, PhabricatorActionListView $actions) { $viewer = $this->getViewer(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setObject($question) ->setActionList($actions); $view->addProperty( pht('Author'), $viewer->renderHandle($question->getAuthorPHID())); $view->addProperty( pht('Created'), phabricator_datetime($question->getDateCreated(), $viewer)); $view->invokeWillRenderEvent(); - $view->addSectionHeader(pht('Question')); + $view->addSectionHeader( + pht('Details'), + PHUIPropertyListView::ICON_SUMMARY); + $view->addTextContent( array( phutil_tag( 'div', array( 'class' => 'phabricator-remarkup', ), PhabricatorMarkupEngine::renderOneObject( $question, $question->getMarkupField(), $viewer)), )); return $view; } /** * This is fairly non-standard; building N timelines at once (N = number of * answers) is tricky business. * * TODO - re-factor this to ajax in one answer panel at a time in a more * standard fashion. This is necessary to scale this application. */ private function buildAnswers(array $answers) { $viewer = $this->getViewer(); $xactions = id(new PonderAnswerTransactionQuery()) ->setViewer($viewer) ->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT)) ->withObjectPHIDs(mpull($answers, 'getPHID')) ->execute(); $engine = id(new PhabricatorMarkupEngine()) ->setViewer($viewer); foreach ($xactions as $xaction) { if ($xaction->getComment()) { $engine->addObject( $xaction->getComment(), PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT); } } $engine->process(); $xaction_groups = mgroup($xactions, 'getObjectPHID'); $view = array(); foreach ($answers as $answer) { $xactions = idx($xaction_groups, $answer->getPHID(), array()); $id = $answer->getID(); $view[] = id(new PonderAnswerView()) ->setUser($viewer) ->setAnswer($answer) ->setTransactions($xactions) ->setMarkupEngine($engine); } return $view; } private function buildSidebar(PonderQuestion $question) { $viewer = $this->getViewer(); $status = $question->getStatus(); $id = $question->getID(); $questions = id(new PonderQuestionQuery()) ->setViewer($viewer) ->withStatuses(array($status)) ->withEdgeLogicPHIDs( PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, PhabricatorQueryConstraint::OPERATOR_OR, $question->getProjectPHIDs()) ->setLimit(10) ->execute(); $list = id(new PHUIObjectItemListView()) ->setUser($viewer) ->setNoDataString(pht('No similar questions found.')); foreach ($questions as $question) { if ($id == $question->getID()) { continue; } $item = new PHUIObjectItemView(); $item->setObjectName('Q'.$question->getID()); $item->setHeader($question->getTitle()); $item->setHref('/Q'.$question->getID()); $item->setObject($question); $item->addAttribute( pht('%d Answer(s)', $question->getAnswerCount())); $list->addItem($item); } $box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Similar Questions')) ->setObjectList($list); return $box; } }