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')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 42if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 43require_once(DOKU_PLUGIN.'syntax.php'); 44 45/* 46 * All DokuWiki plugins to extend the parser/rendering mechanism 47 * need to inherit from this class 48 */ 49class syntax_plugin_rack extends DokuWiki_Syntax_Plugin { 50 51 /* 52 * return some info 53 */ 54 function getInfo(){ 55 return array( 56 'author' => 'Tyler Bletsch', 57 'email' => 'Tyler.Bletsch@netapp.com', 58 'date' => '2010-01-18', 59 'name' => 'Rack Elevation Plugin', 60 'desc' => 'Displays an elevation of a datacenter rack.', 61 'url' => 'http://www.dokuwiki.org/plugin:rack', 62 ); 63 } 64 65 /* 66 * What kind of syntax are we? 67 */ 68 function getType(){ 69 return 'substition'; 70 } 71 72 /* 73 * Where to sort in? 74 */ 75 function getSort(){ 76 return 155; 77 } 78 79 /* 80 * Paragraph Type 81 */ 82 function getPType(){ 83 return 'block'; 84 } 85 86 /* 87 * Connect pattern to lexer 88 */ 89 function connectTo($mode) { 90 $this->Lexer->addSpecialPattern("<rack[^>]*>.*?(?:<\/rack>)",$mode,'plugin_rack'); 91 } 92 93 94 /* 95 * Handle the matches 96 */ 97 function handle($match, $state, $pos, &$handler){ 98 $match = substr($match,5,-7); 99 100 //default options 101 $opt = array( 102 'name' => 'Rack', 103 'height' => 42, 104 'content' => '' 105 ); 106 107 list($optstr,$opt['content']) = explode('>',$match,2); 108 unset($match); 109 110 // parse options 111 $optsin = explode(' ',$optstr); 112 foreach($optsin as $o){ 113 $o = trim($o); 114 if (preg_match("/^name=(.+)/",$o,$matches)) { 115 $opt['name'] = $matches[1]; 116 } elseif (preg_match("/^height=(\d+)/",$o,$matches)) { 117 $opt['height'] = $matches[1]; 118 } elseif (preg_match("/^descending/",$o,$matches)) { 119 $opt['descending'] = 1; 120 } 121 } 122 123 return $opt; 124 } 125 126 function autoselect_color($item) { 127 $color = '#888'; 128 if (preg_match('/(wire|cable)\s*guide|pdu|patch|term server|lcd/i',$item['model'])) { $color = '#bba'; } 129 if (preg_match('/blank/i', $item['model'])) { $color = '#fff'; } 130 if (preg_match('/netapp|fas\d/i', $item['model'])) { $color = '#07c'; } 131 if (preg_match('/^Sh(elf)?\s/i', $item['model'])) { $color = '#0AE'; } 132 if (preg_match('/cisco|catalyst|nexus/i', $item['model'])) { $color = '#F80'; } 133 if (preg_match('/brocade|mds/i', $item['model'])) { $color = '#8F0'; } 134 if (preg_match('/ucs/i', $item['model'])) { $color = '#c00'; } 135 if (preg_match('/ibm/i', $item['model'])) { $color = '#67A'; } 136 if (preg_match('/hp/i', $item['model'])) { $color = '#A67'; } 137 if (!$item['model']) { $color = '#FFF'; } 138 return $color; 139 } 140 141 /* 142 * Create output 143 */ 144 function render($mode, &$renderer, $opt) { 145 if($mode == 'metadata') return false; 146 147 $content = $opt['content']; 148 149 // clear any trailing or leading empty lines from the data set 150 $content = preg_replace("/[\r\n]*$/","",$content); 151 $content = preg_replace("/^\s*[\r\n]*/","",$content); 152 153 if(!trim($content)){ 154 $renderer->cdata('No data found'); 155 } 156 157 $items = array(); 158 159 $csv_id = uniqid("csv_"); 160 $csv = "Model,Name,Rack,U,Height,Comment\n"; 161 162 foreach (explode("\n",$content) as $line) { 163 $item = array(); 164 if (preg_match("/^\s*#/",$line) || !trim($line)) { continue; } # skip comments & blanks 165 # Ustart Usize Model Name? Color? Link? Comment 166 if (preg_match('/^\s* (\d+) \s+ (\d+) \s+ ((?:"[^"]*")|\S+) \s* ((?:"[^"]*")|(?!(?:(?:\#)|(?:link:)))\S*)? \s* (\#\w+)? \s* ( link: (?: (?:\[\[[^]|]+(?:\|[^]]*)?]]) | \S* ) )? \s* (.*?)? \s* $/x',$line,$matches)) { 167 $item['u_bottom'] = $matches[1]; 168 $item['u_size'] = $matches[2]; 169 $item['model'] = preg_replace('/^"/','',preg_replace('/"$/','',$matches[3])); 170 $item['name'] = preg_replace('/^"/','',preg_replace('/"$/','',$matches[4])); 171 $item['color'] = $matches[5] ? $matches[5] : $this->autoselect_color($item); 172 $item['linktitle'] = ''; 173 $item['link'] = substr($matches[6], 5); 174 if( '[' == substr($item['link'], 0, 1)) { 175 if(preg_match( '/^\[\[[^|]+\|([^]]+)]]$/', $item['link'], $titlematch )) { 176 $item['linktitle'] = ' title="'.hsc($titlematch[1]). '"'; 177 } 178 $item['link']=wl(cleanID(preg_replace( '/^\[\[([^]|]+).*/', '$1', $item['link'] ))); 179 } 180 $item['comment'] = $matches[7]; 181 $item['u_top'] = $item['u_bottom'] + ($opt['descending']?-1:1)*($item['u_size'] - 1); 182 $items[$item['u_top']] = $item; 183 $csv .= "\"$item[model]\",\"$item[name]\",$opt[name],$item[u_bottom],$item[u_size],\"$item[comment]\"\n"; 184 } else { 185 #$renderer->doc .= "Syntax error on the following line: <pre style='color:red'>$line</pre>\n"; 186 $renderer->doc .= 'Syntax error on the following line: <pre style="color:red">'.hsc($line)."</pre>\n"; 187 } 188 } 189 190 #$renderer->doc .= "<pre>ALL\n".print_r($items,true)."</pre>"; 191 192 $u_first = $opt['descending'] ? 1 : $opt['height']; 193 $u_last = $opt['descending'] ? $opt['height'] : 1; 194 $u_delta = $opt['descending'] ? +1 : -1; 195 $renderer->doc .= "<table class='rack'><tr><th colspan='2' class='title'>$opt[name]</th></tr>\n"; 196 #for ($u=$opt['height']; $u>=1; $u--) { 197 #foreach (range($u_first,$u_last,$u_delta) as $u) { 198 for ($u=$u_first; ($opt['descending'] ? $u<=$u_last : $u>=$u_last); $u += $u_delta) { 199 if ($items[$u] && $items[$u]['model']) { 200 $item = $items[$u]; 201 $renderer->doc .= 202 "<tr><th>$u</th>". 203 "<td class='item' rowspan='$item[u_size]' style='background-color: $item[color];' title=\"".htmlspecialchars($item['comment'])."\">". 204 ($item['link'] ? '<a href="'.$item['link'].'"'.$item['linktitle'].'>' : ''). 205 "<div style='float: left; font-weight:bold;'>". 206 "$item[model]" . 207 ($item['comment'] ? ' *' : ''). 208 "</div>". 209 "<div style='float: right; margin-left: 3em; '>$item[name]". 210 "</div>". 211 ($item['link'] ? '</a>' : ''). 212 "</td></tr>\n"; 213 for ($d = 1; $d < $item['u_size']; $d++) { 214 $u += $u_delta; 215 $renderer->doc .= "<tr><th>$u</th></tr>\n"; 216 } 217 } else { 218 $renderer->doc .= "<Tr><Th>$u</th><td class='empty'></td></tr>\n"; 219 } 220 } 221 # we use a whole row as a bottom border to sidestep an asinine rendering bug in firefox 3.0.10 222 $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"; 223 $renderer->doc .= "</table> "; 224 225 # this javascript hack sets the CSS "display" property of the tables to "inline", 226 # since IE is too dumb to have heard of the "inline-table" mode. 227 $renderer->doc .= "<script type='text/javascript'>rack_ie6fix();</script>\n"; 228 229 $renderer->doc .= "<pre style='display:none;' id='$csv_id'>$csv</pre>\n"; 230 231 return true; 232 } 233 234} 235