1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-25 16:22:43 +01:00

Implement basic transaction detail blocks

Summary:
Some transactions (like editing configuration values, task descriptions, or Conpherence images) can't be simply explained and need an additional larger element to show them fully (like a text diff).

Support change details like this in ApplicationTransactions. Implements the element in Config, so you can see changes.

Test Plan: {F32974}

Reviewers: chad

Reviewed By: chad

CC: aran

Maniphest Tasks: T2213

Differential Revision: https://secure.phabricator.com/D4984
This commit is contained in:
epriestley 2013-02-17 06:37:02 -08:00
parent 5fb56f859c
commit 2231e5200a
13 changed files with 250 additions and 95 deletions

View file

@ -57,7 +57,7 @@ $package_spec = array(
'phabricator-textareautils',
'phabricator-file-upload',
'javelin-behavior-global-drag-and-drop',
'javelin-behavior-phabricator-home-reveal-tiles',
'javelin-behavior-phabricator-reveal-content',
),
'core.pkg.css' => array(
'phabricator-core-css',

View file

@ -1686,18 +1686,6 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/js/application/core/behavior-file-tree.js',
),
'javelin-behavior-phabricator-home-reveal-tiles' =>
array(
'uri' => '/res/b3c5cea9/rsrc/js/application/core/behavior-home-reveal-tiles.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-stratcom',
2 => 'javelin-dom',
),
'disk' => '/rsrc/js/application/core/behavior-home-reveal-tiles.js',
),
'javelin-behavior-phabricator-keyboard-pager' =>
array(
'uri' => '/res/56d64eff/rsrc/js/application/core/behavior-keyboard-pager.js',
@ -1789,6 +1777,18 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/js/application/core/behavior-phabricator-remarkup-assist.js',
),
'javelin-behavior-phabricator-reveal-content' =>
array(
'uri' => '/res/a4fae14a/rsrc/js/application/core/behavior-reveal-content.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-stratcom',
2 => 'javelin-dom',
),
'disk' => '/rsrc/js/application/core/behavior-reveal-content.js',
),
'javelin-behavior-phabricator-search-typeahead' =>
array(
'uri' => '/res/046ab274/rsrc/js/application/core/behavior-search-typeahead.js',
@ -3017,7 +3017,7 @@ celerity_register_resource_map(array(
),
'phabricator-timeline-view-css' =>
array(
'uri' => '/res/d87e1d60/rsrc/css/layout/phabricator-timeline-view.css',
'uri' => '/res/5517bf1a/rsrc/css/layout/phabricator-timeline-view.css',
'type' => 'css',
'requires' =>
array(
@ -3471,7 +3471,7 @@ celerity_register_resource_map(array(
'uri' => '/res/pkg/acc46105/core.pkg.css',
'type' => 'css',
),
'bc0774e5' =>
'd29c1557' =>
array(
'name' => 'core.pkg.js',
'symbols' =>
@ -3508,9 +3508,9 @@ celerity_register_resource_map(array(
29 => 'phabricator-textareautils',
30 => 'phabricator-file-upload',
31 => 'javelin-behavior-global-drag-and-drop',
32 => 'javelin-behavior-phabricator-home-reveal-tiles',
32 => 'javelin-behavior-phabricator-reveal-content',
),
'uri' => '/res/pkg/bc0774e5/core.pkg.js',
'uri' => '/res/pkg/d29c1557/core.pkg.js',
'type' => 'js',
),
'dca4a03d' =>
@ -3682,17 +3682,17 @@ celerity_register_resource_map(array(
'diffusion-icons-css' => 'c8ce2d88',
'global-drag-and-drop-css' => 'acc46105',
'inline-comment-summary-css' => '8aaacd1b',
'javelin-aphlict' => 'bc0774e5',
'javelin-aphlict' => 'd29c1557',
'javelin-behavior' => 'a69b9f1f',
'javelin-behavior-aphlict-dropdown' => 'bc0774e5',
'javelin-behavior-aphlict-listen' => 'bc0774e5',
'javelin-behavior-aphront-basic-tokenizer' => 'bc0774e5',
'javelin-behavior-aphlict-dropdown' => 'd29c1557',
'javelin-behavior-aphlict-listen' => 'd29c1557',
'javelin-behavior-aphront-basic-tokenizer' => 'd29c1557',
'javelin-behavior-aphront-drag-and-drop' => '95d0d865',
'javelin-behavior-aphront-drag-and-drop-textarea' => '95d0d865',
'javelin-behavior-aphront-form-disable-on-submit' => 'bc0774e5',
'javelin-behavior-aphront-form-disable-on-submit' => 'd29c1557',
'javelin-behavior-audit-preview' => 'f96657b8',
'javelin-behavior-dark-console' => 'dca4a03d',
'javelin-behavior-device' => 'bc0774e5',
'javelin-behavior-device' => 'd29c1557',
'javelin-behavior-differential-accept-with-errors' => '95d0d865',
'javelin-behavior-differential-add-reviewers-and-ccs' => '95d0d865',
'javelin-behavior-differential-comment-jump' => '95d0d865',
@ -3708,29 +3708,29 @@ celerity_register_resource_map(array(
'javelin-behavior-diffusion-commit-graph' => 'f96657b8',
'javelin-behavior-diffusion-pull-lastmodified' => 'f96657b8',
'javelin-behavior-error-log' => 'dca4a03d',
'javelin-behavior-global-drag-and-drop' => 'bc0774e5',
'javelin-behavior-konami' => 'bc0774e5',
'javelin-behavior-lightbox-attachments' => 'bc0774e5',
'javelin-behavior-global-drag-and-drop' => 'd29c1557',
'javelin-behavior-konami' => 'd29c1557',
'javelin-behavior-lightbox-attachments' => 'd29c1557',
'javelin-behavior-maniphest-batch-selector' => '7707de41',
'javelin-behavior-maniphest-subpriority-editor' => '7707de41',
'javelin-behavior-maniphest-transaction-controls' => '7707de41',
'javelin-behavior-maniphest-transaction-expand' => '7707de41',
'javelin-behavior-maniphest-transaction-preview' => '7707de41',
'javelin-behavior-phabricator-active-nav' => 'bc0774e5',
'javelin-behavior-phabricator-autofocus' => 'bc0774e5',
'javelin-behavior-phabricator-home-reveal-tiles' => 'bc0774e5',
'javelin-behavior-phabricator-keyboard-shortcuts' => 'bc0774e5',
'javelin-behavior-phabricator-nav' => 'bc0774e5',
'javelin-behavior-phabricator-active-nav' => 'd29c1557',
'javelin-behavior-phabricator-autofocus' => 'd29c1557',
'javelin-behavior-phabricator-keyboard-shortcuts' => 'd29c1557',
'javelin-behavior-phabricator-nav' => 'd29c1557',
'javelin-behavior-phabricator-object-selector' => '95d0d865',
'javelin-behavior-phabricator-oncopy' => 'bc0774e5',
'javelin-behavior-phabricator-remarkup-assist' => 'bc0774e5',
'javelin-behavior-phabricator-search-typeahead' => 'bc0774e5',
'javelin-behavior-phabricator-tooltips' => 'bc0774e5',
'javelin-behavior-phabricator-watch-anchor' => 'bc0774e5',
'javelin-behavior-refresh-csrf' => 'bc0774e5',
'javelin-behavior-phabricator-oncopy' => 'd29c1557',
'javelin-behavior-phabricator-remarkup-assist' => 'd29c1557',
'javelin-behavior-phabricator-reveal-content' => 'd29c1557',
'javelin-behavior-phabricator-search-typeahead' => 'd29c1557',
'javelin-behavior-phabricator-tooltips' => 'd29c1557',
'javelin-behavior-phabricator-watch-anchor' => 'd29c1557',
'javelin-behavior-refresh-csrf' => 'd29c1557',
'javelin-behavior-repository-crossreference' => '95d0d865',
'javelin-behavior-toggle-class' => 'bc0774e5',
'javelin-behavior-workflow' => 'bc0774e5',
'javelin-behavior-toggle-class' => 'd29c1557',
'javelin-behavior-workflow' => 'd29c1557',
'javelin-dom' => 'a69b9f1f',
'javelin-event' => 'a69b9f1f',
'javelin-install' => 'a69b9f1f',
@ -3752,39 +3752,39 @@ celerity_register_resource_map(array(
'lightbox-attachment-css' => 'acc46105',
'maniphest-task-summary-css' => 'e30a3fa8',
'maniphest-transaction-detail-css' => 'e30a3fa8',
'phabricator-busy' => 'bc0774e5',
'phabricator-busy' => 'd29c1557',
'phabricator-content-source-view-css' => '8aaacd1b',
'phabricator-core-buttons-css' => 'acc46105',
'phabricator-core-css' => 'acc46105',
'phabricator-crumbs-view-css' => 'acc46105',
'phabricator-directory-css' => 'acc46105',
'phabricator-drag-and-drop-file-upload' => '95d0d865',
'phabricator-dropdown-menu' => 'bc0774e5',
'phabricator-file-upload' => 'bc0774e5',
'phabricator-dropdown-menu' => 'd29c1557',
'phabricator-file-upload' => 'd29c1557',
'phabricator-filetree-view-css' => 'acc46105',
'phabricator-flag-css' => 'acc46105',
'phabricator-form-view-css' => 'acc46105',
'phabricator-header-view-css' => 'acc46105',
'phabricator-jump-nav' => 'acc46105',
'phabricator-keyboard-shortcut' => 'bc0774e5',
'phabricator-keyboard-shortcut-manager' => 'bc0774e5',
'phabricator-keyboard-shortcut' => 'd29c1557',
'phabricator-keyboard-shortcut-manager' => 'd29c1557',
'phabricator-main-menu-view' => 'acc46105',
'phabricator-menu-item' => 'bc0774e5',
'phabricator-menu-item' => 'd29c1557',
'phabricator-nav-view-css' => 'acc46105',
'phabricator-notification' => 'bc0774e5',
'phabricator-notification' => 'd29c1557',
'phabricator-notification-css' => 'acc46105',
'phabricator-notification-menu-css' => 'acc46105',
'phabricator-object-item-list-view-css' => 'acc46105',
'phabricator-object-selector-css' => '8aaacd1b',
'phabricator-paste-file-upload' => 'bc0774e5',
'phabricator-prefab' => 'bc0774e5',
'phabricator-paste-file-upload' => 'd29c1557',
'phabricator-prefab' => 'd29c1557',
'phabricator-project-tag-css' => 'e30a3fa8',
'phabricator-remarkup-css' => 'acc46105',
'phabricator-shaped-request' => '95d0d865',
'phabricator-side-menu-view-css' => 'acc46105',
'phabricator-standard-page-view' => 'acc46105',
'phabricator-textareautils' => 'bc0774e5',
'phabricator-tooltip' => 'bc0774e5',
'phabricator-textareautils' => 'd29c1557',
'phabricator-tooltip' => 'd29c1557',
'phabricator-transaction-view-css' => 'acc46105',
'phabricator-zindex-css' => 'acc46105',
'sprite-apps-large-css' => 'acc46105',

View file

@ -684,6 +684,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionNoEffectResponse' => 'applications/transactions/response/PhabricatorApplicationTransactionNoEffectResponse.php',
'PhabricatorApplicationTransactionQuery' => 'applications/transactions/query/PhabricatorApplicationTransactionQuery.php',
'PhabricatorApplicationTransactionResponse' => 'applications/transactions/response/PhabricatorApplicationTransactionResponse.php',
'PhabricatorApplicationTransactionTextDiffDetailView' => 'applications/transactions/view/PhabricatorApplicationTransactionTextDiffDetailView.php',
'PhabricatorApplicationTransactionView' => 'applications/transactions/view/PhabricatorApplicationTransactionView.php',
'PhabricatorApplicationTransactions' => 'applications/transactions/application/PhabricatorApplicationTransactions.php',
'PhabricatorApplicationUIExamples' => 'applications/uiexample/application/PhabricatorApplicationUIExamples.php',
@ -2168,6 +2169,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionNoEffectResponse' => 'AphrontProxyResponse',
'PhabricatorApplicationTransactionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorApplicationTransactionResponse' => 'AphrontProxyResponse',
'PhabricatorApplicationTransactionTextDiffDetailView' => 'AphrontView',
'PhabricatorApplicationTransactionView' => 'AphrontView',
'PhabricatorApplicationTransactions' => 'PhabricatorApplication',
'PhabricatorApplicationUIExamples' => 'PhabricatorApplication',
@ -2222,7 +2224,7 @@ phutil_register_library_map(array(
'PhabricatorCalendarHoliday' => 'PhabricatorCalendarDAO',
'PhabricatorCalendarHolidayTestCase' => 'PhabricatorTestCase',
'PhabricatorCalendarViewStatusController' => 'PhabricatorCalendarController',
'PhabricatorCampfireProtocolAdapter' => 'PhabricatorBaseProtocolAdapter',
'PhabricatorCampfireProtocolAdapter' => 'PhabricatorBotBaseStreamingProtocolAdapter',
'PhabricatorChangesetResponse' => 'AphrontProxyResponse',
'PhabricatorChatLogChannel' =>
array(

View file

@ -70,6 +70,39 @@ final class PhabricatorConfigTransaction
return parent::getIcon();
}
public function hasChangeDetails() {
switch ($this->getTransactionType()) {
case self::TYPE_EDIT:
return true;
}
return parent::hasChangeDetails();
}
public function renderChangeDetails() {
$old = $this->getOldValue();
$new = $this->getNewValue();
if ($old['deleted']) {
$old_text = '';
} else {
// NOTE: Here and below, we're adding a synthetic "\n" to prevent the
// differ from complaining about missing trailing newlines.
$old_text = PhabricatorConfigJSON::prettyPrintJSON($old['value'])."\n";
}
if ($new['deleted']) {
$new_text = '';
} else {
$new_text = PhabricatorConfigJSON::prettyPrintJSON($new['value'])."\n";
}
$view = id(new PhabricatorApplicationTransactionTextDiffDetailView())
->setOldText($old_text)
->setNewText($new_text);
return $view->render();
}
public function getColor() {
$old = $this->getOldValue();
$new = $this->getNewValue();

View file

@ -26,7 +26,7 @@ final class DifferentialParseRenderTestCase extends PhabricatorTestCase {
$actual = $parser->render(null, null, array());
$expect = Filesystem::readFile($dir.$file.'.'.$type.'.expect');
$this->assertEqual($expect, $actual, $file.'.'.$type);
$this->assertEqual($expect, (string)$actual, $file.'.'.$type);
}
}
}

View file

@ -252,7 +252,7 @@ abstract class DifferentialChangesetRenderer {
// TODO: Both these steps should happen earlier.
$result = str_replace("\t", ' ', $result);
return $result;
return phutil_safe_html($result);
}
abstract public function isOneUpRenderer();

View file

@ -84,7 +84,7 @@ abstract class PhabricatorDirectoryController extends PhabricatorController {
$show_item = id(new PhabricatorMenuItemView())
->setName(pht('Show More Applications'))
->setHref('#')
->addSigil('home-show-applications')
->addSigil('reveal-content')
->setID($show_item_id);
$hide_item = id(new PhabricatorMenuItemView())
@ -92,7 +92,7 @@ abstract class PhabricatorDirectoryController extends PhabricatorController {
->setHref('#')
->setStyle('display: none')
->setID($hide_item_id)
->addSigil('home-hide-applications');
->addSigil('reveal-content');
$nav->addMenuItem($show_item);
$tile_ids = array($hide_item_id);
@ -146,10 +146,18 @@ abstract class PhabricatorDirectoryController extends PhabricatorController {
}
if ($is_hide) {
Javelin::initBehavior('phabricator-home-reveal-tiles', array(
'tileIDs' => $tile_ids,
'showID' => $show_item_id,
));
Javelin::initBehavior('phabricator-reveal-content');
$show_item->setMetadata(
array(
'showIDs' => $tile_ids,
'hideIDs' => array($show_item_id),
));
$hide_item->setMetadata(
array(
'showIDs' => array($show_item_id),
'hideIDs' => $tile_ids,
));
$nav->addMenuItem($hide_item);
}
}

View file

@ -321,6 +321,13 @@ abstract class PhabricatorApplicationTransaction
return array();
}
public function hasChangeDetails() {
return false;
}
public function renderChangeDetails() {
return null;
}
/* -( PhabricatorPolicyInterface Implementation )-------------------------- */

View file

@ -0,0 +1,45 @@
<?php
final class PhabricatorApplicationTransactionTextDiffDetailView
extends AphrontView {
private $oldText;
private $newText;
public function setNewText($new_text) {
$this->newText = $new_text;
return $this;
}
public function setOldText($old_text) {
$this->oldText = $old_text;
return $this;
}
public function render() {
$old = $this->oldText;
$new = $this->newText;
// TODO: On mobile, or perhaps by default, we should switch to 1-up once
// that is built.
// TODO: This should be utf8-aware, but we don't currently have a plain-text
// utf8 hard-wrap function. See T2554.
$old = wordwrap($old, 80);
$new = wordwrap($new, 80);
$engine = new PhabricatorDifferenceEngine();
$changeset = $engine->generateChangesetFromFileContent($old, $new);
$whitespace_mode = DifferentialChangesetParser::WHITESPACE_SHOW_ALL;
$parser = new DifferentialChangesetParser();
$parser->setChangeset($changeset);
$parser->setMarkupEngine(new PhabricatorMarkupEngine());
$parser->setWhitespaceMode($whitespace_mode);
return $parser->render(0, PHP_INT_MAX, array());
}
}

View file

@ -59,8 +59,17 @@ class PhabricatorApplicationTransactionView extends AphrontView {
->setTransactionPHID($xaction->getPHID())
->setUserHandle($xaction->getHandle($xaction->getAuthorPHID()))
->setIcon($xaction->getIcon())
->setColor($xaction->getColor())
->setTitle($xaction->getTitle());
->setColor($xaction->getColor());
$title = $xaction->getTitle();
if ($xaction->hasChangeDetails()) {
$title = array(
$title,
' ',
$this->buildChangeDetails($xaction),
);
}
$event->setTitle($title);
if ($this->isPreview) {
$event->setIsPreview(true);
@ -73,7 +82,6 @@ class PhabricatorApplicationTransactionView extends AphrontView {
$anchor++;
}
$has_deleted_comment = $xaction->getComment() &&
$xaction->getComment()->getIsDeleted();
@ -153,5 +161,60 @@ class PhabricatorApplicationTransactionView extends AphrontView {
return $engine;
}
private function buildChangeDetails(
PhabricatorApplicationTransaction $xaction) {
Javelin::initBehavior('phabricator-reveal-content');
$show_id = celerity_generate_unique_node_id();
$hide_id = celerity_generate_unique_node_id();
$content_id = celerity_generate_unique_node_id();
$show_more = javelin_tag(
'a',
array(
'href' => '#',
'sigil' => 'reveal-content',
'mustcapture' => true,
'id' => $show_id,
'meta' => array(
'hideIDs' => array($show_id),
'showIDs' => array($hide_id, $content_id),
),
),
pht('(Show Details)'));
$hide_more = javelin_tag(
'a',
array(
'href' => '#',
'sigil' => 'reveal-content',
'mustcapture' => true,
'id' => $hide_id,
'style' => 'display: none',
'meta' => array(
'hideIDs' => array($hide_id, $content_id),
'showIDs' => array($show_id),
),
),
pht('(Hide Details)'));
$content = phutil_tag(
'div',
array(
'id' => $content_id,
'style' => 'display: none',
'class' => 'phabricator-timeline-change-details',
),
$xaction->renderChangeDetails());
return array(
$show_more,
$hide_more,
$content,
);
}
}

View file

@ -250,3 +250,10 @@
padding: 4px 1.25%;
border: solid #c0c5d1 1px 0;
}
.phabricator-timeline-change-details {
padding: 10px 0;
border-style: solid;
border-color: #efefef;
border-width: 1px 0;
}

View file

@ -1,33 +0,0 @@
/**
* @provides javelin-behavior-phabricator-home-reveal-tiles
* @requires javelin-behavior
* javelin-stratcom
* javelin-dom
* @javelin
*/
JX.behavior('phabricator-home-reveal-tiles', function(config) {
JX.Stratcom.listen(
'click',
'home-show-applications',
function(e) {
e.kill();
for (var ii = 0; ii < config.tileIDs.length; ii++) {
JX.DOM.show(JX.$(config.tileIDs[ii]));
}
JX.DOM.hide(JX.$(config.showID));
});
JX.Stratcom.listen(
'click',
'home-hide-applications',
function(e) {
e.kill();
for (var ii = 0; ii < config.tileIDs.length; ii++) {
JX.DOM.hide(JX.$(config.tileIDs[ii]));
}
JX.DOM.show(JX.$(config.showID));
});
});

View file

@ -0,0 +1,23 @@
/**
* @provides javelin-behavior-phabricator-reveal-content
* @requires javelin-behavior
* javelin-stratcom
* javelin-dom
* @javelin
*/
JX.behavior('phabricator-reveal-content', function(config) {
JX.Stratcom.listen(
'click',
'reveal-content',
function(e) {
e.kill();
var nodes = e.getNodeData('reveal-content');
for (var ii = 0; ii < nodes.showIDs.length; ii++) {
JX.DOM.show(JX.$(nodes.showIDs[ii]));
}
for (var ii = 0; ii < nodes.hideIDs.length; ii++) {
JX.DOM.hide(JX.$(nodes.hideIDs[ii]));
}
});
});