diff options
Diffstat (limited to 'library/Icinga/Web/Helper')
-rw-r--r-- | library/Icinga/Web/Helper/CookieHelper.php | 81 | ||||
-rw-r--r-- | library/Icinga/Web/Helper/HtmlPurifier.php | 95 | ||||
-rw-r--r-- | library/Icinga/Web/Helper/Markdown.php | 34 | ||||
-rw-r--r-- | library/Icinga/Web/Helper/Markdown/LinkTransformer.php | 73 |
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(); + } +} |