Merge pull request #3814 from Icinga/feature/make-notes-and-comments-markdown-aware-3684

Make notes, comments and announcements markdown aware
This commit is contained in:
Johannes Meyer 2019-07-02 15:30:21 +02:00 committed by GitHub
commit 36524bcd94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 63 additions and 25 deletions

View File

@ -154,6 +154,27 @@ class FileCache
return $this->basedir . '/' . $file;
}
/**
* Prepare a sub directory with the given name and return its path
*
* @param string $name
*
* @return string|false Returns FALSE in case the cache is not enabled or an error occurred
*/
public function directory($name)
{
if (! $this->enabled) {
return false;
}
$path = $this->filename($name);
if (! is_dir($path) && ! @mkdir($path, octdec('1750'), true)) {
return false;
}
return $path;
}
/**
* Whether the given ETag matches a cached file
*

View File

@ -4,6 +4,7 @@
namespace Icinga\Web\Helper;
use Closure;
use Icinga\Web\FileCache;
use InvalidArgumentException;
class HtmlPurifier
@ -29,11 +30,16 @@ class HtmlPurifier
$purifierConfig = \HTMLPurifier_Config::createDefault();
$purifierConfig->set('Core.EscapeNonASCIICharacters', true);
$purifierConfig->set('Attr.AllowedFrameTargets', array('_blank'));
if (($cachePath = FileCache::instance()->directory('htmlpurifier.cache')) !== false) {
$purifierConfig->set('Cache.SerializerPath', $cachePath);
} else {
$purifierConfig->set('Cache.DefinitionImpl', null);
}
// This avoids permission problems:
// $purifierConfig->set('Core.DefinitionCache', null);
$purifierConfig->set('Cache.DefinitionImpl', null);
// TODO: Use a cache directory:
// $purifierConfig->set('Cache.SerializerPath', '/var/spool/whatever');
// $purifierConfig->set('URI.Base', 'http://www.example.com');
// $purifierConfig->set('URI.MakeAbsolute', true);

View File

@ -0,0 +1,15 @@
<?php
/* Icinga Web 2 | (c) 2019 Icinga GmbH | GPLv2+ */
namespace Icinga\Web\Helper;
use Parsedown;
class Markdown
{
public static function text($content)
{
require_once 'Parsedown/Parsedown.php';
return HtmlPurifier::process(Parsedown::instance()->text($content));
}
}

View File

@ -4,6 +4,7 @@
namespace Icinga\Web\View;
use Icinga\Util\StringHelper;
use Icinga\Web\Helper\Markdown;
$this->addHelperFunction('ellipsis', function ($string, $maxLength, $ellipsis = '...') {
return StringHelper::ellipsis($string, $maxLength, $ellipsis);
@ -12,3 +13,7 @@ $this->addHelperFunction('ellipsis', function ($string, $maxLength, $ellipsis =
$this->addHelperFunction('nl2br', function ($string) {
return nl2br(str_replace(array('\r\n', '\r', '\n'), '<br>', $string), false);
});
$this->addHelperFunction('markdown', function ($content) {
return Markdown::text($content);
});

View File

@ -8,7 +8,7 @@ use Icinga\Data\Filter\Filter;
use Icinga\Forms\Announcement\AcknowledgeAnnouncementForm;
use Icinga\Web\Announcement\AnnouncementCookie;
use Icinga\Web\Announcement\AnnouncementIniRepository;
use Icinga\Web\Helper\HtmlPurifier;
use Icinga\Web\Helper\Markdown;
/**
* Render announcements
@ -36,13 +36,12 @@ class Announcements extends AbstractWidget
$announcements = $repo->findActive();
$announcements->applyFilter($acked);
if ($announcements->hasResult()) {
$purifier = new HtmlPurifier(array('HTML.Allowed' => 'b,a[href|target],i,*[class]'));
$html = '<ul role="alert" id="announcements">';
foreach ($announcements as $announcement) {
$ackForm = new AcknowledgeAnnouncementForm();
$ackForm->populate(array('hash' => $announcement->hash));
$html .= '<li><div>'
. $purifier->purify($announcement->message)
. Markdown::text($announcement->message)
. '</div>'
. $ackForm
. '</li>';

View File

@ -8,7 +8,7 @@ use Icinga\Application\Hook\ApplicationStateHook;
use Icinga\Authentication\Auth;
use Icinga\Forms\AcknowledgeApplicationStateMessageForm;
use Icinga\Web\ApplicationStateCookie;
use Icinga\Web\Helper\HtmlPurifier;
use Icinga\Web\Helper\Markdown;
/**
* Render application state messages
@ -27,12 +27,6 @@ class ApplicationStateMessages extends AbstractWidget
return $active;
}
protected function getPurifier()
{
return new HtmlPurifier(['HTML.Allowed' => 'b,a[href|target],i,*[class]']);
}
public function render()
{
$enabled = Auth::getInstance()
@ -55,8 +49,6 @@ class ApplicationStateMessages extends AbstractWidget
return '<div style="display: none;"></div>';
}
$purifier = $this->getPurifier();
$html = '<div>';
reset($active);
@ -69,7 +61,7 @@ class ApplicationStateMessages extends AbstractWidget
$ackForm = new AcknowledgeApplicationStateMessageForm();
$ackForm->populate(['id' => $id]);
$html .= $purifier->purify($message) . $ackForm;
$html .= Markdown::text($message) . $ackForm;
$html .= '</div>';

View File

@ -136,7 +136,7 @@ class EventController extends Controller
*/
protected function comment($message)
{
return $this->view->nl2br($this->view->createTicketLinks($this->view->escapeComment($message)));
return $this->view->nl2br($this->view->createTicketLinks($this->view->markdown($message)));
}
/**

View File

@ -45,7 +45,7 @@
</tr>
<tr title="<?= $this->translate('A comment, as entered by the author, associated with the scheduled downtime'); ?>">
<th><?= $this->translate('Comment') ?></th>
<td class="comment-text"><?= $this->nl2br($this->escapeComment($this->downtime->comment)) ?></td>
<td class="comment-text"><?= $this->nl2br($this->markdown($this->downtime->comment)) ?></td>
</tr>
</tbody>
</table>

View File

@ -66,5 +66,5 @@
</span>
</div>
<p class="comment-text"<?php if (isset($textId)): ?> id="<?= $textId ?>"<?php endif ?>>
<?= $this->nl2br($this->escapeComment($comment->comment)) ?>
<?= $this->nl2br($this->markdown($comment->comment)) ?>
</p>

View File

@ -82,6 +82,6 @@
</span>
</div>
<p class="comment-text"<?php if (isset($textId)): ?> id="<?= $textId ?>"<?php endif ?>>
<?= $this->nl2br($this->escapeComment($downtime->comment)) ?>
<?= $this->nl2br($this->markdown($downtime->comment)) ?>
</p>
</td>

View File

@ -182,7 +182,7 @@ $rowAction = Url::fromPath('monitoring/event/show');
<?php endif ?>
<p class="overview-plugin-output"><?php if ($icon) {
echo $this->icon($icon, $iconTitle);
} ?><?= $this->nl2br($this->createTicketLinks($this->escapeComment($msg)))
} ?><?= $this->nl2br($this->createTicketLinks($this->markdown($msg)))
// TODO(ak): this allows only a[href] in messages, but plugin output allows more
?></p>
</td>

View File

@ -44,7 +44,7 @@ $acknowledgement = $object->acknowledgement;
} ?>
</dt>
<dd>
<?= $this->nl2br($this->createTicketLinks($this->escapeComment($acknowledgement->getComment()))) ?>
<?= $this->nl2br($this->createTicketLinks($this->markdown($acknowledgement->getComment()))) ?>
</dd>
</dl>
<?php elseif (isset($removeAckForm)): ?>

View File

@ -68,7 +68,7 @@ if (empty($object->comments) && ! $addLink) {
} ?>
</dt>
<dd>
<?= $this->nl2br($this->createTicketLinks($this->escapeComment($comment->comment))) ?>
<?= $this->nl2br($this->createTicketLinks($this->markdown($comment->comment))) ?>
</dd>
<?php endforeach ?>
</dl>

View File

@ -108,7 +108,7 @@ if (empty($object->comments) && ! $addLink) {
} ?>
</dt>
<dd>
<?= $this->nl2br($this->createTicketLinks($this->escapeComment($downtime->comment))) ?>
<?= $this->nl2br($this->createTicketLinks($this->markdown($downtime->comment))) ?>
</dd>
<?php endforeach ?>
</dl>

View File

@ -38,7 +38,7 @@ if (($navigation->isEmpty() || ! $navigation->hasRenderableItems()) && $notes ==
<td>
<?= $navigation->getRenderer() ?>
<?php if ($notes !== ''): ?>
<p><?= $this->nl2br($this->escape($notes)) ?></p>
<section class="notes"><?= $this->markdown($notes) ?></section>
<?php endif ?>
</td>
</tr>