123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- <?php
- // https://github.com/picocms/Pico/issues/394#issuecomment-330030163
- // unicode arrow: 🡵 or 🡥 or ↗ (the last one seems to be the most common)
- // CSS:
- // a.external::after { line-height: 100%; vertical-align: super; font-size: 60%; content: "↗" }
- // and if you don't want the arrow on some external links, add the "noarrow" class. CSS:
- // a.external.noarrow::after { content: '' }
- class PicoTargetBlank extends AbstractPicoPlugin
- {
- /**
- * This plugin is enabled by default
- * but you can still disable it in config.yml:
- *
- * PicoTargetBlank:
- * enabled: false
- *
- * You can also set the rel= attribute, or disable it with an empty string:
- * rel: 'noopener noreferrer nofollow
- * or
- * rel: ''
- * etc.
- *
- * defaults to 'noopener noreferrer' if unspecified.
- * Also see:
- * pointjupiter.com/what-noopener-noreferrer-nofollow-explained/
- */
- const API_VERSION = 3;
- protected $doit = true;
- public function onConfigLoaded(&$config)
- {
- $this->rel = $this->getPluginConfig('rel', 'noopener noreferrer');
- $this->exclude = $this->getPluginConfig('exclude',null);
- }
- public function onRequestUrl(&$url)
- {
- $name = $url == "" ? "index" : $url;
- if(in_array($url,$this->exclude)) $this->doit=false;
- }
- public function onContentParsed(&$content)
- {
- if(trim($content)=="") return;
- if($this->doit === false) return;
- $dom = new DOMDocument();
- $dom->preserveWhiteSpace = true;
- // encoding problems require us to jump through hoops here...
- //~ $dom->loadXML($utf8.$content,LIBXML_NOWARNING|LIBXML_NONET|LIBXML_COMPACT|LIBXML_NOERROR);
- // $content is a html fragment. We like to modify its content with DOMDocument, but it prefers complete documents and completes them
- // if necessary. This leads to strangeness. The best thing I found is to give it a complete document with all required headers and tags,
- // then strip those off in the end. That way DOMDocument doesn't meddle, and we have control. Hah, hopefully.
- $dom->loadHTML('<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$content.'</body></html>',
- LIBXML_NONET|LIBXML_COMPACT|LIBXML_HTML_NODEFDTD|LIBXML_NOWARNING);
- //~ LIBXML_NOEMPTYTAG|LIBXML_NOENT|LIBXML_HTML_NOIMPLIED|LIBXML_NOERROR);
- $trim_before=92; // What we need to trim when saving (the above string added to $content)
- $trim_after=-15; // - " -
- // please read this: stackoverflow.com/a/11310258
- //~ $dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'));
- // This we need only once
- // remove port from HTTP_HOST, if applicable
- $host = preg_replace('/:[0-9]+$/','',$_SERVER['HTTP_HOST']);
- // add target _blank to external links
- $links = $dom->getElementsByTagName('a');
- //~ var_dump($links);
- //~ array_walk(iterator_to_array($links),'var_dump');
- //~ die();
- //~ echo "Before ";
- foreach ($links as $item) {
- $href = parse_url($item->getAttribute('href'), PHP_URL_HOST);
- //~ echo "foreach ".$href.' ';
- // IF THIS LINK IS EXTERNAL (differs from the server)...
- if (!empty($href) && $href != $host){
- // open in new tab
- $item->setAttribute('target','_blank');
- // set rel attribute (configurable)
- if (!empty($this->rel)) $item->setAttribute('rel',$this->rel);
- // if it has a class already, preserve it
- if ( $item->hasAttribute('class') ) {
- $string = $item->getAttribute('class') . ' external';
- }
- else $string = 'external';
- // set class to $string
- $item->setAttribute('class',$string);
- // if it has a title already, preserve it
- if ( $item->hasAttribute('title') ) {
- $string = $item->getAttribute('title') . ' - external link: ' . $href;
- }
- else $string = 'External: ' . $href;
- // set title to $string
- $item->setAttribute('title',$string);
- }
- }
- //~ // we now have a full html document in $dom. Let's extract only what is between
- //~ // <body> tags,as it was before. - stackoverflow.com/a/18090774
- //~ $content='';
- //~ foreach($dom->getElementsByTagName("body")->item(0)->childNodes as $child) {
- //~ $content .= $dom->saveXML($child);
- //~ }
- $content = $dom->saveHTML();
- // remove the tag we added at loadHTML
- $content = substr($content,$trim_before,$trim_after);
- // please read this: stackoverflow.com/a/20675396
- //~ $content = $dom->saveHTML($dom->documentElement);
- }
- }
|