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; 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 * Whether the given ETag matches a cached file
* *

View File

@ -4,6 +4,7 @@
namespace Icinga\Web\Helper; namespace Icinga\Web\Helper;
use Closure; use Closure;
use Icinga\Web\FileCache;
use InvalidArgumentException; use InvalidArgumentException;
class HtmlPurifier class HtmlPurifier
@ -29,11 +30,16 @@ class HtmlPurifier
$purifierConfig = \HTMLPurifier_Config::createDefault(); $purifierConfig = \HTMLPurifier_Config::createDefault();
$purifierConfig->set('Core.EscapeNonASCIICharacters', true); $purifierConfig->set('Core.EscapeNonASCIICharacters', true);
$purifierConfig->set('Attr.AllowedFrameTargets', array('_blank')); $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: // This avoids permission problems:
// $purifierConfig->set('Core.DefinitionCache', null); // $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.Base', 'http://www.example.com');
// $purifierConfig->set('URI.MakeAbsolute', true); // $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; namespace Icinga\Web\View;
use Icinga\Util\StringHelper; use Icinga\Util\StringHelper;
use Icinga\Web\Helper\Markdown;
$this->addHelperFunction('ellipsis', function ($string, $maxLength, $ellipsis = '...') { $this->addHelperFunction('ellipsis', function ($string, $maxLength, $ellipsis = '...') {
return StringHelper::ellipsis($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) { $this->addHelperFunction('nl2br', function ($string) {
return nl2br(str_replace(array('\r\n', '\r', '\n'), '<br>', $string), false); 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\Forms\Announcement\AcknowledgeAnnouncementForm;
use Icinga\Web\Announcement\AnnouncementCookie; use Icinga\Web\Announcement\AnnouncementCookie;
use Icinga\Web\Announcement\AnnouncementIniRepository; use Icinga\Web\Announcement\AnnouncementIniRepository;
use Icinga\Web\Helper\HtmlPurifier; use Icinga\Web\Helper\Markdown;
/** /**
* Render announcements * Render announcements
@ -36,13 +36,12 @@ class Announcements extends AbstractWidget
$announcements = $repo->findActive(); $announcements = $repo->findActive();
$announcements->applyFilter($acked); $announcements->applyFilter($acked);
if ($announcements->hasResult()) { if ($announcements->hasResult()) {
$purifier = new HtmlPurifier(array('HTML.Allowed' => 'b,a[href|target],i,*[class]'));
$html = '<ul role="alert" id="announcements">'; $html = '<ul role="alert" id="announcements">';
foreach ($announcements as $announcement) { foreach ($announcements as $announcement) {
$ackForm = new AcknowledgeAnnouncementForm(); $ackForm = new AcknowledgeAnnouncementForm();
$ackForm->populate(array('hash' => $announcement->hash)); $ackForm->populate(array('hash' => $announcement->hash));
$html .= '<li><div>' $html .= '<li><div>'
. $purifier->purify($announcement->message) . Markdown::text($announcement->message)
. '</div>' . '</div>'
. $ackForm . $ackForm
. '</li>'; . '</li>';

View File

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

View File

@ -136,7 +136,7 @@ class EventController extends Controller
*/ */
protected function comment($message) 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>
<tr title="<?= $this->translate('A comment, as entered by the author, associated with the scheduled downtime'); ?>"> <tr title="<?= $this->translate('A comment, as entered by the author, associated with the scheduled downtime'); ?>">
<th><?= $this->translate('Comment') ?></th> <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> </tr>
</tbody> </tbody>
</table> </table>

View File

@ -66,5 +66,5 @@
</span> </span>
</div> </div>
<p class="comment-text"<?php if (isset($textId)): ?> id="<?= $textId ?>"<?php endif ?>> <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> </p>

View File

@ -82,6 +82,6 @@
</span> </span>
</div> </div>
<p class="comment-text"<?php if (isset($textId)): ?> id="<?= $textId ?>"<?php endif ?>> <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> </p>
</td> </td>

View File

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

View File

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

View File

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

View File

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

View File

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