1<?php 2/** 3 * @file syntax.php 4 * @brief DokuWiki syntax plugin : htmlabstract 5 * @author Vincent Feltz <psycho@feltzv.fr> 6 */ 7 8if (!defined('DOKU_INC')) 9 define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 10if (!defined('DOKU_PLUGIN')) 11 define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 12require_once(DOKU_PLUGIN.'syntax.php'); 13 14class syntax_plugin_htmlabstract extends DokuWiki_Syntax_Plugin 15{ 16 /** 17 * return informations for plugins managing page 18 */ 19 function getInfo() 20 { 21 return array( 22 'author' => 'Vincent Feltz', 23 'email' => 'psycho@feltzv.fr', 24 'date' => '2016-11-07', //first version 2008-11-14 25 'name' => 'HtmlAbstract', 26 'desc' => 'Allows integration of remote or local DW RSS feeds using html formatted abstracts instead of choosing between html OR abstract.', 27 'url' => 'http://www.dokuwiki.org/plugin:htmlabstract', 28 ); 29 } 30 31 /** 32 * return the type of syntax defined by this plugin 33 */ 34 function getType() {return 'substition';} // This is not a mispelling! ;) (http://www.dokuwiki.org/devel:syntax_plugins#fn__5) 35 36 /** 37 * return when to call this plugin 38 */ 39 function getSort() {return 310;} // 310 = Doku_Parser_Mode_rss (http://www.dokuwiki.org/devel:parser:getsort_list) 40 41 /** 42 * connect the pattern to the lexer 43 */ 44 function connectTo($mode) {$this->Lexer->addSpecialPattern('{{htmlabs>.*?}}',$mode,'plugin_htmlabstract');} 45 46 /** 47 * handle the pattern match 48 */ 49 function handle($match, $state, $pos, &$handler) 50 { 51 $params = $this->splitAndSortParams($match); 52 $elements = $this->getFeedElements($params); 53 if (!is_array($elements) && false !== strpos($elements, 'ERROR')) 54 return array($elements); 55 $content = $this->formatElements($elements, $params); 56 return array($content); 57 } 58 59 /** 60 * handle rendering output 61 */ 62 function render($mode, &$renderer, $data) 63 { 64 $renderer->doc .= $data[0]; 65 $data[0] = ""; 66 return TRUE; 67 } 68 69 /** 70 * determines params to be used from match and configuration 71 */ 72 function splitAndSortParams($match) 73 { 74 global $conf; 75 76 $match = trim(trim($match, '{}')); 77 $match = substr($match, strlen("htmlabs>")); 78 if (false !== strpos($match, ' ')) 79 $params['feed_url'] = trim(substr($match, 0, strpos($match, ' '))); 80 else 81 $params['feed_url'] = trim($match); 82 $match = substr($match, strlen($params['feed_url']) + 1); 83 if (substr($params['feed_url'], 0, 7) != 'http://') 84 $params['feed_url'] = DOKU_URL.$params['feed_url']; 85 if (false !== ($pos = strpos($params['feed_url'], '?'))) 86 { 87 $tmp = explode('?', $params['feed_url']); 88 $params['feed_url'] = $tmp[0]; 89 $params['feed_params'] = $tmp[1]; 90 } 91 else 92 $params['feed_params'] = ''; 93 $params['feed_params'] .= '&content=html&type=rss2'; 94 $opts = explode(' ', strtolower($match)); 95 $params['author'] = !in_array('noauthor', $opts); 96 $params['title'] = !in_array('notitle', $opts); 97 $params['date'] = !in_array('nodate', $opts); 98 $params['textlink'] = $this->getConf('textlink') ? $this->getConf('textlink') : $this->getLang("textlink"); 99 $params['maxlen'] = $this->getConf('maxlength'); 100 if ($params['maxlen'] <= 0) 101 $params['maxlen'] = 750; 102 $params['trycleancut'] = $this->getConf('paragraph'); 103 $params['bg_color'] = $this->getConf('bg_color'); 104 $params['unknown_author'] = $this->getLang('extern_edit'); 105 return $params; 106 } 107 108 /** 109 * get and parse elements of targeted feed 110 */ 111 function getFeedElements($params) 112 { 113 if (!($xml = @file_get_contents($params['feed_url'].'?'.$params['feed_params']))) 114 return '<b>ERROR : </b>Cannot get content from <a href="'.$params['feed_url'].'">'.$params['feed_url'].' !</a> Please check the feed URL.<br/>'; 115 $dom = new DOMDocument(); 116 if (false === @$dom->loadXML($xml)) 117 return '<b>ERROR : </b>XML error in feed, cannot parse.<br/>'; 118 $elements = array(); 119 $items = $dom->getElementsByTagName('item'); 120 foreach ($items as $item) 121 { 122 $element = array(); 123 $details = $item->getElementsByTagName('*'); 124 foreach ($details as $detail) 125 switch ($detail->nodeName) 126 { 127 case 'title' : 128 case 'author' : 129 case 'pubDate' : 130 case 'link' : 131 case 'description' : 132 $element[$detail->nodeName] = $detail->nodeValue; 133 break; 134 } 135 if (!isset($element['author'])) 136 $element['author'] = $params['unknown_author']; 137 $elements[] = $element; 138 } 139 return $elements; 140 } 141 142 /** 143 * format elements to put them in a list of coloured-background previews 144 */ 145 function formatElements($elements, $params) 146 { 147 $css = ' style="background-color:#'.$params['bg_color'].'; padding: 0px 5px 5px; overflow:auto; margin-bottom: 20px;"'; 148 $content = '</p><ul class="rss">'."\n"; // need to close the <p> opened by DW before plugin handling for W3C compliance (<ul> mustn't be contained by <p>) 149 $content.= '<!-- preview produced with HtmlAbstract - http://dokuwiki.org/plugin:htmlabstract --> '."\n"; 150 foreach ($elements as $element) 151 { 152 $item = '<li>'."\n"; 153 $item .= '<div class="li"'.$css.'>'."\n"; 154 if ($params['title']) 155 $item .= '<a href="'.$element['link'].'" class="wikilink1" title="'.$element['title'].'">'.$element['title'].'</a>'; 156 if ($params['author']) 157 $item .= $this->getLang('author').$element['author']; 158 if ($params['date']) 159 $item .= ' '.$this->formatDate($element['pubDate']); 160 $item .= "\n"; 161 $item .= '<div class="detail">'."\n".trim($this->formatDescription($element['description'], $params))."\n".'</div>'."\n"; 162 $item .= '<div class="level1" style="clear:both;"><strong><a href="'.$element['link'].'">'.$params['textlink'].'</a></strong></div>'."\n"; 163 $item .= '</div>'; 164 $item .= '</li>'."\n\n"; 165 $content .= $item; 166 } 167 $content .= '</ul><p>'; 168 return $content; 169 } 170 171//TODO à refaire pour les windowsiens! 172 /** 173 * format feed items'dates to adapt them to local wiki config (config:dformat) 174 */ 175 function formatDate($date) 176 { 177 global $conf; 178 179 if (false !== strpos($_SERVER['SERVER_SOFTWARE'], 'Win32') || 180 false !== strpos($_SERVER['SERVER_SOFTWARE'], 'Win64')) 181 return $date; //strptime() is not implemented on Windows platforms, sorry! 182 183 $decomposed_date = strptime($date, '%a, %d %b %Y %H:%M:%S %z'); 184 extract($decomposed_date); 185 $date = strftime($conf['dformat'], 186 mktime($tm_hour, $tm_min, $tm_sec, $tm_mon + 1, $tm_mday, 1900 + $tm_year)); 187 return $date; 188 } 189 190 /** 191 * cut abstracts to desired length, searches the cleaner cut, and close broken tags 192 */ 193 function formatDescription($text, $params) 194 { 195 $cut = $this->cutTextToLength($text, $params['maxlen']); 196 $text = preg_replace('/<([a-z]+[^>]*) id="([^>]+)"/', '<$1 id="${2}_htmlabstract_'.microtime(true).'"', $text); 197 //time() is used here for W3C compliance (avoid duplicated id's) 198 if ($cut && $params['trycleancut']) 199 { 200 $min = ($params['maxlen'] > 400) ? (200) : ($params['maxlen'] / 2); 201 if (FALSE !== ($pos = strrpos($text, "</p>")) && $pos >= $min) 202 $text = substr($text, 0, 4 + $pos); 203 } 204 if ($cut) 205 $text .= '... '; 206 $text = $this->closeBrokenTags($text); 207 return $text; 208 } 209 210 /** 211 * brutally cut abstract to desired length without considering html tags 212 */ 213 function cutTextToLength(&$text, $maxlen) 214 { 215 $intag = false; 216 $i = -1; 217 $len = 0; 218 $textlen = strlen($text); 219 while (++$i < $textlen) 220 if ('<' == $text[$i]) 221 $intag = true; 222 elseif ('>' == $text[$i]) 223 $intag = false; 224 elseif (!$intag) 225 if ($maxlen == ++$len) 226 { 227 $text = substr($text, 0, $i); 228 return true; 229 } 230 return false; 231 } 232 233 /** 234 * search tags broken by brutal cut and close them 235 */ 236 function closeBrokenTags($text) 237 { 238 $tags = array(); 239 $i = -1; 240 $textlen = strlen($text); 241 while (++$i < $textlen) 242 if ($text[$i] == '<') 243 { 244 if ($text[$i + 1] != '/' && $text[$i + 1] != '!') //opening tag 245 { 246 $j = $i; 247 while ($text[++$j] != ' ' && $text[$j] != '>'); 248 array_push($tags, substr($text, $i + 1, $j - $i - 1)); 249 } 250 elseif ($text[$i + 1] == '/') //closing tag 251 { 252 $j = $i + 1; 253 while ($text[++$j] != ' ' && $text[$j] != '>'); 254 $closed_tag = substr($text, $i + 2, $j - $i - 2); 255 while ($tags[count($tags) - 1] != $closed_tag) 256 array_pop($tags); 257 array_pop($tags); 258 } 259 } 260 while (count($tags)) 261 $text .= '</'.array_pop($tags).'>'; 262 return $text; 263 } 264 265} 266?> 267