1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 08:42:41 +01:00

Add comment linking to Maniphest and Differential

Summary:
Allows you to link to comments with "D123#3" or "T123#3", then adds a pile of JS
to try to make it not terrible. :/

The thing I'm trying to avoid here is when someone says "look at this!
http://blog.com/#comment-239291" and you click and your browser jumps somewhere
random and you have no idea which comment they meant. Since I really hate this,
I've tried to avoid it by making sure the comment is always highlighted.

Test Plan:
Put T1#1 and D1#1 in remarkup and verified they linked properly.

Clicked anchors on individual comments.

Faked all comments hidden in Differential and verified they expanded on anchor
or anchor change.

Reviewed By: aran
Reviewers: aran, tomo, mroch, jungejason, tuomaspelkonen
CC: aran, epriestley
Differential Revision: 383
This commit is contained in:
epriestley 2011-05-31 10:23:31 -07:00
parent ead9bbfeb1
commit d96d515cc2
22 changed files with 343 additions and 116 deletions

2
externals/javelin vendored

@ -1 +1 @@
Subproject commit 7ac64cd03d89d6d5f8c7f85b32c9611d3866ea2b Subproject commit c714cff45db83fc8c34e82b7ec1155b7c442dc7b

View file

@ -172,7 +172,7 @@ celerity_register_resource_map(array(
), ),
'differential-revision-comment-css' => 'differential-revision-comment-css' =>
array( array(
'uri' => '/res/e3ea8c34/rsrc/css/application/differential/revision-comment.css', 'uri' => '/res/e3539439/rsrc/css/application/differential/revision-comment.css',
'type' => 'css', 'type' => 'css',
'requires' => 'requires' =>
array( array(
@ -188,6 +188,16 @@ celerity_register_resource_map(array(
), ),
'disk' => '/rsrc/css/application/differential/revision-comment-list.css', 'disk' => '/rsrc/css/application/differential/revision-comment-list.css',
), ),
0 =>
array(
'uri' => '/res/39de799e/rsrc/js/javelin/docs/Base.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
),
'disk' => '/rsrc/js/javelin/docs/Base.js',
),
'differential-revision-detail-css' => 'differential-revision-detail-css' =>
array( array(
'uri' => '/res/ea9de420/rsrc/css/application/differential/revision-detail.css', 'uri' => '/res/ea9de420/rsrc/css/application/differential/revision-detail.css',
@ -279,16 +289,6 @@ celerity_register_resource_map(array(
), ),
'disk' => '/rsrc/js/javelin/lib/behavior.js', 'disk' => '/rsrc/js/javelin/lib/behavior.js',
), ),
0 =>
array(
'uri' => '/res/39de799e/rsrc/js/javelin/docs/Base.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
),
'disk' => '/rsrc/js/javelin/docs/Base.js',
),
'javelin-behavior-aphront-basic-tokenizer' => 'javelin-behavior-aphront-basic-tokenizer' =>
array( array(
'uri' => '/res/bce3961b/rsrc/js/application/core/behavior-tokenizer.js', 'uri' => '/res/bce3961b/rsrc/js/application/core/behavior-tokenizer.js',
@ -424,7 +424,7 @@ celerity_register_resource_map(array(
), ),
'javelin-behavior-differential-show-all-comments' => 'javelin-behavior-differential-show-all-comments' =>
array( array(
'uri' => '/res/b7c7e1ee/rsrc/js/application/differential/behavior-show-all-comments.js', 'uri' => '/res/bcc990f0/rsrc/js/application/differential/behavior-show-all-comments.js',
'type' => 'js', 'type' => 'js',
'requires' => 'requires' =>
array( array(
@ -560,6 +560,19 @@ celerity_register_resource_map(array(
), ),
'disk' => '/rsrc/js/application/core/behavior-object-selector.js', 'disk' => '/rsrc/js/application/core/behavior-object-selector.js',
), ),
'javelin-behavior-phabricator-watch-anchor' =>
array(
'uri' => '/res/bb6fa5b2/rsrc/js/application/core/behavior-watch-anchor.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-stratcom',
2 => 'javelin-util',
3 => 'javelin-dom',
),
'disk' => '/rsrc/js/application/core/behavior-watch-anchor.js',
),
'javelin-behavior-workflow' => 'javelin-behavior-workflow' =>
array( array(
'uri' => '/res/079f49c3/rsrc/js/application/core/behavior-workflow.js', 'uri' => '/res/079f49c3/rsrc/js/application/core/behavior-workflow.js',
@ -574,7 +587,7 @@ celerity_register_resource_map(array(
), ),
'javelin-dom' => 'javelin-dom' =>
array( array(
'uri' => '/res/770d72cd/rsrc/js/javelin/lib/DOM.js', 'uri' => '/res/43e9e2de/rsrc/js/javelin/lib/DOM.js',
'type' => 'js', 'type' => 'js',
'requires' => 'requires' =>
array( array(
@ -620,7 +633,7 @@ celerity_register_resource_map(array(
), ),
'javelin-magical-init' => 'javelin-magical-init' =>
array( array(
'uri' => '/res/ce002e50/rsrc/js/javelin/core/init.js', 'uri' => '/res/92e7f37e/rsrc/js/javelin/core/init.js',
'type' => 'js', 'type' => 'js',
'requires' => 'requires' =>
array( array(
@ -641,7 +654,7 @@ celerity_register_resource_map(array(
), ),
'javelin-request' => 'javelin-request' =>
array( array(
'uri' => '/res/72a23e59/rsrc/js/javelin/lib/Request.js', 'uri' => '/res/1ed0d596/rsrc/js/javelin/lib/Request.js',
'type' => 'js', 'type' => 'js',
'requires' => 'requires' =>
array( array(
@ -811,7 +824,7 @@ celerity_register_resource_map(array(
), ),
'maniphest-transaction-detail-css' => 'maniphest-transaction-detail-css' =>
array( array(
'uri' => '/res/14758b00/rsrc/css/application/maniphest/transaction-detail.css', 'uri' => '/res/7ee02b5e/rsrc/css/application/maniphest/transaction-detail.css',
'type' => 'css', 'type' => 'css',
'requires' => 'requires' =>
array( array(
@ -1002,23 +1015,6 @@ celerity_register_resource_map(array(
'uri' => '/res/pkg/122a6b6d/workflow.pkg.js', 'uri' => '/res/pkg/122a6b6d/workflow.pkg.js',
'type' => 'js', 'type' => 'js',
), ),
'1ac25e8a' =>
array (
'name' => 'differential.pkg.css',
'symbols' =>
array (
0 => 'differential-core-view-css',
1 => 'differential-changeset-view-css',
2 => 'differential-revision-detail-css',
3 => 'differential-revision-history-css',
4 => 'differential-table-of-contents-css',
5 => 'differential-revision-comment-css',
6 => 'differential-revision-add-comment-css',
7 => 'differential-revision-comment-list-css',
),
'uri' => '/res/pkg/1ac25e8a/differential.pkg.css',
'type' => 'css',
),
'33f413ef' => '33f413ef' =>
array ( array (
'name' => 'typeahead.pkg.js', 'name' => 'typeahead.pkg.js',
@ -1035,6 +1031,23 @@ celerity_register_resource_map(array(
'uri' => '/res/pkg/33f413ef/typeahead.pkg.js', 'uri' => '/res/pkg/33f413ef/typeahead.pkg.js',
'type' => 'js', 'type' => 'js',
), ),
'613cf273' =>
array (
'name' => 'differential.pkg.css',
'symbols' =>
array (
0 => 'differential-core-view-css',
1 => 'differential-changeset-view-css',
2 => 'differential-revision-detail-css',
3 => 'differential-revision-history-css',
4 => 'differential-table-of-contents-css',
5 => 'differential-revision-comment-css',
6 => 'differential-revision-add-comment-css',
7 => 'differential-revision-comment-list-css',
),
'uri' => '/res/pkg/613cf273/differential.pkg.css',
'type' => 'css',
),
'a6102fe7' => 'a6102fe7' =>
array ( array (
'name' => 'core.pkg.css', 'name' => 'core.pkg.css',
@ -1059,7 +1072,7 @@ celerity_register_resource_map(array(
'uri' => '/res/pkg/a6102fe7/core.pkg.css', 'uri' => '/res/pkg/a6102fe7/core.pkg.css',
'type' => 'css', 'type' => 'css',
), ),
'd985d27a' => 'db95a6d0' =>
array ( array (
'name' => 'javelin.pkg.js', 'name' => 'javelin.pkg.js',
'symbols' => 'symbols' =>
@ -1075,7 +1088,7 @@ celerity_register_resource_map(array(
8 => 'javelin-json', 8 => 'javelin-json',
9 => 'javelin-uri', 9 => 'javelin-uri',
), ),
'uri' => '/res/pkg/d985d27a/javelin.pkg.js', 'uri' => '/res/pkg/db95a6d0/javelin.pkg.js',
'type' => 'js', 'type' => 'js',
), ),
'ed383f69' => 'ed383f69' =>
@ -1104,16 +1117,16 @@ celerity_register_resource_map(array(
'aphront-table-view-css' => 'a6102fe7', 'aphront-table-view-css' => 'a6102fe7',
'aphront-tokenizer-control-css' => 'a6102fe7', 'aphront-tokenizer-control-css' => 'a6102fe7',
'aphront-typeahead-control-css' => 'a6102fe7', 'aphront-typeahead-control-css' => 'a6102fe7',
'differential-changeset-view-css' => '1ac25e8a', 'differential-changeset-view-css' => '613cf273',
'differential-core-view-css' => '1ac25e8a', 'differential-core-view-css' => '613cf273',
'differential-revision-add-comment-css' => '1ac25e8a', 'differential-revision-add-comment-css' => '613cf273',
'differential-revision-comment-css' => '1ac25e8a', 'differential-revision-comment-css' => '613cf273',
'differential-revision-comment-list-css' => '1ac25e8a', 'differential-revision-comment-list-css' => '613cf273',
'differential-revision-detail-css' => '1ac25e8a', 'differential-revision-detail-css' => '613cf273',
'differential-revision-history-css' => '1ac25e8a', 'differential-revision-history-css' => '613cf273',
'differential-table-of-contents-css' => '1ac25e8a', 'differential-table-of-contents-css' => '613cf273',
'diffusion-commit-view-css' => '03ef179e', 'diffusion-commit-view-css' => '03ef179e',
'javelin-behavior' => 'd985d27a', 'javelin-behavior' => 'db95a6d0',
'javelin-behavior-aphront-basic-tokenizer' => '33f413ef', 'javelin-behavior-aphront-basic-tokenizer' => '33f413ef',
'javelin-behavior-differential-diff-radios' => 'ed383f69', 'javelin-behavior-differential-diff-radios' => 'ed383f69',
'javelin-behavior-differential-edit-inline-comments' => 'ed383f69', 'javelin-behavior-differential-edit-inline-comments' => 'ed383f69',
@ -1121,22 +1134,22 @@ celerity_register_resource_map(array(
'javelin-behavior-differential-populate' => 'ed383f69', 'javelin-behavior-differential-populate' => 'ed383f69',
'javelin-behavior-differential-show-more' => 'ed383f69', 'javelin-behavior-differential-show-more' => 'ed383f69',
'javelin-behavior-workflow' => '122a6b6d', 'javelin-behavior-workflow' => '122a6b6d',
'javelin-dom' => 'd985d27a', 'javelin-dom' => 'db95a6d0',
'javelin-event' => 'd985d27a', 'javelin-event' => 'db95a6d0',
'javelin-install' => 'd985d27a', 'javelin-install' => 'db95a6d0',
'javelin-json' => 'd985d27a', 'javelin-json' => 'db95a6d0',
'javelin-mask' => '122a6b6d', 'javelin-mask' => '122a6b6d',
'javelin-request' => 'd985d27a', 'javelin-request' => 'db95a6d0',
'javelin-stratcom' => 'd985d27a', 'javelin-stratcom' => 'db95a6d0',
'javelin-tokenizer' => '33f413ef', 'javelin-tokenizer' => '33f413ef',
'javelin-typeahead' => '33f413ef', 'javelin-typeahead' => '33f413ef',
'javelin-typeahead-normalizer' => '33f413ef', 'javelin-typeahead-normalizer' => '33f413ef',
'javelin-typeahead-ondemand-source' => '33f413ef', 'javelin-typeahead-ondemand-source' => '33f413ef',
'javelin-typeahead-preloaded-source' => '33f413ef', 'javelin-typeahead-preloaded-source' => '33f413ef',
'javelin-typeahead-source' => '33f413ef', 'javelin-typeahead-source' => '33f413ef',
'javelin-uri' => 'd985d27a', 'javelin-uri' => 'db95a6d0',
'javelin-util' => 'd985d27a', 'javelin-util' => 'db95a6d0',
'javelin-vector' => 'd985d27a', 'javelin-vector' => 'db95a6d0',
'javelin-workflow' => '122a6b6d', 'javelin-workflow' => '122a6b6d',
'phabricator-core-buttons-css' => 'a6102fe7', 'phabricator-core-buttons-css' => 'a6102fe7',
'phabricator-core-css' => 'a6102fe7', 'phabricator-core-css' => 'a6102fe7',

View file

@ -410,6 +410,7 @@ phutil_register_library_map(array(
'PhabricatorRemarkupRuleDiffusion' => 'infrastructure/markup/remarkup/markuprule/diffusion', 'PhabricatorRemarkupRuleDiffusion' => 'infrastructure/markup/remarkup/markuprule/diffusion',
'PhabricatorRemarkupRuleImageMacro' => 'infrastructure/markup/remarkup/markuprule/imagemacro', 'PhabricatorRemarkupRuleImageMacro' => 'infrastructure/markup/remarkup/markuprule/imagemacro',
'PhabricatorRemarkupRuleManiphest' => 'infrastructure/markup/remarkup/markuprule/maniphest', 'PhabricatorRemarkupRuleManiphest' => 'infrastructure/markup/remarkup/markuprule/maniphest',
'PhabricatorRemarkupRuleObjectName' => 'infrastructure/markup/remarkup/markuprule/objectname',
'PhabricatorRemarkupRuleProxyImage' => 'infrastructure/markup/remarkup/markuprule/proxyimage', 'PhabricatorRemarkupRuleProxyImage' => 'infrastructure/markup/remarkup/markuprule/proxyimage',
'PhabricatorRemarkupRuleYoutube' => 'infrastructure/markup/remarkup/markuprule/youtube', 'PhabricatorRemarkupRuleYoutube' => 'infrastructure/markup/remarkup/markuprule/youtube',
'PhabricatorRepository' => 'applications/repository/storage/repository', 'PhabricatorRepository' => 'applications/repository/storage/repository',
@ -841,10 +842,11 @@ phutil_register_library_map(array(
'PhabricatorProjectProfile' => 'PhabricatorProjectDAO', 'PhabricatorProjectProfile' => 'PhabricatorProjectDAO',
'PhabricatorProjectProfileController' => 'PhabricatorProjectController', 'PhabricatorProjectProfileController' => 'PhabricatorProjectController',
'PhabricatorRedirectController' => 'PhabricatorController', 'PhabricatorRedirectController' => 'PhabricatorController',
'PhabricatorRemarkupRuleDifferential' => 'PhutilRemarkupRule', 'PhabricatorRemarkupRuleDifferential' => 'PhabricatorRemarkupRuleObjectName',
'PhabricatorRemarkupRuleDiffusion' => 'PhutilRemarkupRule', 'PhabricatorRemarkupRuleDiffusion' => 'PhutilRemarkupRule',
'PhabricatorRemarkupRuleImageMacro' => 'PhutilRemarkupRule', 'PhabricatorRemarkupRuleImageMacro' => 'PhutilRemarkupRule',
'PhabricatorRemarkupRuleManiphest' => 'PhutilRemarkupRule', 'PhabricatorRemarkupRuleManiphest' => 'PhabricatorRemarkupRuleObjectName',
'PhabricatorRemarkupRuleObjectName' => 'PhutilRemarkupRule',
'PhabricatorRemarkupRuleProxyImage' => 'PhutilRemarkupRule', 'PhabricatorRemarkupRuleProxyImage' => 'PhutilRemarkupRule',
'PhabricatorRemarkupRuleYoutube' => 'PhutilRemarkupRule', 'PhabricatorRemarkupRuleYoutube' => 'PhutilRemarkupRule',
'PhabricatorRepository' => 'PhabricatorRepositoryDAO', 'PhabricatorRepository' => 'PhabricatorRepositoryDAO',

View file

@ -42,7 +42,7 @@ class PhabricatorLoginController extends PhabricatorAuthController {
$error_view = null; $error_view = null;
if ($password_auth) { if ($password_auth) {
$error = false; $error = false;
$username = $request->getCookie('phusr'); $username_or_email = $request->getCookie('phusr');
if ($request->isFormPost()) { if ($request->isFormPost()) {
$username_or_email = $request->getStr('username_or_email'); $username_or_email = $request->getStr('username_or_email');

View file

@ -25,6 +25,7 @@ final class DifferentialRevisionCommentView extends AphrontView {
private $inlines; private $inlines;
private $changesets; private $changesets;
private $target; private $target;
private $commentNumber;
public function setComment($comment) { public function setComment($comment) {
$this->comment = $comment; $this->comment = $comment;
@ -61,6 +62,11 @@ final class DifferentialRevisionCommentView extends AphrontView {
$this->target = $target; $this->target = $target;
} }
public function setCommentNumber($comment_number) {
$this->commentNumber = $comment_number;
return $this;
}
public function render() { public function render() {
require_celerity_resource('phabricator-remarkup-css'); require_celerity_resource('phabricator-remarkup-css');
@ -78,6 +84,24 @@ final class DifferentialRevisionCommentView extends AphrontView {
$date = date('F jS, Y g:i:s A', $comment->getDateCreated()); $date = date('F jS, Y g:i:s A', $comment->getDateCreated());
} }
$info = array($date);
$comment_anchor = null;
$num = $this->commentNumber;
if ($num) {
Javelin::initBehavior('phabricator-watch-anchor');
$info[] = phutil_render_tag(
'a',
array(
'name' => 'comment-'.$num,
'href' => '#comment-'.$num,
),
'Comment D'.$comment->getRevisionID().'#'.$num);
$comment_anchor = 'anchor-comment-'.$num;
}
$info = implode(' · ', $info);
$author = $this->handles[$comment->getAuthorPHID()]; $author = $this->handles[$comment->getAuthorPHID()];
$author_link = $author->renderLink(); $author_link = $author->renderLink();
@ -189,21 +213,25 @@ final class DifferentialRevisionCommentView extends AphrontView {
$background = "background-image: url('{$uri}');"; $background = "background-image: url('{$uri}');";
} }
return return phutil_render_tag(
'<div class="differential-comment '.$action_class.'">'. 'div',
'<div class="differential-comment-head">'. array(
'<div class="differential-comment-date">'.$date.'</div>'. 'class' => "differential-comment {$action_class}",
'<div class="differential-comment-title">'.$title.'</div>'. 'id' => $comment_anchor,
'</div>'. ),
'<div class="differential-comment-body" style="'.$background.'">'. '<div class="differential-comment-head">'.
'<div class="differential-comment-content">'. '<span class="differential-comment-info">'.$info.'</span>'.
'<div class="differential-comment-core">'. '<span class="differential-comment-title">'.$title.'</span>'.
$content. '<div style="clear: both;"></div>'.
'</div>'. '</div>'.
$inline_render. '<div class="differential-comment-body" style="'.$background.'">'.
'<div class="differential-comment-content">'.
'<div class="differential-comment-core">'.
$content.
'</div>'. '</div>'.
$inline_render.
'</div>'. '</div>'.
'</div>'; '</div>');
} }
} }

View file

@ -8,6 +8,7 @@
phutil_require_module('phabricator', 'applications/differential/constants/action'); phutil_require_module('phabricator', 'applications/differential/constants/action');
phutil_require_module('phabricator', 'infrastructure/celerity/api'); phutil_require_module('phabricator', 'infrastructure/celerity/api');
phutil_require_module('phabricator', 'infrastructure/javelin/api');
phutil_require_module('phabricator', 'view/base'); phutil_require_module('phabricator', 'view/base');
phutil_require_module('phutil', 'markup'); phutil_require_module('phutil', 'markup');

View file

@ -63,7 +63,7 @@ final class DifferentialRevisionCommentListView extends AphrontView {
$inlines = mgroup($this->inlines, 'getCommentID'); $inlines = mgroup($this->inlines, 'getCommentID');
$num = 1;
$html = array(); $html = array();
foreach ($this->comments as $comment) { foreach ($this->comments as $comment) {
$view = new DifferentialRevisionCommentView(); $view = new DifferentialRevisionCommentView();
@ -73,6 +73,7 @@ final class DifferentialRevisionCommentListView extends AphrontView {
$view->setInlineComments(idx($inlines, $comment->getID(), array())); $view->setInlineComments(idx($inlines, $comment->getID(), array()));
$view->setChangesets($this->changesets); $view->setChangesets($this->changesets);
$view->setTargetDiff($this->target); $view->setTargetDiff($this->target);
$view->setCommentNumber($num++);
$html[] = $view->render(); $html[] = $view->render();
} }
@ -158,7 +159,7 @@ final class DifferentialRevisionCommentListView extends AphrontView {
} }
return return
'<div>'. '<div class="differential-comment-list">'.
implode("\n", $header). implode("\n", $header).
$hidden. $hidden.
implode("\n", $visible). implode("\n", $visible).

View file

@ -23,6 +23,7 @@ class ManiphestTransactionDetailView extends AphrontView {
private $markupEngine; private $markupEngine;
private $forEmail; private $forEmail;
private $preview; private $preview;
private $commentNumber;
private $renderSummaryOnly; private $renderSummaryOnly;
private $renderFullSummary; private $renderFullSummary;
@ -65,6 +66,11 @@ class ManiphestTransactionDetailView extends AphrontView {
return $this->renderFullSummary; return $this->renderFullSummary;
} }
public function setCommentNumber($comment_number) {
$this->commentNumber = $comment_number;
return $this;
}
public function renderForEmail($with_date) { public function renderForEmail($with_date) {
$this->forEmail = true; $this->forEmail = true;
@ -174,16 +180,37 @@ class ManiphestTransactionDetailView extends AphrontView {
$timestamp = phabricator_format_timestamp($transaction->getDateCreated()); $timestamp = phabricator_format_timestamp($transaction->getDateCreated());
} }
$info = array();
$info[] = $timestamp;
$comment_anchor = null;
$num = $this->commentNumber;
if ($num) {
Javelin::initBehavior('phabricator-watch-anchor');
$info[] = javelin_render_tag(
'a',
array(
'name' => 'comment-'.$num,
'href' => '#comment-'.$num,
),
'Comment T'.$any_transaction->getTaskID().'#'.$num);
$comment_anchor = 'anchor-comment-'.$num;
}
$info = implode(' &middot; ', $info);
return phutil_render_tag( return phutil_render_tag(
'div', 'div',
array( array(
'class' => "maniphest-transaction-detail-container", 'class' => "maniphest-transaction-detail-container",
'style' => "background-image: url('".$author->getImageURI()."')", 'style' => "background-image: url('".$author->getImageURI()."')",
'id' => $comment_anchor,
), ),
'<div class="maniphest-transaction-detail-view '.$more_classes.'">'. '<div class="maniphest-transaction-detail-view '.$more_classes.'">'.
'<div class="maniphest-transaction-header">'. '<div class="maniphest-transaction-header">'.
'<div class="maniphest-transaction-timestamp">'. '<div class="maniphest-transaction-timestamp">'.
$timestamp. $info.
'</div>'. '</div>'.
$descs. $descs.
'</div>'. '</div>'.

View file

@ -77,17 +77,19 @@ class ManiphestTransactionListView extends AphrontView {
$groups[] = $group; $groups[] = $group;
} }
$sequence = 1;
foreach ($groups as $group) { foreach ($groups as $group) {
$view = new ManiphestTransactionDetailView(); $view = new ManiphestTransactionDetailView();
$view->setTransactionGroup($group); $view->setTransactionGroup($group);
$view->setHandles($this->handles); $view->setHandles($this->handles);
$view->setMarkupEngine($this->markupEngine); $view->setMarkupEngine($this->markupEngine);
$view->setPreview($this->preview); $view->setPreview($this->preview);
$view->setCommentNumber($sequence++);
$views[] = $view->render(); $views[] = $view->render();
} }
return return
'<div style="padding: .5em 1.5em;">'. '<div class="maniphest-transaction-list-view">'.
implode("\n", $views). implode("\n", $views).
'</div>'; '</div>';
} }

View file

@ -98,6 +98,10 @@ by mentioning the name of an object:
# You must specify at least 7 characters of the hash. # You must specify at least 7 characters of the hash.
T123 # Link to Maniphest task T123 T123 # Link to Maniphest task T123
You can also link directly to a comment in Maniphest and Differential:
T123#4 # Link to comment #4 of T123
= Quoting Text = = Quoting Text =
To quote text, preface it with an ">": To quote text, preface it with an ">":

View file

@ -43,9 +43,22 @@ final class CelerityStaticResourceResponse {
return $this->metadataBlock; return $this->metadataBlock;
} }
/**
* Register a behavior for initialization. NOTE: if $config is empty,
* a behavior will execute only once even if it is initialized multiple times.
* If $config is nonempty, the behavior will be invoked once for each config.
*/
public function initBehavior($behavior, array $config = array()) { public function initBehavior($behavior, array $config = array()) {
$this->requireResource('javelin-behavior-'.$behavior); $this->requireResource('javelin-behavior-'.$behavior);
$this->behaviors[$behavior][] = $config;
if (empty($this->behaviors[$behavior])) {
$this->behaviors[$behavior] = array();
}
if ($config) {
$this->behaviors[$behavior][] = $config;
}
return $this; return $this;
} }

View file

@ -20,18 +20,10 @@
* @group markup * @group markup
*/ */
class PhabricatorRemarkupRuleDifferential class PhabricatorRemarkupRuleDifferential
extends PhutilRemarkupRule { extends PhabricatorRemarkupRuleObjectName {
public function apply($text) { protected function getObjectNamePrefix() {
return preg_replace_callback( return 'D';
'@\bD(\d+)\b@',
array($this, 'markupDifferentialLink'),
$text);
}
public function markupDifferentialLink($matches) {
return $this->getEngine()->storeText(
'<a href="/D'.$matches[1].'">D'.$matches[1].'</a>');
} }
} }

View file

@ -6,7 +6,7 @@
phutil_require_module('phutil', 'markup/engine/remarkup/markuprule/base'); phutil_require_module('phabricator', 'infrastructure/markup/remarkup/markuprule/objectname');
phutil_require_source('PhabricatorRemarkupRuleDifferential.php'); phutil_require_source('PhabricatorRemarkupRuleDifferential.php');

View file

@ -20,18 +20,10 @@
* @group markup * @group markup
*/ */
class PhabricatorRemarkupRuleManiphest class PhabricatorRemarkupRuleManiphest
extends PhutilRemarkupRule { extends PhabricatorRemarkupRuleObjectName {
public function apply($text) { protected function getObjectNamePrefix() {
return preg_replace_callback( return 'T';
'@\bT(\d+)\b@',
array($this, 'markupManiphestLink'),
$text);
}
public function markupManiphestLink($matches) {
return $this->getEngine()->storeText(
'<a href="/T'.$matches[1].'">T'.$matches[1].'</a>');
} }
} }

View file

@ -6,7 +6,7 @@
phutil_require_module('phutil', 'markup/engine/remarkup/markuprule/base'); phutil_require_module('phabricator', 'infrastructure/markup/remarkup/markuprule/objectname');
phutil_require_source('PhabricatorRemarkupRuleManiphest.php'); phutil_require_source('PhabricatorRemarkupRuleManiphest.php');

View file

@ -0,0 +1,57 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group markup
*/
abstract class PhabricatorRemarkupRuleObjectName
extends PhutilRemarkupRule {
abstract protected function getObjectNamePrefix();
public function apply($text) {
$prefix = $this->getObjectNamePrefix();
return preg_replace_callback(
"@\b{$prefix}(\d+)(?:#(\d+))?\b@",
array($this, 'markupObjectNameLink'),
$text);
}
public function markupObjectNameLink($matches) {
$prefix = $this->getObjectNamePrefix();
$id = $matches[1];
if (isset($matches[2])) {
$comment_id = $matches[2];
$href = "/{$prefix}{$id}#comment-{$comment_id}";
$text = "{$prefix}{$id}#{$comment_id}";
} else {
$href = "/{$prefix}{$id}";
$text = "{$prefix}{$id}";
}
return $this->getEngine()->storeText(
phutil_render_tag(
'a',
array(
'href' => $href,
),
$text));
}
}

View file

@ -0,0 +1,13 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'markup/engine/remarkup/markuprule/base');
phutil_require_source('PhabricatorRemarkupRuleObjectName.php');

View file

@ -65,14 +65,7 @@ final class AphrontFormView extends AphrontView {
public function render() { public function render() {
require_celerity_resource('aphront-form-view-css'); require_celerity_resource('aphront-form-view-css');
static $initialized_behavior; Javelin::initBehavior('aphront-form-disable-on-submit');
if (!$initialized_behavior) {
// TODO: This is sort of a yucky hack.
$initialized_behavior = true;
Javelin::initBehavior(
'aphront-form-disable-on-submit',
array());
}
return javelin_render_tag( return javelin_render_tag(
'form', 'form',

View file

@ -3,14 +3,29 @@
*/ */
.differential-comment-date { .differential-comment {
padding: 3px 6px;
margin-bottom: 10px;
border: 1px solid transparent;
}
.differential-comment-list .anchor-target {
background-color: #ffffdd;
border-color: #ffff00;
}
.differential-comment-info {
color: #666666; color: #666666;
float: right; float: right;
font-size: 11px; font-size: 11px;
margin: 0em; margin: 0 0 3px;
padding-top: 6px; padding-top: 6px;
} }
.differential-comment-info a {
font-weight: bold;
}
.differential-comment-title { .differential-comment-title {
font-weight: bold; font-weight: bold;
height: 16px; height: 16px;
@ -24,7 +39,6 @@
padding-left: 62px; padding-left: 62px;
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: 6px 0px; background-position: 6px 0px;
margin-bottom: 14px;
} }
.differential-comment-content { .differential-comment-content {

View file

@ -2,10 +2,21 @@
* @provides maniphest-transaction-detail-css * @provides maniphest-transaction-detail-css
*/ */
.maniphest-trnasaction-list-view {
padding: .5em 1.5em;
}
.maniphest-transaction-detail-container { .maniphest-transaction-detail-container {
margin: 1em 1em 1.5em; margin: 1em 1em 1.25em;
background: 0px 0px no-repeat; background: 2px 2px no-repeat;
min-height: 50px; min-height: 50px;
padding: 2px 2px;
border: 1px solid transparent;
}
.maniphest-transaction-list-view .anchor-target {
background-color: #ffffdd;
border-color: #ffff00;
} }
.maniphest-transaction-detail-container .upforgrab { .maniphest-transaction-detail-container .upforgrab {

View file

@ -0,0 +1,34 @@
/**
* @provides javelin-behavior-phabricator-watch-anchor
* @requires javelin-behavior
* javelin-stratcom
* javelin-util
* javelin-dom
*/
JX.behavior('phabricator-watch-anchor', function() {
var highlighted;
function highlight() {
highlighted && JX.DOM.alterClass(highlighted, 'anchor-target', false);
try {
highlighted = JX.$('anchor-' + window.location.hash.replace('#', ''));
JX.DOM.alterClass(highlighted, 'anchor-target', true);
} catch (ex) {
if (ex === JX.$.NotFound) {
highlighted = null;
} else {
throw ex;
}
}
}
JX.Stratcom.listen(
'hashchange',
null,
// Defer invocation so other listeners can update the document.
function() { JX.defer(highlight); });
JX.defer(highlight);
});

View file

@ -7,13 +7,43 @@
JX.behavior('differential-show-all-comments', function(config) { JX.behavior('differential-show-all-comments', function(config) {
var shown = false;
function reveal(node) {
if (shown) {
return;
}
shown = true;
node = node || JX.DOM.find(
document.body,
'div',
'differential-all-comments-container');
if (node) {
JX.DOM.setContent(node, JX.$H(config.markup));
}
}
// Reveal the hidden comments if the user clicks "Show All Comments", or if
// there's an anchor in the URL, since we don't want to link to "#comment-3"
// and have it collapsed.
if (window.location.hash) {
reveal();
} else {
JX.Stratcom.listen(
'hashchange',
null,
function(e) {
if (window.location.hash.match(/comment/)) {
reveal();
}
});
}
JX.Stratcom.listen( JX.Stratcom.listen(
'click', 'click',
'differential-show-all-comments', 'differential-show-all-comments',
function(e) { function(e) {
JX.DOM.setContent( reveal(e.getNode('differential-all-comments-container'));
e.getNode('differential-all-comments-container'),
JX.$H(config.markup));
e.kill(); e.kill();
}); });