allow markdown in timesheet descriptions (#296)

This commit is contained in:
Kevin Papst
2018-09-02 12:10:49 +02:00
committed by GitHub
parent 9f6079ad8a
commit 4c277fe1b5
13 changed files with 77 additions and 8 deletions

View File

@@ -28,3 +28,11 @@
padding: 15px;
}
}
td {
&.timesheet-description {
& >ul {
padding-left: 10px;
}
}
}

View File

@@ -8,6 +8,9 @@ kimai:
# All configs related to timesheet and record management
timesheet:
# render timesheet descriptions with markdown
markdown_content: false
# Whether we display start and end time columns (false) or durations only (true).
# Setting this to true will also change the "edit timesheet" forms, more infos available in the configurations docu.
duration_only: false

View File

@@ -54,6 +54,10 @@ services:
arguments:
$dashboard: "%kimai.dashboard%"
App\Twig\MarkdownExtension:
arguments:
$timesheetAsMarkdown: "%kimai.timesheet.markdown%"
# ================================================================================
# DATABASE
# ================================================================================

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
{
"build/app.js": "/build/app.js?a67489c4fc07f30e6f86",
"build/app.css": "/build/app.css?e2bd74ce6119917ab0c8fd4de8d4b9c6",
"build/app.css": "/build/app.css?7ddba25b9e0ab7dec9690f128f8691c0",
"build/fonts/fa-solid-900.woff": "/build/fonts/fa-solid-900.woff?dfc040d5",
"build/images/blue@2x.png": "/build/images/blue@2x.png?2694acfd",
"build/fonts/fa-solid-900.woff2": "/build/fonts/fa-solid-900.woff2?e8a92a29",

View File

@@ -75,6 +75,7 @@ class AppExtension extends Extension implements PrependExtensionInterface
$container->setParameter('kimai.timesheet.rates', $config['timesheet']['rates']);
$container->setParameter('kimai.timesheet.rounding', $config['timesheet']['rounding']);
$container->setParameter('kimai.timesheet.duration_only', $config['timesheet']['duration_only']);
$container->setParameter('kimai.timesheet.markdown', $config['timesheet']['markdown_content']);
}
/**

View File

@@ -55,6 +55,9 @@ class Configuration implements ConfigurationInterface
->booleanNode('duration_only')
->defaultValue(false)
->end()
->booleanNode('markdown_content')
->defaultValue(false)
->end()
->arrayNode('rounding')
->requiresAtLeastOneElement()
->useAttributeAsKey('key')

View File

@@ -21,14 +21,19 @@ class MarkdownExtension extends \Twig_Extension
* @var Markdown
*/
private $markdown;
/**
* @var bool
*/
private $timesheetIsMarkdown = false;
/**
* MarkdownExtension constructor.
* @param Markdown $parser
*/
public function __construct(Markdown $parser)
public function __construct(Markdown $parser, bool $timesheetAsMarkdown = false)
{
$this->markdown = $parser;
$this->timesheetIsMarkdown = $timesheetAsMarkdown;
}
/**
@@ -38,9 +43,25 @@ class MarkdownExtension extends \Twig_Extension
{
return [
new TwigFilter('md2html', [$this, 'markdownToHtml'], ['is_safe' => ['html']]),
new TwigFilter('desc2html', [$this, 'timesheetContent'], ['is_safe' => ['html']]),
];
}
/**
* Transforms the timesheet description content into HTML.
*
* @param string $content
* @return string
*/
public function timesheetContent(string $content): string
{
if ($this->timesheetIsMarkdown) {
return $this->markdown->toHtml($content, false);
}
return nl2br($content);
}
/**
* Transforms the given Markdown content into HTML
*
@@ -49,6 +70,6 @@ class MarkdownExtension extends \Twig_Extension
*/
public function markdownToHtml(string $content): string
{
return $this->markdown->toHtml($content);
return $this->markdown->toHtml($content, true);
}
}

View File

@@ -31,10 +31,12 @@ class Markdown
/**
* @param string $text
* @param bool $safe
* @return string
*/
public function toHtml(string $text): string
public function toHtml(string $text, bool $safe = true): string
{
$this->parser->setSafeMode($safe);
return $this->parser->text($text);
}
}

View File

@@ -61,7 +61,7 @@
<a href="{{ path('admin_activity_edit', {'id': entry.activity.id}) }}">{{ widgets.label_activity(entry.activity) }}</a>
</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'username') }}"><a href="{{ path('user_profile'|route_alias, {'username' : entry.user.username}) }}">{{ widgets.label_user(entry.user) }}</a></td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'description') }}">{{ entry.description }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'description') }}">{{ entry.description|nl2br }}</td>
<td>
{% set actionButtons = {} %}
{% if is_granted('edit', entry) %}

View File

@@ -58,7 +58,7 @@
{% endif %}
<td class="{{ tables.data_table_column_class(tableName, columns, 'activity') }}">{{ widgets.label_activity(entry.activity) }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'description') }}">{{ entry.description|nl2br }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'description') }} timesheet-description">{{ entry.description|default('')|desc2html }}</td>
<td>
{% set actionButtons = {'edit': path('timesheet_edit', {'id' : entry.id, 'page': page})} %}

View File

@@ -22,8 +22,9 @@ class MarkdownExtensionTest extends TestCase
{
$sut = new MarkdownExtension(new Markdown());
$filters = $sut->getFilters();
$this->assertCount(1, $filters);
$this->assertCount(2, $filters);
$this->assertEquals('md2html', $filters[0]->getName());
$this->assertEquals('desc2html', $filters[1]->getName());
}
public function testMarkdownToHtml()
@@ -32,4 +33,20 @@ class MarkdownExtensionTest extends TestCase
$this->assertEquals('<p><em>test</em></p>', $sut->markdownToHtml('*test*'));
$this->assertEquals('<h1>foobar</h1>', $sut->markdownToHtml('# foobar'));
}
public function testTimesheetContent()
{
$sut = new MarkdownExtension(new Markdown(), false);
$this->assertEquals(
"- test<br />\n- foo",
$sut->timesheetContent("- test\n- foo")
);
$sut = new MarkdownExtension(new Markdown(), true);
$this->assertEquals(
"<ul>\n<li>test</li>\n<li>foo</li>\n</ul>\n<p>foo <strong>bar</strong></p>",
$sut->timesheetContent("- test\n- foo\n\nfoo __bar__")
);
}
}

View File

@@ -134,6 +134,16 @@ admin_lte:
## Timesheets (kimai.yaml)
### Descriptions with Markdown
The description for every timesheet entry can be formatted in two different ways, configured with the `markdown_content` setting.
- `false` - simple newlines in the description box will be displayed in the frontend as well (default)
- `true` - description will be rendered with a markdown engine, supporting simple lists and other HTML content
Allowing Markdown in timesheet descriptions is beautiful, but also could be a [security risk](https://github.com/erusev/parsedown/blob/master/README.md#security).
Kimai will only apply the markdown in the user timesheet and not in the admin section as additional security measure.
### Duration only
Kimai supports two modes for displaying and recording timesheet entries: