Fixed wrong task link generation within Markdown text

This commit is contained in:
Frederic Guillot
2016-04-29 17:32:43 -04:00
parent 81a25cbe63
commit fc8f8748b9
8 changed files with 124 additions and 83 deletions

View File

@@ -22,6 +22,7 @@ Improvements:
Bug fixes: Bug fixes:
* Fixed wrong task link generation within Markdown text
* Fixed wrong URL on comment toggle link for sorting * Fixed wrong URL on comment toggle link for sorting
* Fixed form submission with Meta+Enter keyboard shortcut * Fixed form submission with Meta+Enter keyboard shortcut
* Removed PHP notices in comment suppression view * Removed PHP notices in comment suppression view

View File

@@ -15,12 +15,12 @@ use Pimple\Container;
class Markdown extends Parsedown class Markdown extends Parsedown
{ {
/** /**
* Link params for tasks * Task links generated will use the project token instead
* *
* @access private * @access private
* @var array * @var boolean
*/ */
private $link = array(); private $isPublicLink = false;
/** /**
* Container * Container
@@ -35,11 +35,11 @@ class Markdown extends Parsedown
* *
* @access public * @access public
* @param Container $container * @param Container $container
* @param array $link * @param boolean $isPublicLink
*/ */
public function __construct(Container $container, array $link) public function __construct(Container $container, $isPublicLink)
{ {
$this->link = $link; $this->isPublicLink = $isPublicLink;
$this->container = $container; $this->container = $container;
$this->InlineTypes['#'][] = 'TaskLink'; $this->InlineTypes['#'][] = 'TaskLink';
$this->InlineTypes['@'][] = 'UserLink'; $this->InlineTypes['@'][] = 'UserLink';
@@ -53,26 +53,26 @@ class Markdown extends Parsedown
* *
* @access public * @access public
* @param array $Excerpt * @param array $Excerpt
* @return array * @return array|null
*/ */
protected function inlineTaskLink(array $Excerpt) protected function inlineTaskLink(array $Excerpt)
{ {
if (! empty($this->link) && preg_match('!#(\d+)!i', $Excerpt['text'], $matches)) { if (preg_match('!#(\d+)!i', $Excerpt['text'], $matches)) {
$url = $this->container['helper']->url->href( $link = $this->buildTaskLink($matches[1]);
$this->link['controller'],
$this->link['action'],
$this->link['params'] + array('task_id' => $matches[1])
);
return array( if (! empty($link)) {
'extent' => strlen($matches[0]), return array(
'element' => array( 'extent' => strlen($matches[0]),
'name' => 'a', 'element' => array(
'text' => $matches[0], 'name' => 'a',
'attributes' => array('href' => $url) 'text' => $matches[0],
), 'attributes' => array('href' => $link),
); ),
);
}
} }
return null;
} }
/** /**
@@ -82,11 +82,11 @@ class Markdown extends Parsedown
* *
* @access public * @access public
* @param array $Excerpt * @param array $Excerpt
* @return array * @return array|null
*/ */
protected function inlineUserLink(array $Excerpt) protected function inlineUserLink(array $Excerpt)
{ {
if (preg_match('/^@([^\s]+)/', $Excerpt['text'], $matches)) { if (! $this->isPublicLink && preg_match('/^@([^\s]+)/', $Excerpt['text'], $matches)) {
$user_id = $this->container['user']->getIdByUsername($matches[1]); $user_id = $this->container['user']->getIdByUsername($matches[1]);
if (! empty($user_id)) { if (! empty($user_id)) {
@@ -102,5 +102,40 @@ class Markdown extends Parsedown
); );
} }
} }
return null;
}
/**
* Build task link
*
* @access private
* @param integer $task_id
* @return string
*/
private function buildTaskLink($task_id)
{
if ($this->isPublicLink) {
$token = $this->container['memoryCache']->proxy($this->container['taskFinder'], 'getProjectToken', $task_id);
if (! empty($token)) {
return $this->container['helper']->url->href(
'task',
'readonly',
array(
'token' => $token,
'task_id' => $task_id,
)
);
}
return '';
}
return $this->container['helper']->url->href(
'task',
'show',
array('task_id' => $task_id)
);
} }
} }

View File

@@ -27,13 +27,13 @@ class TextHelper extends Base
/** /**
* Markdown transformation * Markdown transformation
* *
* @param string $text Markdown content * @param string $text
* @param array $link Link parameters for replacement * @param boolean $isPublicLink
* @return string * @return string
*/ */
public function markdown($text, array $link = array()) public function markdown($text, $isPublicLink = false)
{ {
$parser = new Markdown($this->container, $link); $parser = new Markdown($this->container, $isPublicLink);
$parser->setMarkupEscaped(MARKDOWN_ESCAPE_HTML); $parser->setMarkupEscaped(MARKDOWN_ESCAPE_HTML);
return $parser->text($text); return $parser->text($text);
} }

View File

@@ -452,4 +452,20 @@ class TaskFinder extends Base
{ {
return $this->db->table(Task::TABLE)->eq('id', $task_id)->exists(); return $this->db->table(Task::TABLE)->eq('id', $task_id)->exists();
} }
/**
* Get project token
*
* @access public
* @param integer $task_id
* @return string
*/
public function getProjectToken($task_id)
{
return $this->db
->table(Task::TABLE)
->eq(Task::TABLE.'.id', $task_id)
->join(Project::TABLE, 'id', 'project_id')
->findOneColumn(Project::TABLE.'.token');
}
} }

View File

@@ -12,29 +12,7 @@
<div class="comment-content"> <div class="comment-content">
<div class="markdown"> <div class="markdown">
<?php if (isset($is_public) && $is_public): ?> <?= $this->text->markdown($comment['comment'], isset($is_public) && $is_public) ?>
<?= $this->text->markdown(
$comment['comment'],
array(
'controller' => 'task',
'action' => 'readonly',
'params' => array(
'token' => $project['token']
)
)
) ?>
<?php else: ?>
<?= $this->text->markdown(
$comment['comment'],
array(
'controller' => 'task',
'action' => 'show',
'params' => array(
'project_id' => $task['project_id']
)
)
) ?>
<?php endif ?>
</div> </div>
</div> </div>

View File

@@ -4,29 +4,7 @@
</div> </div>
<div class="accordion-content"> <div class="accordion-content">
<article class="markdown"> <article class="markdown">
<?php if (! isset($is_public)): ?> <?= $this->text->markdown($task['description'], isset($is_public) && $is_public) ?>
<?= $this->text->markdown(
$task['description'],
array(
'controller' => 'task',
'action' => 'show',
'params' => array(
'project_id' => $task['project_id']
)
)
) ?>
<?php else: ?>
<?= $this->text->markdown(
$task['description'],
array(
'controller' => 'task',
'action' => 'readonly',
'params' => array(
'token' => $project['token']
)
)
) ?>
<?php endif ?>
</article> </article>
</div> </div>
</section> </section>

View File

@@ -3,30 +3,43 @@
require_once __DIR__.'/../Base.php'; require_once __DIR__.'/../Base.php';
use Kanboard\Helper\TextHelper; use Kanboard\Helper\TextHelper;
use Kanboard\Model\Project;
use Kanboard\Model\TaskCreation;
class TextHelperTest extends Base class TextHelperTest extends Base
{ {
public function testMarkdownTaskLink() public function testMarkdownTaskLink()
{ {
$h = new TextHelper($this->container); $helper = new TextHelper($this->container);
$projectModel = new Project($this->container);
$taskCreationModel = new TaskCreation($this->container);
$this->assertEquals('<p>Test</p>', $h->markdown('Test')); $this->assertEquals(1, $projectModel->create(array('name' => 'Project #1')));
$this->assertTrue($projectModel->enablePublicAccess(1));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'Task #1', 'project_id' => 1)));
$project = $projectModel->getById(1);
$this->assertEquals('<p>Test</p>', $helper->markdown('Test'));
$this->assertEquals( $this->assertEquals(
'<p>Task #123</p>', '<p>Task <a href="?controller=task&amp;action=show&amp;task_id=123">#123</a></p>',
$h->markdown('Task #123') $helper->markdown('Task #123')
); );
$this->assertEquals( $this->assertEquals(
'<p>Task <a href="?controller=a&amp;action=b&amp;c=d&amp;task_id=123">#123</a></p>', '<p>Task #123</p>',
$h->markdown('Task #123', array('controller' => 'a', 'action' => 'b', 'params' => array('c' => 'd'))) $helper->markdown('Task #123', true)
);
$this->assertEquals(
'<p>Task <a href="?controller=task&amp;action=readonly&amp;token='.$project['token'].'&amp;task_id=1">#1</a></p>',
$helper->markdown('Task #1', true)
); );
$this->assertEquals( $this->assertEquals(
'<p>Check that: <a href="http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454">http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454</a></p>', '<p>Check that: <a href="http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454">http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454</a></p>',
$h->markdown( $helper->markdown(
'Check that: http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454', 'Check that: http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454'
array('controller' => 'a', 'action' => 'b', 'params' => array('c' => 'd'))
) )
); );
} }
@@ -35,6 +48,7 @@ class TextHelperTest extends Base
{ {
$h = new TextHelper($this->container); $h = new TextHelper($this->container);
$this->assertEquals('<p>Text <a href="?controller=user&amp;action=profile&amp;user_id=1" class="user-mention-link">@admin</a> @notfound</p>', $h->markdown('Text @admin @notfound')); $this->assertEquals('<p>Text <a href="?controller=user&amp;action=profile&amp;user_id=1" class="user-mention-link">@admin</a> @notfound</p>', $h->markdown('Text @admin @notfound'));
$this->assertEquals('<p>Text @admin @notfound</p>', $h->markdown('Text @admin @notfound', true));
} }
public function testFormatBytes() public function testFormatBytes()

View File

@@ -93,4 +93,23 @@ class TaskFinderTest extends Base
$this->assertEquals(1, $tf->countByProjectId(1)); $this->assertEquals(1, $tf->countByProjectId(1));
$this->assertEquals(2, $tf->countByProjectId(2)); $this->assertEquals(2, $tf->countByProjectId(2));
} }
public function testGetProjectToken()
{
$taskCreationModel = new TaskCreation($this->container);
$taskFinderModel = new TaskFinder($this->container);
$projectModel = new Project($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'Project #1')));
$this->assertEquals(2, $projectModel->create(array('name' => 'Project #2')));
$this->assertTrue($projectModel->enablePublicAccess(1));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'Task #1', 'project_id' => 1)));
$this->assertEquals(2, $taskCreationModel->create(array('title' => 'Task #2', 'project_id' => 2)));
$project = $projectModel->getById(1);
$this->assertEquals($project['token'], $taskFinderModel->getProjectToken(1));
$this->assertEmpty($taskFinderModel->getProjectToken(2));
}
} }