1<?php 2/* 3 * Rack Plugin: display a rack elevation from a plaintext source 4 * 5 * Each rack is enclosed in <rack>...</rack> tags. The rack tag can have the 6 * following parameters (all optional): 7 * name=<name> The name of the rack (default: 'Rack') 8 * height=<U> The height of the rack, in U (default: 42) 9 * descending Indicates that the rack is numbered top-to-bottom 10 * Between these tags is a series of lines, each describing a piece of equipment: 11 * 12 * <u_bottom> <u_size> <model> [name] [#color] [link:<URL>] [comment] 13 * 14 * The fields: 15 * - <u_bottom>: The starting (bottom-most) U of the equipment. 16 * - <u_size>: The height of the equipment in U. 17 * - <model>: The model name or other description of the item (e.g. 18 * "Cisco 4948" or "Patch panel"). If it has spaces, enclose it in quotes. 19 * - [name]: Optional. The hostname or other designator of this specific item 20 * (e.g. �rtpserver1�). If it has spaces, enclose it in quotes. If you want 21 * to specify a comment but no name, use �� as the name. 22 * - [#color]: Optional. The color of the item is normally automatically 23 * picked based on the model, but you can override it by specifying a 24 * #RRGGBB HTML color after the model/name. 25 * - [link:http://url]: Optional. The model name will now link to the url given. 26 * - [comment]: Optional. After the name (and possibly color), and remaining 27 * text on the line is treated as free-form comment. Comments are visible 28 * by hovering the mouse over the equipment in the rack. Items with comments 29 * are designated with an asterisk * after the model name. 30 * 31 * You can also include comment lines starting with a pound sign #. 32 * 33 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 34 * @author Tyler Bletsch <Tyler.Bletsch@netapp.com> 35 * @version 20120816.1 36 * 37 * Modded for links usage by Sylvain Bigonneau <s.bigonneau@moka-works.com> 38 * Improved link syntax contributed by Dokuwiki user "schplurtz". 39 */ 40 41if(!defined('DOKU_INC')) die(); // DokuWiki needed 42 43/* 44 * All DokuWiki plugins to extend the parser/rendering mechanism 45 * need to inherit from this class 46 */ 47class syntax_plugin_rack extends DokuWiki_Syntax_Plugin { 48 49 /* 50 * return some info 51 */ 52 function getInfo(){ 53 return confToHash(dirname(__FILE__).'/plugin.info.txt'); 54 } 55 56 /* 57 * What kind of syntax are we? 58 */ 59 function getType(){ 60 return 'substition'; 61 } 62 63 /* 64 * Where to sort in? 65 */ 66 function getSort(){ 67 return 155; 68 } 69 70 /* 71 * Paragraph Type 72 */ 73 function getPType(){ 74 return 'block'; 75 } 76 77 /* 78 * Connect pattern to lexer 79 */ 80 function connectTo($mode) { 81 $this->Lexer->addSpecialPattern("<rack[^>]*>.*?(?:<\/rack>)",$mode,'plugin_rack'); 82 } 83 84 85 /* 86 * Handle the matches 87 */ 88 function handle($match, $state, $pos, Doku_Handler $handler){ 89 $match = substr($match,5,-7); 90 91 //default options 92 $opt = array( 93 'name' => 'Rack', 94 'height' => 42, 95 'content' => '' 96 ); 97 98 list($optstr,$opt['content']) = explode('>',$match,2); 99 unset($match); 100 101 // parse options 102 $optsin = explode(' ',$optstr); 103 foreach($optsin as $o){ 104 $o = trim($o); 105 if (preg_match("/^name=(.+)/",$o,$matches)) { 106 $opt['name'] = $matches[1]; 107 } elseif (preg_match("/^height=(\d+)/",$o,$matches)) { 108 $opt['height'] = $matches[1]; 109 } elseif (preg_match("/^descending/",$o,$matches)) { 110 $opt['descending'] = 1; 111 } 112 } 113 return $opt; 114 } 115 116 function autoselect_color($item) { 117 $color = '#888'; 118 if (preg_match('/(wire|cable)\s*guide|pdu|patch|term server|lcd/i',$item['model'])) { $color = '#bba'; } 119 if (preg_match('/blank/i', $item['model'])) { $color = '#fff'; } 120 if (preg_match('/netapp|fas\d/i', $item['model'])) { $color = '#07c'; } 121 if (preg_match('/^Sh(elf)?\s/i', $item['model'])) { $color = '#0AE'; } 122 if (preg_match('/cisco|catalyst|nexus/i', $item['model'])) { $color = '#F80'; } 123 if (preg_match('/brocade|mds/i', $item['model'])) { $color = '#8F0'; } 124 if (preg_match('/ucs/i', $item['model'])) { $color = '#c00'; } 125 if (preg_match('/ibm/i', $item['model'])) { $color = '#67A'; } 126 if (preg_match('/hp/i', $item['model'])) { $color = '#A67'; } 127 if (!$item['model']) { $color = '#FFF'; } 128 return $color; 129 } 130 131 /* 132 * Create output 133 */ 134 function render($mode, Doku_Renderer $renderer, $opt) { 135 if($mode == 'metadata') return false; 136 137 $content = $opt['content']; 138 139 // clear any trailing or leading empty lines from the data set 140 $content = preg_replace("/[\r\n]*$/","",$content); 141 $content = preg_replace("/^\s*[\r\n]*/","",$content); 142 143 if(!trim($content)){ 144 $renderer->cdata('No data found'); 145 } 146 147 $items = array(); 148 149 $csv_id = uniqid("csv_"); 150 $csv = "Model,Name,Rack,U,Height,Comment\n"; 151 152 foreach (explode("\n",$content) as $line) { 153 $item = array(); 154 if (preg_match("/^\s*#/",$line) || !trim($line)) { continue; } # skip comments & blanks 155 # Ustart Usize Model Name? Color? Link? Comment 156 if (preg_match('/^\s* (\d+) \s+ (\d+) \s+ ((?:"[^"]*")|\S+) \s* ((?:"[^"]*")|(?!(?:(?:\#)|(?:link:)))\S*)? \s* (\#\w+)? \s* ( link: (?: (?:\[\[[^]|]+(?:\|[^]]*)?]]) | \S* ) )? \s* (.*?)? \s* $/x',$line,$matches)) { 157 $item['u_bottom'] = $matches[1]; 158 $item['u_size'] = $matches[2]; 159 $item['model'] = preg_replace('/^"/','',preg_replace('/"$/','',$matches[3])); 160 $item['name'] = preg_replace('/^"/','',preg_replace('/"$/','',$matches[4])); 161 $item['color'] = $matches[5] ? $matches[5] : $this->autoselect_color($item); 162 $item['linktitle'] = ''; 163 $item['link'] = substr($matches[6], 5); 164 if( '[' == substr($item['link'], 0, 1)) { 165 if(preg_match( '/^\[\[[^|]+\|([^]]+)]]$/', $item['link'], $titlematch )) { 166 $item['linktitle'] = ' title="'.hsc($titlematch[1]). '"'; 167 } 168 $item['link']=wl(cleanID(preg_replace( '/^\[\[([^]|]+).*/', '$1', $item['link'] ))); 169 } 170 $item['comment'] = $matches[7]; 171 $item['u_top'] = $item['u_bottom'] + ($opt['descending']?-1:1)*($item['u_size'] - 1); 172 $items[$item['u_top']] = $item; 173 $csv .= "\"$item[model]\",\"$item[name]\",$opt[name],$item[u_bottom],$item[u_size],\"$item[comment]\"\n"; 174 } else { 175 #$renderer->doc .= "Syntax error on the following line: <pre style='color:red'>$line</pre>\n"; 176 $renderer->doc .= 'Syntax error on the following line: <pre style="color:red">'.hsc($line)."</pre>\n"; 177 } 178 } 179 180 #$renderer->doc .= "<pre>ALL\n".print_r($items,true)."</pre>"; 181 182 $u_first = $opt['descending'] ? 1 : $opt['height']; 183 $u_last = $opt['descending'] ? $opt['height'] : 1; 184 $u_delta = $opt['descending'] ? +1 : -1; 185 $renderer->doc .= "<table class='rack'><tr><th colspan='2' class='title'>$opt[name]</th></tr>\n"; 186 #for ($u=$opt['height']; $u>=1; $u--) { 187 #foreach (range($u_first,$u_last,$u_delta) as $u) { 188 for ($u=$u_first; ($opt['descending'] ? $u<=$u_last : $u>=$u_last); $u += $u_delta) { 189 if ($items[$u] && $items[$u]['model']) { 190 $item = $items[$u]; 191 $renderer->doc .= 192 "<tr><th>$u</th>". 193 "<td class='item' rowspan='$item[u_size]' style='background-color: $item[color];' title=\"".htmlspecialchars($item['comment'])."\">". 194 ($item['link'] ? '<a href="'.$item['link'].'"'.$item['linktitle'].'>' : ''). 195 "<div style='float: left; font-weight:bold;'>". 196 "$item[model]" . 197 ($item['comment'] ? ' *' : ''). 198 "</div>". 199 "<div style='float: right; margin-left: 3em; '>$item[name]". 200 "</div>". 201 ($item['link'] ? '</a>' : ''). 202 "</td></tr>\n"; 203 for ($d = 1; $d < $item['u_size']; $d++) { 204 $u += $u_delta; 205 $renderer->doc .= "<tr><th>$u</th></tr>\n"; 206 } 207 } else { 208 $renderer->doc .= "<Tr><Th>$u</th><td class='empty'></td></tr>\n"; 209 } 210 } 211 # we use a whole row as a bottom border to sidestep an asinine rendering bug in firefox 3.0.10 212 $renderer->doc .= "<tr><th colspan='2' class='bottom'><span style='cursor: pointer;' onclick=\"this.innerHTML = rack_toggle_vis(document.getElementById('$csv_id'),'block')?'Hide CSV ↑':'Show CSV ↓';\">Show CSV ↓</span></th></tr>\n"; 213 $renderer->doc .= "</table> "; 214 215 # this javascript hack sets the CSS "display" property of the tables to "inline", 216 # since IE is too dumb to have heard of the "inline-table" mode. 217 $renderer->doc .= "<script type='text/javascript'>rack_ie6fix();</script>\n"; 218 219 $renderer->doc .= "<pre style='display:none;' id='$csv_id'>$csv</pre>\n"; 220 221 return true; 222 } 223 224} 225