Add previews to ApplicationTransaction

Summary:
Implements previews for Macros and Pholio.

(Design is nonfinal -- kind of split the difference between `diff_full_view.png`, laziness, and space concerns. Next couple diffs will add more stuff here.)

Test Plan: {F28055}

Reviewers: btrahan, chad

Reviewed By: btrahan

CC: aran, vrana

Maniphest Tasks: T2104

Differential Revision: https://secure.phabricator.com/D4246
This commit is contained in:
epriestley 2012-12-21 05:51:33 -08:00
parent 4af2e3c4e2
commit 0fd77783a4
15 changed files with 391 additions and 137 deletions

View file

@ -1676,6 +1676,19 @@ celerity_register_resource_map(array(
), ),
'disk' => '/rsrc/js/application/core/behavior-tooltip.js', 'disk' => '/rsrc/js/application/core/behavior-tooltip.js',
), ),
'javelin-behavior-phabricator-transaction-comment-form' =>
array(
'uri' => '/res/bdc362ee/rsrc/js/application/transactions/behavior-transaction-comment-form.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
2 => 'javelin-util',
3 => 'phabricator-shaped-request',
),
'disk' => '/rsrc/js/application/transactions/behavior-transaction-comment-form.js',
),
'javelin-behavior-phabricator-transaction-list' => 'javelin-behavior-phabricator-transaction-list' =>
array( array(
'uri' => '/res/212f97ba/rsrc/js/application/transactions/behavior-transaction-list.js', 'uri' => '/res/212f97ba/rsrc/js/application/transactions/behavior-transaction-list.js',
@ -2847,7 +2860,7 @@ celerity_register_resource_map(array(
), ),
'phabricator-timeline-view-css' => 'phabricator-timeline-view-css' =>
array( array(
'uri' => '/res/1846f7d5/rsrc/css/layout/phabricator-timeline-view.css', 'uri' => '/res/9cbeb7f0/rsrc/css/layout/phabricator-timeline-view.css',
'type' => 'css', 'type' => 'css',
'requires' => 'requires' =>
array( array(

View file

@ -613,6 +613,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionCommentEditor' => 'applications/transactions/editor/PhabricatorApplicationTransactionCommentEditor.php', 'PhabricatorApplicationTransactionCommentEditor' => 'applications/transactions/editor/PhabricatorApplicationTransactionCommentEditor.php',
'PhabricatorApplicationTransactionCommentHistoryController' => 'applications/transactions/controller/PhabricatorApplicationTransactionCommentHistoryController.php', 'PhabricatorApplicationTransactionCommentHistoryController' => 'applications/transactions/controller/PhabricatorApplicationTransactionCommentHistoryController.php',
'PhabricatorApplicationTransactionCommentQuery' => 'applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php', 'PhabricatorApplicationTransactionCommentQuery' => 'applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php',
'PhabricatorApplicationTransactionCommentView' => 'applications/transactions/view/PhabricatorApplicationTransactionCommentView.php',
'PhabricatorApplicationTransactionController' => 'applications/transactions/controller/PhabricatorApplicationTransactionController.php', 'PhabricatorApplicationTransactionController' => 'applications/transactions/controller/PhabricatorApplicationTransactionController.php',
'PhabricatorApplicationTransactionEditor' => 'applications/transactions/editor/PhabricatorApplicationTransactionEditor.php', 'PhabricatorApplicationTransactionEditor' => 'applications/transactions/editor/PhabricatorApplicationTransactionEditor.php',
'PhabricatorApplicationTransactionFeedStory' => 'applications/transactions/feed/PhabricatorApplicationTransactionFeedStory.php', 'PhabricatorApplicationTransactionFeedStory' => 'applications/transactions/feed/PhabricatorApplicationTransactionFeedStory.php',
@ -1904,6 +1905,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionCommentEditor' => 'PhabricatorEditor', 'PhabricatorApplicationTransactionCommentEditor' => 'PhabricatorEditor',
'PhabricatorApplicationTransactionCommentHistoryController' => 'PhabricatorApplicationTransactionController', 'PhabricatorApplicationTransactionCommentHistoryController' => 'PhabricatorApplicationTransactionController',
'PhabricatorApplicationTransactionCommentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorApplicationTransactionCommentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorApplicationTransactionCommentView' => 'AphrontView',
'PhabricatorApplicationTransactionController' => 'PhabricatorController', 'PhabricatorApplicationTransactionController' => 'PhabricatorController',
'PhabricatorApplicationTransactionEditor' => 'PhabricatorEditor', 'PhabricatorApplicationTransactionEditor' => 'PhabricatorEditor',
'PhabricatorApplicationTransactionFeedStory' => 'PhabricatorFeedStory', 'PhabricatorApplicationTransactionFeedStory' => 'PhabricatorFeedStory',

View file

@ -18,6 +18,7 @@ final class AphrontRequest {
const TYPE_CONDUIT = '__conduit__'; const TYPE_CONDUIT = '__conduit__';
const TYPE_WORKFLOW = '__wflow__'; const TYPE_WORKFLOW = '__wflow__';
const TYPE_CONTINUE = '__continue__'; const TYPE_CONTINUE = '__continue__';
const TYPE_PREVIEW = '__preview__';
private $host; private $host;
private $path; private $path;
@ -339,6 +340,10 @@ final class AphrontRequest {
return $this->isFormPost() && $this->getStr('__continue__'); return $this->isFormPost() && $this->getStr('__continue__');
} }
public function isPreviewRequest() {
return $this->isFormPost() && $this->getStr('__preview__');
}
/** /**
* Get application request parameters in a flattened form suitable for * Get application request parameters in a flattened form suitable for
* inclusion in an HTTP request, excluding parameters with special meanings. * inclusion in an HTTP request, excluding parameters with special meanings.

View file

@ -22,10 +22,11 @@ final class PhabricatorMacroCommentController
return new Aphront404Response(); return new Aphront404Response();
} }
$is_preview = $request->isPreviewRequest();
$view_uri = $this->getApplicationURI('/view/'.$macro->getID().'/'); $view_uri = $this->getApplicationURI('/view/'.$macro->getID().'/');
$xactions = array(); $xactions = array();
$xactions[] = id(new PhabricatorMacroTransaction()) $xactions[] = id(new PhabricatorMacroTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
->attachComment( ->attachComment(
@ -40,7 +41,8 @@ final class PhabricatorMacroCommentController
PhabricatorContentSource::SOURCE_WEB, PhabricatorContentSource::SOURCE_WEB,
array( array(
'ip' => $request->getRemoteAddr(), 'ip' => $request->getRemoteAddr(),
))); )))
->setIsPreview($is_preview);
try { try {
$xactions = $editor->applyTransactions($macro, $xactions); $xactions = $editor->applyTransactions($macro, $xactions);
@ -54,6 +56,7 @@ final class PhabricatorMacroCommentController
return id(new PhabricatorApplicationTransactionResponse()) return id(new PhabricatorApplicationTransactionResponse())
->setViewer($user) ->setViewer($user)
->setTransactions($xactions) ->setTransactions($xactions)
->setIsPreview($is_preview)
->setAnchorOffset($request->getStr('anchor')); ->setAnchorOffset($request->getStr('anchor'));
} else { } else {
return id(new AphrontRedirectResponse()) return id(new AphrontRedirectResponse())

View file

@ -79,23 +79,14 @@ final class PhabricatorMacroViewController
? pht('Add Comment') ? pht('Add Comment')
: pht('Grovel in Awe')); : pht('Grovel in Awe'));
$add_comment_form = id(new AphrontFormView()) $submit_button_name = $is_serious
->setWorkflow(true) ? pht('Add Comment')
->setFlexible(true) : pht('Lavish Praise');
->addSigil('transaction-append')
->setAction($this->getApplicationURI('/comment/'.$macro->getID().'/')) $add_comment_form = id(new PhabricatorApplicationTransactionCommentView())
->setUser($user) ->setUser($user)
->appendChild( ->setAction($this->getApplicationURI('/comment/'.$macro->getID().'/'))
id(new PhabricatorRemarkupControl()) ->setSubmitButtonName($submit_button_name);
->setUser($user)
->setLabel('Comment')
->setName('comment'))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(
$is_serious
? pht('Add Comment')
: pht('Lavish Praise')));
return $this->buildApplicationPage( return $this->buildApplicationPage(
array( array(

View file

@ -15,6 +15,10 @@ final class PholioMockCommentController extends PholioController {
$request = $this->getRequest(); $request = $this->getRequest();
$user = $request->getUser(); $user = $request->getUser();
if (!$request->isFormPost()) {
return new Aphront400Response();
}
$mock = id(new PholioMockQuery()) $mock = id(new PholioMockQuery())
->setViewer($user) ->setViewer($user)
->withIDs(array($this->id)) ->withIDs(array($this->id))
@ -24,6 +28,8 @@ final class PholioMockCommentController extends PholioController {
return new Aphront404Response(); return new Aphront404Response();
} }
$is_preview = $request->isPreviewRequest();
$mock_uri = '/M'.$mock->getID(); $mock_uri = '/M'.$mock->getID();
$comment = $request->getStr('comment'); $comment = $request->getStr('comment');
@ -44,7 +50,8 @@ final class PholioMockCommentController extends PholioController {
$editor = id(new PholioMockEditor()) $editor = id(new PholioMockEditor())
->setActor($user) ->setActor($user)
->setContentSource($content_source) ->setContentSource($content_source)
->setContinueOnException($request->isContinueRequest()); ->setContinueOnNoEffect($request->isContinueRequest())
->setIsPreview($is_preview);
try { try {
$xactions = $editor->applyTransactions($mock, $xactions); $xactions = $editor->applyTransactions($mock, $xactions);
@ -58,6 +65,7 @@ final class PholioMockCommentController extends PholioController {
return id(new PhabricatorApplicationTransactionResponse()) return id(new PhabricatorApplicationTransactionResponse())
->setViewer($user) ->setViewer($user)
->setTransactions($xactions) ->setTransactions($xactions)
->setIsPreview($is_preview)
->setAnchorOffset($request->getStr('anchor')); ->setAnchorOffset($request->getStr('anchor'));
} else { } else {
return id(new AphrontRedirectResponse())->setURI($mock_uri); return id(new AphrontRedirectResponse())->setURI($mock_uri);

View file

@ -120,7 +120,7 @@ final class PholioMockEditController extends PholioController {
$mock->openTransaction(); $mock->openTransaction();
$editor = id(new PholioMockEditor()) $editor = id(new PholioMockEditor())
->setContentSource($content_source) ->setContentSource($content_source)
->setContinueOnException(true) ->setContinueOnNoEffect(true)
->setActor($user); ->setActor($user);
$xactions = $editor->applyTransactions($mock, $xactions); $xactions = $editor->applyTransactions($mock, $xactions);

View file

@ -65,7 +65,7 @@ final class PholioMockViewController extends PholioController {
'Carousel Goes Here</h1>'; 'Carousel Goes Here</h1>';
$xaction_view = id(new PhabricatorApplicationTransactionView()) $xaction_view = id(new PhabricatorApplicationTransactionView())
->setViewer($this->getRequest()->getUser()) ->setUser($this->getRequest()->getUser())
->setTransactions($xactions) ->setTransactions($xactions)
->setMarkupEngine($engine); ->setMarkupEngine($engine);
@ -168,24 +168,14 @@ final class PholioMockViewController extends PholioController {
$header = id(new PhabricatorHeaderView()) $header = id(new PhabricatorHeaderView())
->setHeader($title); ->setHeader($title);
$action = $is_serious $button_name = $is_serious
? pht('Add Comment') ? pht('Add Comment')
: pht('Answer The Call'); : pht('Answer The Call');
$form = id(new AphrontFormView()) $form = id(new PhabricatorApplicationTransactionCommentView())
->setUser($user) ->setUser($user)
->addSigil('transaction-append') ->setSubmitButtonName($button_name)
->setAction($this->getApplicationURI('/comment/'.$mock->getID().'/')) ->setAction($this->getApplicationURI('/comment/'.$mock->getID().'/'));
->setWorkflow(true)
->setFlexible(true)
->appendChild(
id(new PhabricatorRemarkupControl())
->setName('comment')
->setLabel(pht('Comment'))
->setUser($user))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue($action));
return array( return array(
$header, $header,

View file

@ -15,6 +15,7 @@ abstract class PhabricatorApplicationTransactionEditor
private $mentionedPHIDs; private $mentionedPHIDs;
private $continueOnNoEffect; private $continueOnNoEffect;
private $isPreview;
/** /**
* When the editor tries to apply transactions that have no effect, should * When the editor tries to apply transactions that have no effect, should
@ -46,6 +47,15 @@ abstract class PhabricatorApplicationTransactionEditor
return $this->mentionedPHIDs; return $this->mentionedPHIDs;
} }
public function setIsPreview($is_preview) {
$this->isPreview = $is_preview;
return $this;
}
public function getIsPreview() {
return $this->isPreview;
}
public function getTransactionTypes() { public function getTransactionTypes() {
$types = array( $types = array(
PhabricatorTransactions::TYPE_COMMENT, PhabricatorTransactions::TYPE_COMMENT,
@ -225,11 +235,16 @@ abstract class PhabricatorApplicationTransactionEditor
$xactions = $this->filterTransactions($object, $xactions); $xactions = $this->filterTransactions($object, $xactions);
if (!$xactions) { if (!$xactions) {
return $this; return array();
} }
$xactions = $this->sortTransactions($xactions); $xactions = $this->sortTransactions($xactions);
if ($this->getIsPreview()) {
$this->loadHandles($xactions);
return $xactions;
}
$comment_editor = id(new PhabricatorApplicationTransactionCommentEditor()) $comment_editor = id(new PhabricatorApplicationTransactionCommentEditor())
->setActor($actor) ->setActor($actor)
->setContentSource($this->getContentSource()); ->setContentSource($this->getContentSource());
@ -256,10 +271,8 @@ abstract class PhabricatorApplicationTransactionEditor
} }
$object->saveTransaction(); $object->saveTransaction();
$this->loadHandles($xactions); $this->loadHandles($xactions);
$mail = null; $mail = null;
if ($this->supportsMail()) { if ($this->supportsMail()) {
$mail = $this->sendMail($object, $xactions); $mail = $this->sendMail($object, $xactions);
@ -285,8 +298,8 @@ abstract class PhabricatorApplicationTransactionEditor
private function loadHandles(array $xactions) { private function loadHandles(array $xactions) {
$phids = array(); $phids = array();
foreach ($xactions as $xaction) { foreach ($xactions as $key => $xaction) {
$phids[$xaction->getPHID()] = $xaction->getRequiredHandlePHIDs(); $phids[$key] = $xaction->getRequiredHandlePHIDs();
} }
$handles = array(); $handles = array();
$merged = array_mergev($phids); $merged = array_mergev($phids);
@ -295,11 +308,8 @@ abstract class PhabricatorApplicationTransactionEditor
->setViewer($this->requireActor()) ->setViewer($this->requireActor())
->loadHandles(); ->loadHandles();
} }
foreach ($xactions as $xaction) { foreach ($xactions as $key => $xaction) {
$xaction->setHandles( $xaction->setHandles(array_select_keys($handles, $phids[$key]));
array_select_keys(
$handles,
$phids[$xaction->getPHID()]));
} }
} }
@ -587,13 +597,18 @@ abstract class PhabricatorApplicationTransactionEditor
return $xactions; return $xactions;
} }
if (!$this->getContinueOnNoEffect()) { if (!$this->getContinueOnNoEffect() && !$this->getIsPreview()) {
throw new PhabricatorApplicationTransactionNoEffectException( throw new PhabricatorApplicationTransactionNoEffectException(
$no_effect, $no_effect,
$any_effect, $any_effect,
$has_comment); $has_comment);
} }
if (!$any_effect && !$has_comment) {
// If we only have empty comment transactions, just drop them all.
return array();
}
foreach ($no_effect as $key => $xaction) { foreach ($no_effect as $key => $xaction) {
if ($xaction->getComment()) { if ($xaction->getComment()) {
$xaction->setTransactionType($type_comment); $xaction->setTransactionType($type_comment);

View file

@ -6,6 +6,7 @@ final class PhabricatorApplicationTransactionResponse
private $viewer; private $viewer;
private $transactions; private $transactions;
private $anchorOffset; private $anchorOffset;
private $isPreview;
protected function buildProxy() { protected function buildProxy() {
return new AphrontAjaxResponse(); return new AphrontAjaxResponse();
@ -40,16 +41,26 @@ final class PhabricatorApplicationTransactionResponse
return $this->viewer; return $this->viewer;
} }
public function setIsPreview($is_preview) {
$this->isPreview = $is_preview;
return $this;
}
public function reduceProxyResponse() { public function reduceProxyResponse() {
$view = id(new PhabricatorApplicationTransactionView()) $view = id(new PhabricatorApplicationTransactionView())
->setViewer($this->getViewer()) ->setUser($this->getViewer())
->setTransactions($this->getTransactions()); ->setTransactions($this->getTransactions())
->setIsPreview($this->isPreview);
if ($this->getAnchorOffset()) { if ($this->getAnchorOffset()) {
$view->setAnchorOffset($this->getAnchorOffset()); $view->setAnchorOffset($this->getAnchorOffset());
} }
$xactions = mpull($view->buildEvents(), 'render', 'getTransactionPHID'); if ($this->isPreview) {
$xactions = mpull($view->buildEvents(), 'render');
} else {
$xactions = mpull($view->buildEvents(), 'render', 'getTransactionPHID');
}
$content = array( $content = array(
'xactions' => $xactions, 'xactions' => $xactions,

View file

@ -0,0 +1,147 @@
<?php
/**
* @concrete-extensible
*/
class PhabricatorApplicationTransactionCommentView extends AphrontView {
private $submitButtonName;
private $action;
private $previewPanelID;
private $previewTimelineID;
private $previewToggleID;
private $formID;
private $statusID;
public function setSubmitButtonName($submit_button_name) {
$this->submitButtonName = $submit_button_name;
return $this;
}
public function getSubmitButtonName() {
return $this->submitButtonName;
}
public function setAction($action) {
$this->action = $action;
return $this;
}
public function getAction() {
return $this->action;
}
public function render() {
$data = array();
$comment = $this->renderCommentPanel();
$preview = $this->renderPreviewPanel();
Javelin::initBehavior(
'phabricator-transaction-comment-form',
array(
'formID' => $this->getFormID(),
'timelineID' => $this->getPreviewTimelineID(),
'panelID' => $this->getPreviewPanelID(),
'statusID' => $this->getStatusID(),
'loadingString' => pht('Loading Preview...'),
'savingString' => pht('Saving Draft...'),
'draftString' => pht('Saved Draft'),
'actionURI' => $this->getAction(),
));
return self::renderSingleView(
array(
$comment,
$preview,
));
}
private function renderCommentPanel() {
$status = phutil_render_tag(
'div',
array(
'id' => $this->getStatusID(),
),
'');
return id(new AphrontFormView())
->setUser($this->getUser())
->setFlexible(true)
->addSigil('transaction-append')
->setWorkflow(true)
->setAction($this->getAction())
->setID($this->getFormID())
->appendChild(
id(new PhabricatorRemarkupControl())
->setName('comment')
->setLabel(pht('Comment'))
->setUser($this->getUser()))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue($this->getSubmitButtonName()))
->appendChild(
id(new AphrontFormMarkupControl())
->setValue($status));
}
private function renderPreviewPanel() {
$preview = id(new PhabricatorTimelineView())
->setID($this->getPreviewTimelineID());
$header = phutil_render_tag(
'div',
array(
'class' => 'phabricator-timeline-preview-header',
),
phutil_escape_html(pht('Preview')));
return phutil_render_tag(
'div',
array(
'id' => $this->getPreviewPanelID(),
'style' => 'display: none',
),
self::renderSingleView(
array(
$header,
$preview,
)));
}
private function getPreviewPanelID() {
if (!$this->previewPanelID) {
$this->previewPanelID = celerity_generate_unique_node_id();
}
return $this->previewPanelID;
}
private function getPreviewTimelineID() {
if (!$this->previewTimelineID) {
$this->previewTimelineID = celerity_generate_unique_node_id();
}
return $this->previewTimelineID;
}
private function getFormID() {
if (!$this->formID) {
$this->formID = celerity_generate_unique_node_id();
}
return $this->formID;
}
private function getStatusID() {
if (!$this->statusID) {
$this->statusID = celerity_generate_unique_node_id();
}
return $this->statusID;
}
}

View file

@ -5,11 +5,16 @@
*/ */
class PhabricatorApplicationTransactionView extends AphrontView { class PhabricatorApplicationTransactionView extends AphrontView {
private $viewer;
private $transactions; private $transactions;
private $engine; private $engine;
private $anchorOffset = 1; private $anchorOffset = 1;
private $showEditActions = true; private $showEditActions = true;
private $isPreview;
public function setIsPreview($is_preview) {
$this->isPreview = $is_preview;
return $this;
}
public function setShowEditActions($show_edit_actions) { public function setShowEditActions($show_edit_actions) {
$this->showEditActions = $show_edit_actions; $this->showEditActions = $show_edit_actions;
@ -36,16 +41,11 @@ class PhabricatorApplicationTransactionView extends AphrontView {
return $this; return $this;
} }
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function buildEvents() { public function buildEvents() {
$field = PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT; $field = PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT;
$engine = $this->getOrBuildEngine(); $engine = $this->getOrBuildEngine();
$viewer = $this->viewer; $user = $this->getUser();
$anchor = $this->anchorOffset; $anchor = $this->anchorOffset;
$events = array(); $events = array();
@ -55,22 +55,29 @@ class PhabricatorApplicationTransactionView extends AphrontView {
} }
$event = id(new PhabricatorTimelineEventView()) $event = id(new PhabricatorTimelineEventView())
->setViewer($viewer) ->setUser($user)
->setTransactionPHID($xaction->getPHID()) ->setTransactionPHID($xaction->getPHID())
->setUserHandle($xaction->getHandle($xaction->getAuthorPHID())) ->setUserHandle($xaction->getHandle($xaction->getAuthorPHID()))
->setIcon($xaction->getIcon()) ->setIcon($xaction->getIcon())
->setColor($xaction->getColor()) ->setColor($xaction->getColor())
->setTitle($xaction->getTitle()) ->setTitle($xaction->getTitle());
->setDateCreated($xaction->getDateCreated())
->setContentSource($xaction->getContentSource()) if ($this->isPreview) {
->setAnchor($anchor); $event->setIsPreview(true);
} else {
$event
->setDateCreated($xaction->getDateCreated())
->setContentSource($xaction->getContentSource())
->setAnchor($anchor);
$anchor++;
}
$anchor++;
$has_deleted_comment = $xaction->getComment() && $has_deleted_comment = $xaction->getComment() &&
$xaction->getComment()->getIsDeleted(); $xaction->getComment()->getIsDeleted();
if ($this->getShowEditActions()) { if ($this->getShowEditActions() && !$this->isPreview) {
if ($xaction->getCommentVersion() > 1) { if ($xaction->getCommentVersion() > 1) {
$event->setIsEdited(true); $event->setIsEdited(true);
} }
@ -79,7 +86,7 @@ class PhabricatorApplicationTransactionView extends AphrontView {
if ($xaction->hasComment() || $has_deleted_comment) { if ($xaction->hasComment() || $has_deleted_comment) {
$has_edit_capability = PhabricatorPolicyFilter::hasCapability( $has_edit_capability = PhabricatorPolicyFilter::hasCapability(
$viewer, $user,
$xaction, $xaction,
$can_edit); $can_edit);
if ($has_edit_capability) { if ($has_edit_capability) {
@ -134,7 +141,7 @@ class PhabricatorApplicationTransactionView extends AphrontView {
$field = PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT; $field = PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT;
$engine = id(new PhabricatorMarkupEngine()) $engine = id(new PhabricatorMarkupEngine())
->setViewer($this->viewer); ->setViewer($this->getUser());
foreach ($this->transactions as $xaction) { foreach ($this->transactions as $xaction) {
if (!$xaction->hasComment()) { if (!$xaction->hasComment()) {
continue; continue;

View file

@ -9,11 +9,11 @@ final class PhabricatorTimelineEventView extends AphrontView {
private $classes = array(); private $classes = array();
private $contentSource; private $contentSource;
private $dateCreated; private $dateCreated;
private $viewer;
private $anchor; private $anchor;
private $isEditable; private $isEditable;
private $isEdited; private $isEdited;
private $transactionPHID; private $transactionPHID;
private $isPreview;
public function setTransactionPHID($transaction_phid) { public function setTransactionPHID($transaction_phid) {
$this->transactionPHID = $transaction_phid; $this->transactionPHID = $transaction_phid;
@ -33,6 +33,14 @@ final class PhabricatorTimelineEventView extends AphrontView {
return $this->isEdited; return $this->isEdited;
} }
public function setIsPreview($is_preview) {
$this->isPreview = $is_preview;
return $this;
}
public function getIsPreview() {
return $this->isPreview;
}
public function setIsEditable($is_editable) { public function setIsEditable($is_editable) {
$this->isEditable = $is_editable; $this->isEditable = $is_editable;
@ -43,15 +51,6 @@ final class PhabricatorTimelineEventView extends AphrontView {
return $this->isEditable; return $this->isEditable;
} }
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function setDateCreated($date_created) { public function setDateCreated($date_created) {
$this->dateCreated = $date_created; $this->dateCreated = $date_created;
return $this; return $this;
@ -108,67 +107,7 @@ final class PhabricatorTimelineEventView extends AphrontView {
$title = ''; $title = '';
} }
$extra = array(); $extra = $this->renderExtra();
$xaction_phid = $this->getTransactionPHID();
if ($this->getIsEdited()) {
$extra[] = javelin_render_tag(
'a',
array(
'href' => '/transactions/history/'.$xaction_phid.'/',
'sigil' => 'workflow',
),
pht('Edited'));
}
if ($this->getIsEditable()) {
$extra[] = javelin_render_tag(
'a',
array(
'href' => '/transactions/edit/'.$xaction_phid.'/',
'sigil' => 'workflow transaction-edit',
),
pht('Edit'));
}
$source = $this->getContentSource();
if ($source) {
$extra[] = id(new PhabricatorContentSourceView())
->setContentSource($source)
->setUser($this->getViewer())
->render();
}
if ($this->getDateCreated()) {
$date = phabricator_datetime(
$this->getDateCreated(),
$this->getViewer());
if ($this->anchor) {
Javelin::initBehavior('phabricator-watch-anchor');
$anchor = id(new PhabricatorAnchorView())
->setAnchorName($this->anchor)
->render();
$date = $anchor.phutil_render_tag(
'a',
array(
'href' => '#'.$this->anchor,
),
$date);
}
$extra[] = $date;
}
$extra = implode(' &middot; ', $extra);
if ($extra) {
$extra = phutil_render_tag(
'span',
array(
'class' => 'phabricator-timeline-extra',
),
$extra);
}
if ($title !== null || $extra !== null) { if ($title !== null || $extra !== null) {
$title_classes = array(); $title_classes = array();
@ -286,4 +225,76 @@ final class PhabricatorTimelineEventView extends AphrontView {
$content)); $content));
} }
private function renderExtra() {
$extra = array();
if ($this->getIsPreview()) {
$extra[] = pht('PREVIEW');
} else {
$xaction_phid = $this->getTransactionPHID();
if ($this->getIsEdited()) {
$extra[] = javelin_render_tag(
'a',
array(
'href' => '/transactions/history/'.$xaction_phid.'/',
'sigil' => 'workflow',
),
pht('Edited'));
}
if ($this->getIsEditable()) {
$extra[] = javelin_render_tag(
'a',
array(
'href' => '/transactions/edit/'.$xaction_phid.'/',
'sigil' => 'workflow transaction-edit',
),
pht('Edit'));
}
$source = $this->getContentSource();
if ($source) {
$extra[] = id(new PhabricatorContentSourceView())
->setContentSource($source)
->setUser($this->getUser())
->render();
}
if ($this->getDateCreated()) {
$date = phabricator_datetime(
$this->getDateCreated(),
$this->getUser());
if ($this->anchor) {
Javelin::initBehavior('phabricator-watch-anchor');
$anchor = id(new PhabricatorAnchorView())
->setAnchorName($this->anchor)
->render();
$date = $anchor.phutil_render_tag(
'a',
array(
'href' => '#'.$this->anchor,
),
$date);
}
$extra[] = $date;
}
}
$extra = implode(' &middot; ', $extra);
if ($extra) {
$extra = phutil_render_tag(
'span',
array(
'class' => 'phabricator-timeline-extra',
),
$extra);
}
return $extra;
}
} }

View file

@ -245,3 +245,10 @@
background: rgba(255, 255, 0, 0.50); background: rgba(255, 255, 0, 0.50);
box-shadow: 0 0 3px 6px rgba(255, 255, 0, 0.50); box-shadow: 0 0 3px 6px rgba(255, 255, 0, 0.50);
} }
.phabricator-timeline-preview-header {
background: #e0e3ec;
color: #444444;
padding: 4px 1.25%;
border: solid #c0c5d1 1px 0;
}

View file

@ -0,0 +1,44 @@
/**
* @provides javelin-behavior-phabricator-transaction-comment-form
* @requires javelin-behavior
* javelin-dom
* javelin-util
* phabricator-shaped-request
*/
JX.behavior('phabricator-transaction-comment-form', function(config) {
var form = JX.$(config.formID);
var getdata = function() {
var obj = JX.DOM.convertFormToDictionary(form);
obj.__preview__ = 1;
return obj;
};
var onresponse = function(response) {
var panel = JX.$(config.panelID);
if (!response.xactions.length) {
JX.DOM.hide(panel);
} else {
JX.DOM.setContent(
JX.$(config.timelineID),
[
JX.$H(response.spacer),
JX.$H(response.xactions.join(response.spacer)),
JX.$H(response.spacer),
]);
JX.DOM.show(panel);
}
};
var request = new JX.PhabricatorShapedRequest(
config.actionURI,
onresponse,
getdata);
var trigger = JX.bind(request, request.trigger);
JX.DOM.listen(form, 'keydown', null, trigger);
request.start();
});