1<?php 2/** 3 * Plugin google_maps: Generates embedded Google Maps frame or link to Google Maps. 4 * 5 * @license GPLv2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Dmitry Katsubo <dma_k@mail.ru> 7 */ 8 9if(!defined('DOKU_INC')) die(); 10if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 11require_once(DOKU_PLUGIN.'syntax.php'); 12 13/** 14 * All DokuWiki plugins to extend the parser/rendering mechanism 15 * need to inherit from this class 16 */ 17class syntax_plugin_google_maps extends DokuWiki_Syntax_Plugin 18{ 19 private $RE_NON_SYNTAX_SEQ = '[^\[\]{}|]+'; 20 private $RE_PLUGIN_BODY; 21 22 function syntax_plugin_google_maps() 23 { 24 $this->RE_PLUGIN_BODY = $this->RE_NON_SYNTAX_SEQ . '(?:\\[' . $this->RE_NON_SYNTAX_SEQ . '\\])?'; 25 } 26 27 function getInfo() 28 { 29 return array( 30 'author' => 'Dmitry Katsubo', 31 'email' => 'dma_k@mail.ru', 32 'date' => '2016-09-20', 33 'name' => 'Google Maps Plugin', 34 'desc' => 'Adds a Google Maps frame 35 syntax: {{googlemaps>address1;address2;address3[zoom=16,size=small,control=hierarchical,overviewmap=true,width=800,height=600,type=embedded]|alternative text}}', 36 'url' => 'http://centurion.dynalias.com/wiki/plugin/google_maps', 37 ); 38 } 39 40 function getAllowedTypes() 41 { 42 return array('formatting'); 43 } 44 45 function getType() 46 { 47 return 'substition'; 48 } 49 50 function getSort() 51 { 52 return 159; 53 } 54 55 function connectTo($mode) 56 { 57 $this->Lexer->addSpecialPattern('{{googlemaps>' . $this->RE_PLUGIN_BODY . '}}', $mode, 'plugin_google_maps'); 58 $this->Lexer->addEntryPattern('{{googlemaps>' . $this->RE_PLUGIN_BODY . '\|(?=' . $this->RE_NON_SYNTAX_SEQ . '}})', $mode, 'plugin_google_maps'); 59 } 60 61 function postConnect() 62 { 63 $this->Lexer->addExitPattern('}}', 'plugin_google_maps'); 64 } 65 66 private function getConfigValue($options, $option_name, $config_prefix = null) 67 { 68 // Also escape HTML to protect the page: 69 return(htmlspecialchars( 70 isset($options[$option_name]) ? 71 $options[$option_name] : 72 $this->getConf($config_prefix . $option_name) 73 )); 74 } 75 76 function handle($match, $state, $pos, Doku_Handler $handler) 77 { 78 switch ($state) 79 { 80 case DOKU_LEXER_SPECIAL: 81 case DOKU_LEXER_ENTER: 82 $matches = array(); 83 84 if (!preg_match('/{{googlemaps>(' . $this->RE_NON_SYNTAX_SEQ . ')(?:\\[(' . $this->RE_NON_SYNTAX_SEQ . ')\\])?/', $match, $matches)) 85 { 86 return array(''); // this is an error 87 } 88 89 $options = array(); 90 91 if (isset($matches[2])) 92 { 93 $entries = explode(',', $matches[2]); 94 95 foreach ($entries as $entry) 96 { 97 $key_value = explode('=', $entry); 98 99 $options[trim($key_value[0])] = trim($key_value[1]); 100 } 101 } 102 103 return array($state, array($matches[1], &$options)); 104 } 105 106 return array($state, $match); 107 } 108 109 function render($mode, Doku_Renderer $renderer, $data) 110 { 111 if ($mode == 'xhtml') 112 { 113 list($state, $match) = $data; 114 115 switch($state) 116 { 117 case DOKU_LEXER_SPECIAL: 118 case DOKU_LEXER_ENTER: 119 list($text, $options) = $match; 120 121 // All locations are in this array: 122 $locations = array(); 123 $i = 0; 124 125 foreach (explode(";", $text) as $q) 126 { 127 $q = trim($q); 128 if (strlen($q)) 129 { 130 $locations[$i++] = htmlspecialchars(html_entity_decode($q)); 131 } 132 } 133 134 // This type is available only in DOKU_LEXER_SPECIAL state: 135 if ($state == DOKU_LEXER_SPECIAL && $options['type'] == 'embedded') 136 { 137 // Dynamic injection of this script via JS causes FF to hang, so we have to include it for each map: 138 $renderer->doc .= "\n<script type='text/javascript' src='//maps.google.com/maps?file=api&v=2.x&key=" . $this->getConf('google_api_key') . "'></script>"; 139 140 // Default values: 141 $size = $this->getConfigValue($options, 'size'); 142 $width = $this->getConfigValue($options, 'width', $size . '_') . "px"; 143 $height = $this->getConfigValue($options, 'height', $size . '_') . "px"; 144 145 // Embedded div: 146 $renderer->doc .= "\n<div class='gmaps_frame' style='width: $width; height: $height'"; 147 148 foreach ($locations as $i => $q) 149 { 150 $renderer->doc .= " location$i='$q'"; 151 } 152 153 // Copy values into attributes: 154 foreach (array('size', 'control', 'overviewmap', 'zoom') as $attr_name) 155 { 156 $attr_value = $this->getConfigValue($options, $attr_name); 157 158 if (strlen($attr_value)) 159 { 160 $renderer->doc .= ' ' . $attr_name . '="' . $attr_value . '"'; 161 } 162 } 163 164 // Important to leave one hanging node inside <div>, otherwise maps start overlappig. 165 $renderer->doc .= '></div>'; 166 167 return true; 168 } 169 170 // If we are here it means: 171 // * state == DOKU_LEXER_SPECIAL and type != embedded ==> we render a link with a text equal to address, as there is no alternative text in this state 172 // * state == DOKU_LEXER_ENTER and type != embedded ==> we start rendering a link; the alternative text will be rendered by dokuwiki renderer and may include any formatting 173 // * state == DOKU_LEXER_ENTER and type == embedded ==> the is unsupported combination, but we render a link the same as with type != embedded 174 175 // Concat params: 176 $params = '&'; 177 // If not defined, Google Maps engine will automatically select the best zoom: 178 if ($options['zoom']) 179 { 180 $params .= "z=" . $options['zoom']; 181 } 182 183 // Query is already escaped, params are taken from options: 184 $url = "//maps.google.com/maps?q=$locations[0]$params"; 185 186 // External link: 187 $renderer->doc .= "<a href='$url' class='gmaps_link'>"; 188 189 if ($state == DOKU_LEXER_SPECIAL) 190 { 191 $renderer->doc .= "$text</a>"; 192 } 193 194 return true; 195 196 case DOKU_LEXER_UNMATCHED: 197 $renderer->doc .= $renderer->_xmlEntities($match); 198 return true; 199 200 case DOKU_LEXER_EXIT: 201 $renderer->doc .= '</a>'; 202 return true; 203 204 default: 205 //$renderer->doc .= "<div class='error'>Cannot handle mode $style</div>"; 206 } 207 } 208 209 return false; 210 } 211} 212?> 213