diff --git a/src/applications/pholio/constants/PholioTransactionType.php b/src/applications/pholio/constants/PholioTransactionType.php index f2227a0401..561e813b66 100644 --- a/src/applications/pholio/constants/PholioTransactionType.php +++ b/src/applications/pholio/constants/PholioTransactionType.php @@ -1,8 +1,9 @@ id = $data['id']; } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); if (!$request->isFormPost()) { return new Aphront400Response(); } $mock = id(new PholioMockQuery()) ->setViewer($user) ->withIDs(array($this->id)) + ->needImages(true) ->executeOne(); if (!$mock) { return new Aphront404Response(); } $is_preview = $request->isPreviewRequest(); $draft = PhabricatorDraft::buildFromRequest($request); $mock_uri = '/M'.$mock->getID(); $comment = $request->getStr('comment'); $content_source = PhabricatorContentSource::newForSource( PhabricatorContentSource::SOURCE_WEB, array( 'ip' => $request->getRemoteAddr(), )); $xactions = array(); $xactions[] = id(new PholioTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) ->attachComment( id(new PholioTransactionComment()) ->setContent($comment)); + $inlineComments = id(new PholioTransactionComment())->loadAllWhere( + 'authorphid = %s AND transactionphid IS NULL AND imageid IN (%Ld)', + $user->getPHID(), + mpull($mock->getImages(), 'getID') + ); + + foreach ($inlineComments as $inlineComment) { + $xactions[] = id(new PholioTransaction()) + ->setTransactionType(PholioTransactionType::TYPE_INLINE) + ->attachComment($inlineComment); + } + $editor = id(new PholioMockEditor()) ->setActor($user) ->setContentSource($content_source) ->setContinueOnNoEffect($request->isContinueRequest()) ->setIsPreview($is_preview); try { $xactions = $editor->applyTransactions($mock, $xactions); } catch (PhabricatorApplicationTransactionNoEffectException $ex) { return id(new PhabricatorApplicationTransactionNoEffectResponse()) ->setCancelURI($mock_uri) ->setException($ex); } if ($draft) { $draft->replaceOrDelete(); } if ($request->isAjax()) { return id(new PhabricatorApplicationTransactionResponse()) ->setViewer($user) ->setTransactions($xactions) ->setIsPreview($is_preview) ->setAnchorOffset($request->getStr('anchor')); } else { return id(new AphrontRedirectResponse())->setURI($mock_uri); } } } diff --git a/src/applications/pholio/editor/PholioMockEditor.php b/src/applications/pholio/editor/PholioMockEditor.php index 36ab5a847a..51502bb593 100644 --- a/src/applications/pholio/editor/PholioMockEditor.php +++ b/src/applications/pholio/editor/PholioMockEditor.php @@ -1,130 +1,143 @@ getTransactionType()) { case PholioTransactionType::TYPE_NAME: return $object->getName(); case PholioTransactionType::TYPE_DESCRIPTION: return $object->getDescription(); } } protected function getCustomTransactionNewValue( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PholioTransactionType::TYPE_NAME: case PholioTransactionType::TYPE_DESCRIPTION: return $xaction->getNewValue(); } } + protected function transactionHasEffect( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case PholioTransactionType::TYPE_INLINE: + return true; + } + + return parent::transactionHasEffect($object, $xaction); + } + protected function applyCustomInternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PholioTransactionType::TYPE_NAME: $object->setName($xaction->getNewValue()); if ($object->getOriginalName() === null) { $object->setOriginalName($xaction->getNewValue()); } break; case PholioTransactionType::TYPE_DESCRIPTION: $object->setDescription($xaction->getNewValue()); break; } } protected function applyCustomExternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { return; } protected function mergeTransactions( PhabricatorApplicationTransaction $u, PhabricatorApplicationTransaction $v) { $type = $u->getTransactionType(); switch ($type) { case PholioTransactionType::TYPE_NAME: case PholioTransactionType::TYPE_DESCRIPTION: return $v; } return parent::mergeTransactions($u, $v); } protected function supportsMail() { return true; } protected function buildReplyHandler(PhabricatorLiskDAO $object) { return id(new PholioReplyHandler()) ->setMailReceiver($object); } protected function buildMailTemplate(PhabricatorLiskDAO $object) { $id = $object->getID(); $name = $object->getName(); $original_name = $object->getOriginalName(); return id(new PhabricatorMetaMTAMail()) ->setSubject("M{$id}: {$name}") ->addHeader('Thread-Topic', "M{$id}: {$original_name}"); } protected function getMailTo(PhabricatorLiskDAO $object) { return array( $object->getAuthorPHID(), $this->requireActor()->getPHID(), ); } protected function buildMailBody( PhabricatorLiskDAO $object, array $xactions) { $body = parent::buildMailBody($object, $xactions); $body->addTextSection( pht('MOCK DETAIL'), PhabricatorEnv::getProductionURI('/M'.$object->getID())); return $body; } protected function getMailSubjectPrefix() { return PhabricatorEnv::getEnvConfig('metamta.pholio.subject-prefix'); } protected function supportsFeed() { return true; } protected function supportsSearch() { return true; } } diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionCommentEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionCommentEditor.php index fe0680a121..fc35653e6b 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionCommentEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionCommentEditor.php @@ -1,102 +1,105 @@ contentSource = $content_source; return $this; } public function getContentSource() { return $this->contentSource; } /** * Edit a transaction's comment. This method effects the required create, * update or delete to set the transaction's comment to the provided comment. */ public function applyEdit( PhabricatorApplicationTransaction $xaction, PhabricatorApplicationTransactionComment $comment) { $this->validateEdit($xaction, $comment); $actor = $this->requireActor(); $comment->setContentSource($this->getContentSource()); $comment->setAuthorPHID($actor->getPHID()); // TODO: This needs to be more sophisticated once we have meta-policies. $comment->setViewPolicy(PhabricatorPolicies::POLICY_PUBLIC); $comment->setEditPolicy($actor->getPHID()); $xaction->openTransaction(); $xaction->beginReadLocking(); if ($xaction->getID()) { $xaction->reload(); } $new_version = $xaction->getCommentVersion() + 1; $comment->setCommentVersion($new_version); $comment->setTransactionPHID($xaction->getPHID()); $comment->save(); $xaction->setCommentVersion($new_version); $xaction->setCommentPHID($comment->getPHID()); $xaction->setViewPolicy($comment->getViewPolicy()); $xaction->setEditPolicy($comment->getEditPolicy()); $xaction->save(); $xaction->endReadLocking(); $xaction->saveTransaction(); $xaction->attachComment($comment); // TODO: Emit an event for notifications/feed? Can we handle them // generically? return $this; } /** * Validate that the edit is permissible, and the actor has permission to * perform it. */ private function validateEdit( PhabricatorApplicationTransaction $xaction, PhabricatorApplicationTransactionComment $comment) { if (!$xaction->getPHID()) { throw new Exception( "Transaction must have a PHID before calling applyEdit()!"); } - if ($comment->getPHID()) { - throw new Exception( + $type_comment = PhabricatorTransactions::TYPE_COMMENT; + if ($xaction->getTransactionType() == $type_comment) { + if ($comment->getPHID()) { + throw new Exception( "Transaction comment must not yet have a PHID!"); + } } if (!$this->getContentSource()) { throw new Exception( "Call setContentSource() before applyEdit()!"); } $actor = $this->requireActor(); PhabricatorPolicyFilter::requireCapability( $actor, $xaction, PhabricatorPolicyCapability::CAN_VIEW); PhabricatorPolicyFilter::requireCapability( $actor, $xaction, PhabricatorPolicyCapability::CAN_EDIT); } }