source: documentation/trunk/wiki/plugins/greenstonedocs/syntax.php@ 30226

Last change on this file since 30226 was 30226, checked in by jmt12, 9 years ago

Fixing two bugs - one that prevents a blank namespace restriction working and a second that meant updating internal links only worked if there were also bogus image reference tags to fix

File size: 11.2 KB
Line 
1<?php
2/**
3 * A plugin to provide compatibility between the marked up text format used
4 * by the Greenstone Documentation Generation scripts and Dokuwiki
5 *
6 * Features:
7 * - hide the <!-- id:XXX --> markers used during translation process
8 *
9 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
10 */
11
12if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
13if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
14require_once(DOKU_PLUGIN.'syntax.php');
15
16/**
17 * All DokuWiki plugins to extend the parser/rendering mechanism
18 * need to inherit from this class
19 */
20class syntax_plugin_greenstonedocs
21extends DokuWiki_Syntax_Plugin
22{
23
24 /** The plugin should only effect pages within this namespace.
25 */
26 private $target_namespace_prefix;
27
28
29 /** @function __construct()
30 */
31 public function __construct()
32 {
33 global $conf;
34
35 $this->target_namespace_prefix = $this->getConf('currentnamespace');
36
37 // Prerequisite testing. Ensure the following list of plugins are also
38 // installed and complain otherwise.
39 if ($this->getConf('checkprereq'))
40 {
41 $prereqs = array_flip(explode(' ', $this->getConf('prerequisiteplugins')));
42 if ($dh = opendir(DOKU_PLUGIN))
43 {
44 while (($file = readdir($dh)) !== false)
45 {
46 $path = DOKU_PLUGIN . $file;
47 if (substr($file, 0, 1) != '.' && is_dir($path))
48 {
49 if (isset($prereqs[$file]))
50 {
51 unset($prereqs[$file]);
52 }
53 }
54 }
55 closedir($dh);
56 }
57 if (count($prereqs) > 0)
58 {
59 ksort($prereqs);
60 foreach ($prereqs as $plugin_name => $value)
61 {
62 msg("The Greenstone Wiki requires the plugin: " . $plugin_name, -1);
63 }
64 }
65 else
66 {
67 $this->_resetConf('checkprereq');
68 }
69 }
70 // Check the plugin configuration and, if checked, proceed to auto-
71 // matically update the internal links within the Greenstone wiki pages
72 // to have a different base namespace
73 if ($this->getConf('updatenamespace'))
74 {
75 // Determine the starting point for the update
76 $base_dir = DOKU_INC . 'data/pages/';
77 if (!empty($this->getConf('currentnamespace')))
78 {
79 $base_dir .= $this->getConf('currentnamespace') . '/';
80 }
81 // - ensure trailing : for consistency in namespaces
82 $current_namespace = $this->getConf('currentnamespace');
83 if (strlen($current_namespace) > 0 && substr($current_namespace, -1) != ':')
84 {
85 $current_namespace .= ':';
86 }
87 $old_namespace = $this->getConf('oldnamespace');
88 if (strlen($old_namespace) > 0 && substr($old_namespace, -1) != ':')
89 {
90 $old_namespace .= ':';
91 }
92 // - recursive call to do the actual search and replace
93 $raw_child_namespaces = explode(' ', $this->getConf('childnamespaces'));
94 $child_namespaces = array();
95 foreach ($raw_child_namespaces as $raw_child_namespace)
96 {
97 if (strlen($raw_child_namespace) > 0 && substr($raw_child_namespace, -1) != ':')
98 {
99 $raw_child_namespace .= ':';
100 }
101 $child_namespaces[] = $raw_child_namespace;
102 }
103 // Begin search
104 $this->_replaceInternalLinkNamespace($base_dir, $old_namespace, $current_namespace, $child_namespaces);
105 // Finally, we reset the configuration option back to off.
106 $this->_resetConf('updatenamespace');
107 // Let users know what has happened
108 msg("Sucessfully performed one-time update of namespace in Greenstone Documentation.", 1);
109 }
110 }
111 /** __construct() **/
112
113
114 /**
115 */
116 function _resetConf($key)
117 {
118 // Reset the configuration option back to off.
119 // - file on disk
120 $config_file_path = DOKU_CONF . 'local.php';
121 $content = file_get_contents($config_file_path);
122 $option_prefix = '$conf[\'plugin\'][\'greenstonedocs\'][\'' . $key . '\']';
123 if (strpos($content, $option_prefix) !== false)
124 {
125 $content = str_replace($option_prefix . ' = 1;',
126 $option_prefix . ' = 0;',
127 $content);
128 }
129 else
130 {
131 $content .= "\n" . $option_prefix . " = 0;";
132 }
133 file_put_contents($config_file_path, $content);
134 // - in memory cache
135 $conf[$key] = 0;
136 }
137 /** _resetConf($key) **/
138
139
140 /** @function _replaceInternalLinkNamespace()
141 */
142 function _replaceInternalLinkNamespace($dir, $old_namespace, $new_namespace, $child_namespaces)
143 {
144 $files = array();
145 if ($dh = opendir($dir))
146 {
147 while (($file = readdir($dh)) !== false)
148 {
149 if (substr($file, 0, 1) != '.')
150 {
151 $files[] = $file;
152 }
153 }
154 closedir($dh);
155 }
156 foreach ($files as $file)
157 {
158 $path = $dir . $file;
159 // - continue the recursive search through child directories
160 if (is_dir($path))
161 {
162 $this->_replaceInternalLinkNamespace($path . '/', $old_namespace, $new_namespace, $child_namespaces);
163 }
164 else if (substr($file, -4) == '.txt')
165 {
166 // - none of these files are *that* large, so read them
167 // into memory for processing
168 $page_content = file_get_contents($path);
169 // - we are looking for internal links that contain the
170 // old namespace (or '') followed by an expected child
171 // namespace - and then replace the old namespace
172 $pattern = '/(\[\[)' . $old_namespace . '(' . implode('|', $child_namespaces) . ')(.+?\]\])/';
173 $replace = '${1}'. $new_namespace . '${2}${3}';
174 $replace_count = 0;
175 $page_content = preg_replace($pattern, $replace, $page_content, -1, $replace_count);
176 // - special case: also fix up the mismatched imgcaption tags
177 $pattern = '/(<imgcaption .+?>)(<\/imgcaption>)\s*(\{\{.+?\}\})/s';
178 $replace = '${1}${3}${2}';
179 $image_replace_count = 0;
180 $page_content = preg_replace($pattern, $replace, $page_content, -1, $image_replace_count);
181 $replace_count = $replace_count + $image_replace_count;
182 if ($replace_count > 0)
183 {
184 file_put_contents($path, $page_content);
185 }
186 }
187 }
188 }
189 /** @function _replaceInternalLinkNamespace() **/
190
191
192 /**
193 * Should return the type of syntax this plugin defines.
194 * - notice that the typo (substition == substitution) is necessary
195 */
196 function getType()
197 {
198 return 'substition';
199 }
200
201 /**
202 * Returns a number used to determine in which order modes are added.
203 */
204 function getSort()
205 {
206 return 0;
207 }
208
209 /**
210 * Connect lookup pattern to lexer.
211 */
212 function connectTo($mode)
213 {
214 $current_full_namespace = getNS(getID('id',true));
215 // Note that the result is FALSE if no match, and should be 0 if name-
216 // space starts with prefix.
217 if (empty($this->target_namespace_prefix) || strpos($current_full_namespace, $this->target_namespace_prefix) === 0)
218 {
219 // Hiding the section/translation ID tags in headers
220 $this->Lexer->addSpecialPattern( '^[ \t]*={2,6}\s?<!-- s?id:.+? -->[^\n]+={2,6}[ \t]*(?=\n)', $mode, 'plugin_greenstonedocs');
221 // Hiding the translation ID tags in paragraph text
222 $this->Lexer->addSpecialPattern('<!-- id:[^\s]+ -->', $mode, 'plugin_greenstonedocs');
223 // Hiding the translation ID tags in image captions
224 $this->Lexer->addSpecialPattern('\%!-- id:[^\s]+ --\%', $mode, 'plugin_greenstonedocs');
225 }
226 }
227
228 /**
229 * Handler to prepare matched data for the rendering process.
230 *
231 * <p>
232 * The <tt>$aState</tt> parameter gives the type of pattern
233 * which triggered the call to this method:
234 * </p>
235 * <dl>
236 * <dt>DOKU_LEXER_ENTER</dt>
237 * <dd>a pattern set by <tt>addEntryPattern()</tt></dd>
238 * <dt>DOKU_LEXER_MATCHED</dt>
239 * <dd>a pattern set by <tt>addPattern()</tt></dd>
240 * <dt>DOKU_LEXER_EXIT</dt>
241 * <dd> a pattern set by <tt>addExitPattern()</tt></dd>
242 * <dt>DOKU_LEXER_SPECIAL</dt>
243 * <dd>a pattern set by <tt>addSpecialPattern()</tt></dd>
244 * <dt>DOKU_LEXER_UNMATCHED</dt>
245 * <dd>ordinary text encountered within the plugin's syntax mode
246 * which doesn't match any pattern.</dd>
247 * </dl>
248 * @param $aMatch String The text matched by the patterns.
249 * @param $aState Integer The lexer state for the match.
250 * @param $aPos Integer The character position of the matched text.
251 * @param $aHandler Object Reference to the Doku_Handler object.
252 * @return Integer The current lexer state for the match.
253 * @public
254 * @see render()
255 * @static
256 */
257 function handle($match, $state, $pos, &$handler)
258 {
259 if (preg_match('/wiki/i', $match))
260 {
261 return array($state, $match, array('action'=>'debug',
262 'pos'=>$pos));
263 }
264 // Special case for IDs (section and language) in header strings
265 if (preg_match('/={2,6}\s?<!-- (s?id):.+? -->[^\n]+={2,6}/', $match))
266 {
267 //msg("<b>[DEBUG]</b> Title: " . htmlspecialchars($match), 2, '', '', MSG_ADMINS_ONLY);
268 $match = preg_replace('/<!-- sid:.+? -->/', '', $match);
269 $match = preg_replace('/<!-- id:.+? -->/', '', $match);
270 $handler->header($match, $state, $pos);
271 return true;
272 }
273 // any other match gets replaced with empty string
274 return array($state, $match, array('action'=>'erase', 'pos'=>$pos));
275 }
276
277 /**
278 * Handle the actual output creation.
279 *
280 * <p>
281 * The method checks for the given <tt>$aFormat</tt> and returns
282 * <tt>FALSE</tt> when a format isn't supported. <tt>$aRenderer</tt>
283 * contains a reference to the renderer object which is currently
284 * handling the rendering. The contents of <tt>$aData</tt> is the
285 * return value of the <tt>handle()</tt> method.
286 * </p>
287 * @see handle()
288 */
289 function render($mode, &$renderer, $data)
290 {
291 if($mode == 'xhtml')
292 {
293 list($state, $match, $params) = $data;
294 switch ($params['action'])
295 {
296 case 'debug':
297 $renderer->doc .= 'SQUIG-squig-squig';
298 break;
299 // By default we do an erase replace
300 default:
301 $renderer->doc .= '';
302 }
303 return true;
304 }
305 return false;
306 }
307}
308
309//Setup VIM: ex: et ts=4 enc=utf-8 :
310?>
Note: See TracBrowser for help on using the repository browser.