1<?php
2/**
3 * This plugin allows to embed wikipages into the current page
4 * In addition, parts marked with id's can be replaced.
5 *
6 * Inspired (and copy&pasted) from the include plugin:
7 *   http://wiki.splitbrain.org/plugin:include
8 *
9 * For wikipage-portion-replacements, the label-plugin is required: http://wiki.splitbrain.org/plugin:label
10 *
11 *
12 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
13 * @author     Pascal Bihler <bihler@iai.uni-bonn.de>
14 */
15
16// must be run within Dokuwiki
17if(!defined('DOKU_INC')) die();
18
19if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
20if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
21require_once(DOKU_PLUGIN.'syntax.php');
22
23//
24
25class syntax_plugin_embed extends DokuWiki_Syntax_Plugin {
26
27		function syntax_plugin_embed() {
28				global $embedded_pages_by_plugin_embed;
29		    if (! $embedded_pages_by_plugin_embed) {
30					 $embedded_pages_by_plugin_embed = array(); // To avoid recursion;
31				}
32		}
33
34    function getInfo(){
35        return array(
36            'author' => 'Pascal Bihler',
37            'email'  => 'bihler@iai.uni-bonn.de',
38            'date'   => '2007-05-14',
39            'name'   => 'Embed',
40            'desc'   => 'Allows to embed wikipages in other ones, while offering the possibility to replace parts of the original page marked with labels.',
41            'url'    => 'http://wiki.splitbrain.org/plugin:embed',
42        );
43    }
44
45    function getType(){
46        return 'substition';
47    }
48
49		function getSort(){
50        return 500;
51    }
52
53		function connectTo($mode) {
54      $this->Lexer->addSpecialPattern('<embed\s+[^>]+\s*>.*?</embed>',$mode,'plugin_embed');
55      $this->Lexer->addSpecialPattern('<embed\s+[^/]+\s*/>',$mode,'plugin_embed');
56    }
57
58
59    /**
60     * Handle the match
61     */
62    function handle($match, $state, $pos, &$handler){
63        switch ($state) {
64          case DOKU_LEXER_SPECIAL :
65					  if (preg_match('/<embed\s+([^>]+)\s*>(.*?)<\/embed>/ms',$match,$matches))
66							 return array($matches[1],$matches[2]);
67						else if (preg_match('/<embed\s+([^>]+)\s*\/>/',$match,$matches))
68							 return array($matches[1],'');
69            break;
70        }
71        return array();
72    }
73
74    /**
75     * Create output
76     */
77    function render($mode, &$renderer, $data) {
78    	  global $ID;
79				global $embedded_pages_by_plugin_embed;
80
81
82        if($mode == 'xhtml'){
83						list($id,$replacement) = $data;
84
85      			$renderer->info['cache'] = false; // prevent caching (to ensure ACL conformity) (TODO sometimes: make more intelligent)
86
87						resolve_pageid(getNS($ID), $id, $exists); // resolve shortcuts
88
89						//resolve_pageid(getNS($ID), $id, $exists); // resolve shortcuts
90
91						// avoid circular references
92						if (! (array_search($id,$embedded_pages_by_plugin_embed) === false))
93						 		return false;
94
95						array_push($embedded_pages_by_plugin_embed,$id);
96
97						$ins = $this->_embed_file($id,$replacement);
98            $renderer->doc .= p_render('xhtml', $ins, $info);            // ptype = 'normal'
99
100						array_pop($embedded_pages_by_plugin_embed);
101
102            return true;
103        }
104        return false;
105    }
106
107		function _embed_file($id,$replacement) {
108
109
110            // check permission
111            $perm = auth_quickaclcheck($id);
112            if ($perm < AUTH_READ) return false;
113
114
115						//Read embeded page
116						$page = io_readfile(wikiFN($id));
117
118    				// convert relative links
119						$page = $this->_convertInstructions($page,$id);
120
121						// do replacements (on text-base to preserve List indentation and ordering etc.):
122						$page = $this->_do_replacements($page,$replacement);
123
124						$ins = p_get_instructions($page);
125
126						return $ins;
127		}
128
129		function _do_replacements($page,$r_str) {
130			//Build up list of replacements (this needs to be done manually to allow several replacements with nesting if <label>s
131			$r_list = array();
132			preg_match_all('/<label\s+([a-zA-Z0-9_]+)\s*>/',$r_str, $matches_label, PREG_OFFSET_CAPTURE);
133			preg_match_all('/<\/label>/',$r_str, $matches_labelx, PREG_OFFSET_CAPTURE);
134
135			$level = 0;
136			$element = array_shift($matches_label[1]);
137			$end_element = array_shift($matches_labelx[0]);
138			while ($element || $end_element) {
139			   if ($element && $element[1] < $end_element[1]) { // <label ..> before </label>
140  				 if ($level == 0) {
141  			   	    $section_name = $element[0];
142  			        $section_start = $element[1]+strlen($section_name)+1;
143  				  }
144						$level++;
145			      $element = array_shift($matches_label[1]);
146				 } else {  //</label> before <label ..>
147					   $level--;
148				     if ($level == 0) {
149							   $section_end = $end_element[1];
150								 $r_list[$section_name] = substr($r_str,$section_start,$section_end-$section_start);
151						}
152			       $end_element = array_shift($matches_labelx[0]);
153				 }
154			}
155
156			// now do replacements
157			foreach ($r_list as $section => $rep) {
158							$page = preg_replace('/<label\s+' . $section .'\s*>.*?<\/label>/ms',$rep, $page);
159			}
160			return $page;
161		}
162
163		//Does not support CamelCase Links currently
164		function _convert_link($link,$inclNS) {
165
166				//Check if external:
167				if (preg_match('/^(https?:\/\/|mailto:|\\\\)/',$link))
168				   return $link;
169
170				//check if interwiki or email:
171				if ((! strpos('>',$link) === false) || (! strpos('@',$link) === false))
172				   return $link;
173
174			// convert internal links and media from relative to absolute
175
176      // relative subnamespace
177			if ($link{0} == '.') {
178			    // parent namespace
179          if ($link{1} == '.')
180            $link = getNS($inclNS).':'.substr($link, 2);
181          // current namespace
182          else
183            $link = $inclNS.':'.substr($link, 1);
184			} elseif (strpos($link, ':') === false){
185          $link = $inclNS.':'.$link;
186      }
187
188		  return $link;
189		}
190
191		function _convertInstructions($page,$page_id) {
192      global $ID;
193
194  		if (! $page) return;
195
196  		// check if embeded page is in same namespace
197    		$inclNS = getNS($page_id);
198    		if (getNS($ID) == $inclNS) return $page;
199
200  		// convert links
201  		$page = preg_replace("/\[\[([^\|\]]+)(\|[^\]]+)?\]\]/e",
202             "'[[' . \$this->_convert_link('\\1','$inclNS') . '\\2]]'",
203             $page);
204
205  	  //convert embeddings
206  		$page = preg_replace("/<embed\s+([^>]+)\s*\/?>/e",
207             "'<embed ' . \$this->_convert_link('\\1','$inclNS') . '>'",
208             $page);
209
210  	  //convert images
211  		$page = preg_replace("/{{(\s*)([^ \|}]+)(\s*)(||[^}]+)}}/e",
212             "'{{\\1' . \$this->_convert_link('\\2','$inclNS') . '\\3\\4}}'",
213             $page);
214  		return $page;
215
216		}
217}
218//Setup VIM: ex: et ts=4 enc=utf-8 :
219