summaryrefslogtreecommitdiffstats
path: root/library/Icinga/Web/Helper
diff options
context:
space:
mode:
Diffstat (limited to 'library/Icinga/Web/Helper')
-rw-r--r--library/Icinga/Web/Helper/CookieHelper.php81
-rw-r--r--library/Icinga/Web/Helper/HtmlPurifier.php95
-rw-r--r--library/Icinga/Web/Helper/Markdown.php34
-rw-r--r--library/Icinga/Web/Helper/Markdown/LinkTransformer.php73
4 files changed, 283 insertions, 0 deletions
diff --git a/library/Icinga/Web/Helper/CookieHelper.php b/library/Icinga/Web/Helper/CookieHelper.php
new file mode 100644
index 0000000..cc7c448
--- /dev/null
+++ b/library/Icinga/Web/Helper/CookieHelper.php
@@ -0,0 +1,81 @@
+<?php
+/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Web\Helper;
+
+use Icinga\Web\Request;
+
+/**
+ * Helper Class Cookie
+ */
+class CookieHelper
+{
+ /**
+ * The name of the control cookie
+ */
+ const CHECK_COOKIE = '_chc';
+
+ /**
+ * The request
+ *
+ * @var Request
+ */
+ protected $request;
+
+ /**
+ * Create a new cookie
+ *
+ * @param Request $request
+ */
+ public function __construct(Request $request)
+ {
+ $this->request = $request;
+ }
+
+ /**
+ * Check whether cookies are supported or not
+ *
+ * @return bool
+ */
+ public function isSupported()
+ {
+ if (! empty($_COOKIE)) {
+ $this->cleanupCheck();
+ return true;
+ }
+
+ $url = $this->request->getUrl();
+
+ if ($url->hasParam('_checkCookie') && empty($_COOKIE)) {
+ return false;
+ }
+
+ if (! $url->hasParam('_checkCookie')) {
+ $this->provideCheck();
+ }
+
+ return false;
+ }
+
+ /**
+ * Prepare check to detect cookie support
+ */
+ public function provideCheck()
+ {
+ setcookie(self::CHECK_COOKIE, '1');
+
+ $requestUri = $this->request->getUrl()->addParams(array('_checkCookie' => 1));
+ $this->request->getResponse()->redirectAndExit($requestUri);
+ }
+
+ /**
+ * Cleanup the cookie support check
+ */
+ public function cleanupCheck()
+ {
+ if ($this->request->getUrl()->hasParam('_checkCookie') && isset($_COOKIE[self::CHECK_COOKIE])) {
+ $requestUri =$this->request->getUrl()->without('_checkCookie');
+ $this->request->getResponse()->redirectAndExit($requestUri);
+ }
+ }
+}
diff --git a/library/Icinga/Web/Helper/HtmlPurifier.php b/library/Icinga/Web/Helper/HtmlPurifier.php
new file mode 100644
index 0000000..19fd207
--- /dev/null
+++ b/library/Icinga/Web/Helper/HtmlPurifier.php
@@ -0,0 +1,95 @@
+<?php
+/* Icinga Web 2 | (c) 2018 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Web\Helper;
+
+use Closure;
+use Icinga\Web\FileCache;
+use InvalidArgumentException;
+
+class HtmlPurifier
+{
+ /**
+ * The actual purifier instance
+ *
+ * @var \HTMLPurifier
+ */
+ protected $purifier;
+
+ /**
+ * Create a new HtmlPurifier
+ *
+ * @param array|Closure $config Additional configuration
+ */
+ public function __construct($config = null)
+ {
+ $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('URI.Base', 'http://www.example.com');
+ // $purifierConfig->set('URI.MakeAbsolute', true);
+
+ $this->configure($purifierConfig);
+
+ if ($config instanceof Closure) {
+ call_user_func($config, $purifierConfig);
+ } elseif (is_array($config)) {
+ $purifierConfig->loadArray($config);
+ } elseif ($config !== null) {
+ throw new InvalidArgumentException('$config must be either a Closure or array');
+ }
+
+ $this->purifier = new \HTMLPurifier($purifierConfig);
+ }
+
+ /**
+ * Apply additional default configuration
+ *
+ * May be overwritten by more concrete purifier implementations.
+ *
+ * @param \HTMLPurifier_Config $config
+ */
+ protected function configure($config)
+ {
+ }
+
+ /**
+ * Purify and return the given HTML string
+ *
+ * @param string $html
+ * @param array|Closure $config Configuration to use instead of the default
+ *
+ * @return string
+ */
+ public function purify($html, $config = null)
+ {
+ return $this->purifier->purify($html, $config);
+ }
+
+ /**
+ * Purify and return the given HTML string
+ *
+ * Convenience method to bypass object creation.
+ *
+ * @param string $html
+ * @param array|Closure $config Additional configuration
+ *
+ * @return string
+ */
+ public static function process($html, $config = null)
+ {
+ $purifier = new static($config);
+
+ return $purifier->purify($html);
+ }
+}
diff --git a/library/Icinga/Web/Helper/Markdown.php b/library/Icinga/Web/Helper/Markdown.php
new file mode 100644
index 0000000..cb854b4
--- /dev/null
+++ b/library/Icinga/Web/Helper/Markdown.php
@@ -0,0 +1,34 @@
+<?php
+/* Icinga Web 2 | (c) 2019 Icinga GmbH | GPLv2+ */
+
+namespace Icinga\Web\Helper;
+
+use Icinga\Web\Helper\Markdown\LinkTransformer;
+use Parsedown;
+
+class Markdown
+{
+ public static function line($content, $config = null)
+ {
+ if ($config === null) {
+ $config = function (\HTMLPurifier_Config $config) {
+ $config->set('HTML.Parent', 'span'); // Only allow inline elements
+
+ LinkTransformer::attachTo($config);
+ };
+ }
+
+ return HtmlPurifier::process(Parsedown::instance()->line($content), $config);
+ }
+
+ public static function text($content, $config = null)
+ {
+ if ($config === null) {
+ $config = function (\HTMLPurifier_Config $config) {
+ LinkTransformer::attachTo($config);
+ };
+ }
+
+ return HtmlPurifier::process(Parsedown::instance()->text($content), $config);
+ }
+}
diff --git a/library/Icinga/Web/Helper/Markdown/LinkTransformer.php b/library/Icinga/Web/Helper/Markdown/LinkTransformer.php
new file mode 100644
index 0000000..f323085
--- /dev/null
+++ b/library/Icinga/Web/Helper/Markdown/LinkTransformer.php
@@ -0,0 +1,73 @@
+<?php
+/* Icinga Web 2 | (c) 2021 Icinga GmbH | GPLv2+ */
+
+namespace Icinga\Web\Helper\Markdown;
+
+use HTMLPurifier_AttrTransform;
+use HTMLPurifier_Config;
+use ipl\Web\Url;
+
+class LinkTransformer extends HTMLPurifier_AttrTransform
+{
+ /**
+ * Link targets that are considered to have a thumbnail
+ *
+ * @var string[]
+ */
+ public static $IMAGE_FILES = [
+ 'jpg',
+ 'jpeg',
+ 'png',
+ 'bmp',
+ 'gif',
+ 'heif',
+ 'heic',
+ 'webp'
+ ];
+
+ public function transform($attr, $config, $context)
+ {
+ if (! isset($attr['href'])) {
+ return $attr;
+ }
+
+ $url = Url::fromPath($attr['href']);
+ $fileName = basename($url->getPath());
+
+ $ext = null;
+ if (($extAt = strrpos($fileName, '.')) !== false) {
+ $ext = substr($fileName, $extAt + 1);
+ }
+
+ $hasThumbnail = $ext !== null && in_array($ext, static::$IMAGE_FILES, true);
+ if ($hasThumbnail) {
+ // I would have liked to not only base this off of the extension, but also by
+ // whether there is an actual img tag inside the anchor. Seems not possible :(
+ $attr['class'] = 'with-thumbnail';
+ }
+
+ if (! isset($attr['target'])) {
+ if ($url->isExternal()) {
+ $attr['target'] = '_blank';
+ } else {
+ $attr['data-base-target'] = '_next';
+ }
+ }
+
+ return $attr;
+ }
+
+ public static function attachTo(HTMLPurifier_Config $config)
+ {
+ $module = $config->getHTMLDefinition(true)
+ ->getAnonymousModule();
+
+ if (isset($module->info['a'])) {
+ $a = $module->info['a'];
+ } else {
+ $a = $module->addBlankElement('a');
+ }
+
+ $a->attr_transform_post[] = new self();
+ }
+}