1<?php 2/** 3 * DokuWiki Plugin netlogo (Syntax Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author Rik Blok <rik.blok@ubc.ca> 7 * 8 * Download: 9 * <https://github.com/rikblok/dokuwiki-plugin-netlogo/zipball/master> 10 * 11 * ToDo: 12 * * automatically add "nlogo !application/octet-stream" to conf/mime.local.conf? [Rik, 2012-10-19] 13 * * language support [Rik, 2012-10-19] 14 * * better error messages [Rik, 2012-10-19] 15 * * config options (eg. download url) [Rik, 2012-10-19] 16 * * make it work with local media files. Currently reports error: "Unable to load NetLogo model from ..., please ensure: * That you can download the resource at this link * That the server containing the resource has Cross-Origin Resource Sharing configured appropriately". Tried corsharing plugin but didn't help. [Rik, 2016-11-27] 17 18 * 19 * Documentation: 20 * NetLogo model file format <https://github.com/NetLogo/NetLogo/wiki/Model-file-format> 21 * 22 * Acknowledgements: 23 * Thanks to Stylianos Dritsas for the applet plugin 24 * <https://www.dokuwiki.org/plugin:applet>. 25 */ 26 27// must be run within Dokuwiki 28if (!defined('DOKU_INC')) die(); 29 30if (!defined('DOKU_LF')) define('DOKU_LF', "\n"); 31if (!defined('DOKU_TAB')) define('DOKU_TAB', "\t"); 32if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 33 34require_once DOKU_PLUGIN.'syntax.php'; 35require_once DOKU_PLUGIN.'netlogo/inc/support.php'; 36 37class syntax_plugin_netlogo_applet extends DokuWiki_Syntax_Plugin { 38 public function getType() { 39 return 'substition'; 40 } 41 42/* // don't override getPType() 43 public function getPType() { 44 return 'FIXME: normal|block|stack'; 45 } 46*/ 47 48 public function getSort() { 49 /* Should be less than 320 as defined in 50 * /inc/parser/parser.php:class Doku_Parser_Mode_media 51 * http://xref.dokuwiki.org/reference/dokuwiki/_classes/doku_parser_mode_media.html 52 * After plugin:applet (316), before media (320). See https://www.dokuwiki.org/devel:parser:getsort_list 53 */ 54 return 317; 55 } 56 57 58 public function connectTo($mode) { 59 // make regex less greedy so it doesn't include pipe in filename, eg. only match first ugh.nlogo in {{ugh.nlogo|Download ugh.nlogo}} [Rik, 2013-11-28]] 60 $this->Lexer->addSpecialPattern('\{\{[^\}\|]+\.nlogo\?[^\} ]*do=download[^\} ]* ?\}\}',$mode,'media'); // with do=download parameter 61 $this->Lexer->addSpecialPattern('\{\{[^\}\|]+\.nlogo ?\}\}',$mode,'plugin_netlogo_applet'); // without parameters 62 $this->Lexer->addSpecialPattern('\{\{[^\}\|]+\.nlogo\?[^\} ]+ ?\}\}',$mode,'plugin_netlogo_applet'); // with other parameters 63 $this->Lexer->addSpecialPattern('\{\{[^\}\|]+\.nlogo ?\|\}\}',$mode,'plugin_netlogo_applet'); // with empty title [Rik, 2013-11-16] 64 // here are some test cases [Rik, 2012-10-12] 65 /* 66 // should work 67 {{ugh.nlogo|}} 68 {{ugh.nlogo}} 69 {{ugh.nlogo }} 70 {{ ugh.nlogo }} 71 {{ ugh.nlogo}} 72 73 // should work 74 {{ugh.nlogo?818x611}} 75 {{ugh.nlogo?818x611 }} 76 {{ ugh.nlogo?818x611 }} 77 {{ ugh.nlogo?818x611}} 78 79 // should fail 80 {{ugh.nlogo.x}} 81 {{ugh.nlogo.x }} 82 {{ ugh.nlogo.x }} 83 {{ ugh.nlogo.x}} 84 85 // should fail 86 {{ugh.nlogo.x?818x611}} 87 {{ugh.nlogo.x?818x611 }} 88 {{ ugh.nlogo.x?818x611 }} 89 {{ ugh.nlogo.x?818x611}} 90 */ 91// $this->Lexer->addEntryPattern('<FIXME>',$mode,'plugin_netlogo_applet'); 92 } 93 94// public function postConnect() { 95// $this->Lexer->addExitPattern('</FIXME>','plugin_netlogo_applet'); 96// } 97 98 public function handle($match, $state, $pos, Doku_Handler $handler){ 99 /* 100 * Copied from DokuWiki media handler in 101 * http://xref.dokuwiki.org/reference/dokuwiki/_functions/doku_handler_parse_media.html 102 */ 103 // Strip the opening and closing markup 104 $link = preg_replace(array('/^\{\{/','/\}\}$/u'),'',$match); 105 106 // Split title from URL 107 $link = explode('|',$link,2); 108 109 // Check alignment 110 $ralign = (bool)preg_match('/^ /',$link[0]); 111 $lalign = (bool)preg_match('/ $/',$link[0]); 112 113 // Logic = what's that ;)... 114 if ( $lalign & $ralign ) { 115 $align = 'center'; 116 } else if ( $ralign ) { 117 $align = 'right'; 118 } else if ( $lalign ) { 119 $align = 'left'; 120 } else { 121 $align = NULL; 122 } 123 124 // The title... 125 if ( !isset($link[1]) ) { 126 $link[1] = NULL; 127 } 128 129 //remove aligning spaces 130 $link[0] = trim($link[0]); 131 132 //split into src and parameters (using the very last questionmark) 133 $pos = strrpos($link[0], '?'); 134 if($pos !== false){ 135 $src = substr($link[0],0,$pos); 136 $param = substr($link[0],$pos+1); 137 }else{ 138 $src = $link[0]; 139 $param = ''; 140 } 141 142 // parse width and height (must be first parameter) 143 if (preg_match('#^(\d+)(x(\d+))?#i',$param,$size)){ 144 ($size[1]) ? $w = $size[1] : $w = NULL; 145 ($size[3]) ? $h = $size[3] : $h = NULL; 146 } else { 147 $w = NULL; 148 $h = NULL; 149 } 150 151 // parse 'do' action 152 if (preg_match('#do=([a-z]+)#',$param,$action)){ 153 // specified by user 154 $do = $action[1]; 155 } else { 156 $do = "interface"; // default 157 } 158 159 // check for nlogo fileicon 160 $nlogoiconsrc = DOKU_PLUGIN.'netlogo/fileicons/nlogo.png'; 161 $nlogoicondest = DOKU_INC.'lib/images/fileicons/nlogo.png'; 162 if (!file_exists($nlogoicondest)) copy($nlogoiconsrc, $nlogoicondest); 163 164 $params = array( 165 'src'=>$src, 166 'title'=>$link[1], 167 'align'=>$align, 168 'width'=>$w, 169 'height'=>$h, 170 'do'=>$do, 171 ); 172 173 return $params; 174 } 175 176 public function render($mode, Doku_Renderer $renderer, $data) { 177 global $ID, $conf; 178 179 if($mode != 'xhtml') return false; 180 181 // check .nlogo file read permission 182 $src = $data['src']; 183 /* testing: disable filetype checking. Does this allow remote download of file, eg. from github? [Rik, 2016-11-25] 184 resolve_mediaid(getNS($ID),$src,$exists); 185 if(auth_quickaclcheck(getNS($src).':X') < AUTH_READ){ // auth_quickaclcheck() mimicked from http://xref.dokuwiki.org/reference/dokuwiki/_functions/checkfilestatus.html 186 $renderer->doc .= '<div class="error">NetLogo: File not allowed: ' . $src . '</div>'; 187 return true; 188 } 189 $src = mediaFN($src); 190 if (!$exists) { 191 $renderer->doc .= '<div class="error">NetLogo: File not found: ' . $src . '</div>'; 192 return true; 193 } 194 */ 195 196 // parse file to get contents 197 if (is_null($data['width']) || is_null($data['height']) || $data['do']==='code' || $data['do']==='info' || $data['do']==='mdinfo') { 198 $nlogo = file_get_contents($src); 199 $nlogoparts = explode('@#$#@#$#@', $nlogo); 200 /* 201 [0] => code 202 [1] => interface 203 [2] => info 204 [3] => turtle shapes 205 [4] => NetLogo version 206 [5] => preview commands 207 [6] => system dynamics modeler 208 [7] => BehaviorSpace 209 [8] => HubNet client 210 [9] => link shapes 211 [10] =>model settings 212 [11] =>reserved by Michelle 213 [12] => (empty) 214 */ 215 216 // show code 217 if ($data['do']==='code') { 218 $renderer->doc .= p_render('xhtml',p_get_instructions('<code netlogo>' . $nlogoparts[0] . '</code>'),$info); 219 return true; 220 } 221 222 // show info 223 if ($data['do']==='info') { 224 $renderer->doc .= p_render('xhtml',p_get_instructions($nlogoparts[2]),$info); 225 return true; 226 } 227 // show info wrapped in '<markdown>...</markdown>' tags 228 if ($data['do']==='mdinfo') { 229 $renderer->doc .= p_render('xhtml',p_get_instructions('<markdown>' . $nlogoparts[2] . '</markdown>'),$info); 230 return true; 231 } 232 233 // width & height? 234 if (is_null($data['width']) || is_null($data['height'])) { 235 // store x,y coordinates of bottom right corner in $rightbottom[2] & $rightbottom[3], respectively 236 preg_match_all('/(^|\n)\n[A-Z\-]+\n[0-9]+\n[0-9]+\n([0-9]+)\n([0-9]+)\n/',$nlogoparts[1],$rightbottom); 237 if (is_null($data['width'])) $data['width'] = max($rightbottom[2])+50; 238 if (is_null($data['height'])) $data['height'] = max($rightbottom[3])+300; 239 } 240 } 241 242 243 // download libraries? Todo: move root url to config option 244 $urlroot = 'http://ccl.northwestern.edu/netlogo/'; 245 246 // $src is currently realpath. Turn into relative path from DokuWiki media folder 247 // temporarily disabled while testing remote urls [Rik, 2016-11-26] 248 //$src = relativePath(DOKU_INC.'data/media/',$src); 249 250 // Will pass token to servefile.php to authorize. First generate secret uuid if not found. 251 $uuidfile = 'data/tmp/plugin_netlogo_uuid'; 252 if (!file_exists($uuidfile)) { 253 if (!$handle = fopen($uuidfile, 'w')) { 254 $renderer->doc .= '<div class="error">NetLogo: Cannot create UUID ' . $uuidfile . '</div>'; 255 return true; 256 } 257 // Write uuid to our opened file. 258 if (fwrite($handle, uuid4()) === FALSE) { 259 $renderer->doc .= '<div class="error">NetLogo: Cannot write UUID to ' . $uuidfile . '</div>'; 260 return true; 261 } 262 fclose($handle); 263 } 264 // read uuid from file 265 $uuid = file_get_contents($uuidfile); 266 267 // when should the servefile.php link expire? 268 $expires = time()+min(max($conf['cachetime'],60), 3600); // expires in cachetime, but no less than 1 minute or more than 1 hour 269 270 // disable caching of this page to ensure parameters passed to servefile.php are always fresh [Rik, 2012-10-06] 271 $renderer->info['cache'] = false; 272 273 // generate token for servefile.php to authorize, use $uuid as salt. servefile.php must be able to generate same token or it won't serve file. 274 $token=hash('sha256',$uuid.$src.$expires); // replace crypt() for more than first 8 chars [Rik, 2012-10-06] 275 276 // special handling for center 277 $pcenter = false; 278 if (!is_null($data['align']) && $data['align']==='center') { 279 $pcenter = true; 280 $data['align']=null; 281 } 282 283 /* 284 old servefile method: '"lib/plugins/netlogo/inc/servefile.php?src='.urlencode($src).'&expires='.$expires.'&token='.urlencode($token).'"' 285 may still be needed because fetch.php throws Cross-Origin Resource Sharing error 286 [Rik, 2016-11-27] 287 */ 288 if ($pcenter) $renderer->doc .= '<p align="center">'; 289 $renderer->doc .= '<iframe title="" src="https://netlogoweb.org/web?'.$src.'" style="width:'.$data['width'].'px; height:'.$data['height'].'px"></iframe>'; 290 if ($pcenter) $renderer->doc .= '</p>'; 291 return true; 292 } 293} 294 295// vim:ts=4:sw=4:et: 296